Subsections of Extra challenges
Snake
Note
Requires: session 5 finished.
The snake moves by itself. You steer it with the arrow keys. Eat a piece of fruit: the snake grows one tile. Hit the wall or yourself: game over.
Getting started
Download the starter code and unpack the ZIP. Open main.py in Thonny and click Run. You see an empty grid. The controls and the timing are already there. You fill in the functions nieuw_fruit(), zet_stap() and teken().
How does it work?
Snake is played on a grid. The snake is a list of (column, row) coordinates. Every step you add a new head to the front of the list and remove the last element. When the snake eats fruit, you leave the last element in place, so the snake grows.
TILE = 20 # size of one tile in pixels
COLS = WIDTH // TILE
ROWS = HEIGHT // TILE
slang = [(10, 10), (9, 10), (8, 10)] # list of (column, row)
dx, dy = 1, 0 # direction: 1 step to the right
Loading and scaling images
The fruit pictures are 64×64 pixels. That is too big for one tile of 20×20. You load them with pygame.image.load() and then scale them with pygame.transform.scale(). This is already in the starter code.
surf = pygame.image.load('images/apple.png').convert_alpha()
fruit_images['apple'] = pygame.transform.scale(surf, (TILE, TILE))
convert_alpha() makes sure the transparency of the picture works properly. pygame.transform.scale() returns a new Surface at the size you ask for. The original stays unchanged.
To put a scaled picture on the screen, you use screen.blit():
screen.blit(fruit_surf, (fruit_pos[0] * TILE, fruit_pos[1] * TILE))
✅ Basic
- Fill in
nieuw_fruit(): pick a random fruit, find a free tile and store the position and the Surface. - Fill in
zet_stap(): work out the new head, add it to the front of the list, remove the last element. - Fill in
teken(): draw the grid (pygame.draw.line), the fruit (screen.blit), and every snake segment (pygame.draw.circle). Show the score with font.render() and screen.blit(). - Spawn fruit when the snake eats it: leave out
slang.pop() and call nieuw_fruit().
⭐ Stretch
- Game over when the head leaves the grid.
- Game over when the head hits one of its own segments (
nieuw_hoofd in slang[1:]). - Show a game-over screen with the score. Press R to restart.
🔥 Expert
- Raise the speed as the score goes up: lower
STAP_MS based on the score (max(60, BASISINTERVAL - score * 10)). - Store the high score and show it next to the current score, also after a restart.
- Make sure fruit never spawns on a tile the snake already fills.
Downloads
- Starter code:
main.py with the controls and timing ready; you fill in the functions. - Fruit images: if you start without the starter code (already included in the starter ZIP).
Fruit: Kenney Food Kit, CC0 (public domain). kenney.nl
Space Invaders
Note
Requires: session 7 finished.
Aliens are lined up in a 4×9 formation. They move back and forth, drop down at every edge and shoot back at random. You have one ship and three lives. Shoot them all down before they land.
Getting started
Download the starter code and unpack the ZIP. Open main.py in Thonny and click Run. You see the ship and the alien formation. The controls and the images are already there. You fill in the movement, the shooting and the collisions.
How does it work?
All the aliens are pygame.Rect objects in one list. Every frame you move them all by the same number of pixels. When the formation touches an edge, you reverse the direction and drop every alien by ALIEN_VAL pixels.
for alien in aliens:
alien.x += snelheid * alien_richting
rechts = max(a.right for a in aliens)
links = min(a.left for a in aliens)
if rechts >= BREEDTE or links <= 0:
alien_richting *= -1
for alien in aliens:
alien.y += ALIEN_VAL
Bullets are pygame.Rect objects too. Player bullets go up (k.y -= KOGEL_SNL), alien bullets go down (k.y += ALIEN_SNL). You check collisions with k.colliderect(alien).
✅ Basic
- Draw the background (use
random.seed(42) for fixed stars), the ship and all the aliens. - Move the ship left/right with the arrow keys.
- Let the formation move back and forth and drop down at an edge.
- Fire a bullet upwards when the player presses space. Use
COOLDOWN_MS to stop insanely fast shooting.
⭐ Stretch
- Player bullet hits an alien: remove both, score += 10.
- Every alien shoots back at random:
if random.random() < SCHIET_KANS. - Alien bullet hits the player: levens -= 1; game over at 0 lives.
- Show the score and the lives in the top left.
🔥 Expert
- The formation speeds up as fewer aliens are left:
1 + 3 * (1 - restant). From 1 pixel/frame at the start to 4 pixels/frame with the last alien. - Game over when an alien reaches the bottom (
alien.bottom >= HOOGTE - 50). - Game over (won) when the list is empty.
- Restart with R: set all the variables again through a
reset() function.
Downloads
- Starter code:
main.py with the window, images and alien grid ready; you fill in the logic. - Images: ship.png, enemy.png and laser.png if you start without the starter code.
Images: Kenney Space Shooter Redux, CC0 (public domain). kenney.nl
Pac-Man
Note
Requires: session 9 finished.
A maze of blue walls. White dots everywhere. Four coloured ghosts coming at you. Eat all the dots to win. Touch a ghost and it’s game over.
Getting started
Download the starter code and unpack the ZIP. Open main.py in Thonny and click Run. The maze appears and Pac-Man is ready to go. You add the movement, the collisions and the ghosts.
How does it work?
Pac-Man moves in pixels, not in tiles. Every frame he moves snelheid × dt pixels in his direction. dt is the time (in seconds) the previous frame took. That way the game moves just as fast on slow and fast computers.
nieuw_x = self.rect.x + self.dx * PACMAN_SNL * dt
You check a wall collision by making a test rect at the new position and seeing whether it overlaps with a wall block:
test = self.rect.copy()
test.x = nieuw_x
test.y = nieuw_y
botsing = any(test.colliderect(m.rect) for m in muren)
✅ Basic
- Let Pac-Man move in his current direction (the arrow keys are already there).
- Add wall collision: stop when the new position touches a wall.
- Make the direction change work: only switch to the new direction when it’s free.
⭐ Stretch
- Eat white dots: when Pac-Man touches a dot, remove it and count 10 points.
- Eat yellow power dots: remove, count 50 points and set
self.powered_up = True. - Win when all the dots are gone: set
self.staat = GEWONNEN.
🔥 Expert
- Let the ghosts move: try directions and pick a free one (no wall collision).
- Let the ghosts hunt Pac-Man: pick the direction that makes the distance to Pac-Man smaller.
- If a ghost touches Pac-Man and
powered_up is False: game over. - If a ghost touches Pac-Man and
powered_up is True: eat the ghost (+200 points).
Downloads
- Starter code:
main.py + sound files; you fill in the movement and collisions.
Frogger
Note
Requires: session 5 (Breakout) finished.
A frog wants to reach the other side. For that he has to cross a busy road and a dangerous river. Cars drive in three lanes, logs float on the water. One wrong step and it’s over.
Getting started
Download the starter code and unpack the ZIP. Open main.py in Thonny and click Run. You see the playing field, but the frog doesn’t move yet. You fill in the functions.
How does it work?
The screen is split into horizontal lanes of 40 pixels:
- Top: 5 ponds where the frog has to arrive safely
- River (3 lanes): logs float past. On a log you’re safe, in the water you drown.
- Grass verge: safe resting spot in the middle
- Road (3 lanes): cars drive back and forth. Getting hit = dead.
- Bottom: starting position of the frog
TEGEL = 40
KOLOMMEN, RIJEN = 13, 14
# Lanes: (row, direction, speed)
AUTO_BANEN = [
(6, 1, 2), # right, slow
(7, -1, 4), # left, fast
(8, 1, 3), # right, medium
]
Cars and logs are pygame.Rect objects in a list. Every frame you shift them all and remove the ones that fall off the screen.
The frog moves in fixed steps of TEGEL pixels. Press an arrow key and the frog jumps one tile in that direction.
You check collisions with rect.colliderect(andere_rect).
✅ Basic
- Move the frog: catch the arrow keys in
KEYDOWN and adjust kikker.x and kikker.y by TEGEL. - Spawn cars: fill in
spawn_auto(). Pick a random lane, make a Rect just outside the screen, add it to auto_lijst. - Move cars: in
update(): shift every car with rect.x += richting * snelheid. Remove cars outside the screen. Keep at least 6 cars. - Spawn logs: fill in
spawn_hout(). The same principle, but with HOUT_BANEN and a bigger width. - Move logs: in
update(): shift and remove like with the cars. Keep at least 4 logs. - Car collision: if
kikker.colliderect(auto): call sterf(). - Drowning: if the frog is in the river (row 2, 3 or 4) and not on a log:
sterf(). - Pond check: if the frog is close to an empty pond: set
huizen_gevuld[i] = True, score += 10, reset the frog. 5 ponds full = won.
Done? You have a working Frogger!
⭐ Stretch
- Lives counter: the
levens system is already there. Make sure the game stops when levens <= 0 and show “GAME OVER”. - Timer: add a timer. Start with 30 seconds per level. When the timer runs out you lose a life. Show the timer in the HUD. Every pond you fill gives you 5 extra seconds.
- Difficulty: raise the speed of the cars and logs as the score goes up (
snelheid = basis + score // 50).
🔥 Expert
- Bonus fly: a green insect appears at a random spot on the map and moves around. Catch it for 20 bonus points. Let it disappear after 5 seconds.
- High score: store the highest score in a variable
high_score. Show it next to the current score. Update it when the player sets a new record. - Different vehicles: add trucks (2 tiles wide, slower) and motorbikes (1 tile, faster). Use a separate list or a
type field. - Animation: let the frog blink briefly when he dies (
dood_timer is already there). Draw a small splash when the frog drowns.
Downloads
- Starter code:
main.py with the playing field, the frog and all the drawing code ready; you fill in the logic.
Inspired by the classic Frogger (1981, Konami). Drawings with pygame.draw.