Making more complex drawings using loops¶
The regularity we want to use in the drawings can be more complex, compred to previous problems. Here are some examples:

In all these cases, regularity still exists and can be used when writing programs. We can also observe that the examples in the picture all have something in common, which is that two rules appear alternately. For example, in a brick drawing, the first row of bricks begins with the whole brick, the second with the half brick, the third again with the whole brick, and so on. Similarly, illuminated and tinted windows appear alternately in the building drawing.
Due to the alternation of the two rules in all the drawings, the programs drawing them will also have some similarities. Let’s look at code examples.
Example - zipper¶
To draw such a zipper, we will certainly draw the lines in a loop. The drawing shows that each subsequent line is the same number of pixels lower than the previous one, so there should be no problem with computing the y coordinate. The situation with x coordinates is somewhat more difficult because they change according to a slightly more complex rule.
We can solve this problem by using the if statement in the loop. After drawing one line, we check which of the two possible values x coordinate of the beginning of the line has. If it has the first value - we assign it the second and vice versa. Here’s what it looks like in the program:
import pygame as pg, petljapg
(width, height) = (100, 600)
canvas = petljapg.open_window(width, height, "Zipper")
canvas.fill(pg.Color("blue"))
line_length = 50 # length of one zipper line
margin_left_right = 15 # margin to left and right edge of the window
margin_up_down = 40 # margin to upper and lower edge of the window
d_y = 15 # vertical space between zipper lines
# x coordinates of line starts and ends
x_left = margin_left_right
x_right = width - margin_left_right - line_length
# coordinates of the start of the current line
x_start = x_left
y = margin_up_down
while y < height - margin_up_down:
x_end = x_start + line_length
pg.draw.line(canvas, pg.Color("yellow"), (x_start, y), (x_end, y), 6);
# preparing to draw the next line
y += d_y # y coordinate of the next line
if x_start == x_left: # x_start is the opposite of the previous one
x_start = x_right
else:
x_start = x_left
petljapg.wait_loop()
(PyGame_zipper1_eng)
Another possibility to solve the problem with x coordinates is to draw two lines in one loop, for example:
import pygame as pg, petljapg
(width, height) = (100, 600)
canvas = petljapg.open_window(width, height, "Zipper")
canvas.fill(pg.Color("blue"))
line_length = 50 # length of one zipper line
margin_left_right = 15 # margin to left and right edge of the window
margin_up_down = 40 # margin to upper and lower edge of the window
d_y = 15 # vertical space between zipper lines
# x coordinates of line starts and ends
x_left_start = margin_left_right
x_left_end = x_left_start + line_length
x_right_start = width - margin_left_right - line_length
x_right_end = x_right_start + line_length
y = margin_up_down # y coordinate of the current line
while y < height - margin_up_down:
pg.draw.line(canvas, pg.Color("yellow"), (x_left_start, y), (x_left_end, y), 6);
y += d_y # preparing to draw the next line
pg.draw.line(canvas, pg.Color("yellow"), (x_right_start, y), (x_right_end, y), 6);
y += d_y # preparing to draw the next line
petljapg.wait_loop()
(PyGame_zipper2_eng)
Example - Bricks¶
We have already mentioned that the rows of bricks alternately start with the whole brick and half of the brick. That is why when drawing bricks we can use any of the same two ideas as in the previous example.
Let the length of the brick be denoted by: math:a and its height by h. We get the whole brick at the beginning of the row by drawing a rectangle at a given height, with x coordinate equal to zero. Half of a brick at the beginning of a row can be obtained by drawing an entire brick displaced by a2 to the left, that is, by drawing a rectangle with x coordinate equal to -a // 2
. Thus, only the right half of the brick is visible. It remains to be solved when we draw a displaced brick and when a regular one.
One solution is to store the beginning of the row of bricks in a variable, call it x_start. After each line is drawn, we check that the variable x_start has a value of zero or -a // 2
. As in the previous example, whichever of the two values we have, we will assign the other value to the variable, so that in the next row the drawing of the bricks will start differently.
Complete unfinished statements for setting the x_start variable
import pygame as pg, petljapg
(width, height) = (300, 300)
canvas = petljapg.open_window(width, height, "Bricks")
canvas.fill(pg.Color("red"))
brick_a, brick_h = 80, 40
x_start = 0
for y0 in range(0, height, brick_h): # For each row of bricks
for x0 in range(x_start, width, brick_a): # For each brick in the row
pg.draw.rect(canvas, pg.Color("black"), (x0, y0, brick_a, brick_h), 1)
if x_start == x_start: # fix the line
x_start = -brick_a//2
else:
x_start = x_start # fix the line
petljapg.wait_loop()
(PyGame_loops_bricks1_eng)
The second idea is to draw two bricks in each pass through the double loop: the one which we drew in the first solution, and the brick below and half-left of it. Notice that in this case the loop by y0 has twice the step, because the inner loop draws two rows of bricks.
Complete unfinished statements for drawing rectangles
import pygame as pg, petljapg
width, height = 300, 300
canvas = petljapg.open_window(width, height, "Bricks")
canvas.fill(pg.Color("red"))
brick_width, brick_height = 80, 40
for y0 in range(0, height, 2 * brick_height):
for x0 in range(0, width, brick_width):
# draw the first brick
pg.draw.rect(???) # complete the statement as before
# the second brick is in the following row, displaced by half its width
x1, y1 = x0 - brick_width//2, y0 + brick_height
pg.draw.rect(???) # draw the brick below and half-left of the previous one
petljapg.wait_loop()
(PyGame_loops_bricks2_eng)
Tasks for exercise¶
Task - chessboard
Draw a chessboard across the entire window (the board squares should be 50x50 pixels). The lower left square should be dark.
Most of the program is written, try to finish it.
import pygame as pg, petljapg
width, height = 400, 400
canvas = petljapg.open_window(width, height, "Chessboard")
canvas.fill(pg.Color("gray")) # background - light squares
# square size
num_squares = 8
square_width = width / num_squares
square_height = height / num_squares
# go through all the squares
for i in range(num_squares):
for j in range(num_squares):
# paint black squares only
if (i + j) % 2 != 0:
pass # fix the statement
petljapg.wait_loop()
(PyGame_loops_chessboard_eng)
Task - Building
Modify the program below so that the windows are drawn in a double loop.
The part that needs to be changed, after the change, can start like this:
for y in range(5): # floor
for x in range(2): # side of the building (0 - left, 1 - right)
if (x+y) % 2 == 0:
color = ...
import pygame as pg, petljapg
canvas = petljapg.open_window(300, 300, "Building")
pg.draw.rect(canvas, pg.Color("darkgray"), (120, 50, 60, 140)) # building
# change this part
pg.draw.rect(canvas, pg.Color('yellow'), (130, 60, 15, 15))
pg.draw.rect(canvas, pg.Color('black'), (155, 60, 15, 15))
pg.draw.rect(canvas, pg.Color('black'), (130, 80, 15, 15))
pg.draw.rect(canvas, pg.Color('yellow'), (155, 80, 15, 15))
pg.draw.rect(canvas, pg.Color('yellow'), (130, 100, 15, 15))
pg.draw.rect(canvas, pg.Color('black'), (155, 100, 15, 15))
pg.draw.rect(canvas, pg.Color('black'), (130, 120, 15, 15))
pg.draw.rect(canvas, pg.Color('yellow'), (155, 120, 15, 15))
pg.draw.rect(canvas, pg.Color('yellow'), (130, 140, 15, 15))
pg.draw.rect(canvas, pg.Color('black'), (155, 140, 15, 15))
pg.draw.rect(canvas, pg.Color("black"), (140, 160, 20, 30)) # door
petljapg.wait_loop()
(PyGame_loops_building_alternating_eng)
If you haven’t had any major problems with all these tasks, try to solve one more difficult task as well.
Task - challenge: parquet
Write a program that shows the parquet (you can see the parquet picture when you click the “Play task” button, and the picture is the same as at the beginning of this page, right). The goal, of course, is to draw the floorboards in a multiple loop. The board dimensions are 10x60 and the colors are goldenrod and brown.
The skeleton of the program roughly looks like this:
for row ...
for column ...
if ...
for floorboard in range(6):
pg.draw.rect(...)
else:
for floorboard in range(6):
pg.draw.rect(...)
import pygame as pg, petljapg
(width, height) = (300, 300)
canvas = petljapg.open_window(width, height, "Parquet")
petljapg.wait_loop()
(PyGame_loops_parquet_eng)