32. Drawing on screen — Homework solutions

Each solution is a complete, runnable pygame program. Run with:

python exercises/32/homework/solutions/01-flag.py

Problem 1 — Flag

Problem. Draw a three-stripe flag filling most of the window.

How to think about it. Divide the window height by 3. Each stripe is a rectangle spanning the full width at a different y position.

Worked solution.

import pygame

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Flag")
clock = pygame.time.Clock()

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill((255, 255, 255))

    stripe_height = 600 // 3
    pygame.draw.rect(screen, (0, 57, 166),  (0, 0,              800, stripe_height))
    pygame.draw.rect(screen, (255, 255, 255),(0, stripe_height,  800, stripe_height))
    pygame.draw.rect(screen, (255, 0, 0),   (0, stripe_height * 2, 800, stripe_height))

    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Any three colours are acceptable. The key is computing the y offset of each stripe as stripe_height * stripe_index.

Common mistakes.

  • Hard-coding pixel positions like 0, 200, 400 instead of computing from 600 // 3. If the window size changes, hard-coded positions break; computed positions adapt.

Problem 2 — Target

Problem. Three concentric circles in alternating colours.

How to think about it. Draw the largest circle first, then the medium one on top, then the smallest. They all share the same centre. Using different radii with the same centre produces concentric rings.

Worked solution.

import pygame

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Target")
clock = pygame.time.Clock()

cx, cy = 400, 300   # centre

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill((200, 200, 200))

    pygame.draw.circle(screen, (220, 0, 0),     (cx, cy), 150)
    pygame.draw.circle(screen, (255, 255, 255), (cx, cy), 100)
    pygame.draw.circle(screen, (220, 0, 0),     (cx, cy), 50)

    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Common mistakes.

  • Drawing smallest first. The largest circle will paint over the smaller ones, leaving only a single filled circle. Always draw back-to-front.

Problem 3 — Grid

Problem. A 4x4 grid of outlined squares using nested loops.

How to think about it. Divide the window into 4 equal columns and 4 equal rows. Each cell's top-left corner is at (col * cell_w, row * cell_h).

Worked solution.

import pygame

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Grid")
clock = pygame.time.Clock()

cols = 4
rows = 4
cell_w = 800 // cols
cell_h = 600 // rows

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill((30, 30, 30))

    for row in range(rows):
        for col in range(cols):
            x = col * cell_w
            y = row * cell_h
            pygame.draw.rect(screen, (180, 180, 180), (x, y, cell_w, cell_h), 2)

    pygame.display.flip()
    clock.tick(60)

pygame.quit()

The thickness argument 2 draws only the border. Setting it to 0 would fill each cell.

Common mistakes.

  • Using range(1, 5) instead of range(4). Both produce four values but range(4) starts at 0, which correctly maps to pixel positions via multiplication.

Challenge — Chessboard

Problem. An 8x8 chessboard where (row + col) % 2 == 0 is white and the rest is dark.

How to think about it. Same structure as the grid, but the fill colour depends on (row + col) % 2. Use filled rectangles (thickness 0).

Worked solution.

import pygame

pygame.init()
SIZE = 600
screen = pygame.display.set_mode((SIZE, SIZE))
pygame.display.set_caption("Chessboard")
clock = pygame.time.Clock()

LIGHT = (240, 217, 181)
DARK  = (181, 136, 99)
CELLS = 8
cell = SIZE // CELLS

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill((0, 0, 0))

    for row in range(CELLS):
        for col in range(CELLS):
            color = LIGHT if (row + col) % 2 == 0 else DARK
            x = col * cell
            y = row * cell
            pygame.draw.rect(screen, color, (x, y, cell, cell))

    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Using a square window (600x600) and dividing by 8 gives equal cells with no rounding error (600 / 8 = 75 exactly).

Common mistakes.

  • Using a non-square window (e.g., 800x600). 800 / 8 = 100 but 600 / 8 = 75, so cells would be rectangular rather than square. Either use a square window or use separate cell_w and cell_h.

Done?

Chapter 33 adds keyboard and mouse input so the program can react to what the player does.