Snake: Coach notes

Goal of the challenge

A ninja has finished the challenge when:

  • βœ… Basic: the snake moves with the arrow keys, eats fruit, grows and shows the score.
  • ⭐ Stretch: game over on a wall collision or a collision with itself; a game-over screen with the score; restart with R.
  • πŸ”₯ Expert: speed goes up as the score rises; the high score survives a restart; fruit never spawns on the snake.

Preparation

  • Give the ninja the starter code. It contains main.py and the images/ folder with the fruit pictures.
  • The controls, the window and the timing are already there. The ninja fills in nieuw_fruit(), zet_stap() and teken().
  • Check that pygame is available on the laptop (see Installing Thonny).

New concept: loading and scaling images

This is the first time ninjas load an image and scale it too. The starter code already does it, but explain it when a ninja asks about it:

surf = pygame.image.load('images/apple.png').convert_alpha()
fruit_images['apple'] = pygame.transform.scale(surf, (TILE, TILE))
  • pygame.image.load() reads the PNG file in as a Surface.
  • .convert_alpha() makes sure the transparency works correctly.
  • pygame.transform.scale() returns a new Surface at the size you ask for. The original does not change.
  • screen.blit(surf, (x, y)) draws the Surface at position (x, y).

Common questions

“The snake moves too fast” clock.tick(60) runs 60 times per second. One step per frame is far too fast. The starter code uses pygame.time.get_ticks() to measure whether enough time has passed. Check that the ninja only calls zet_stap() when nu - laatste_stap >= STAP_MS.

“The snake can move backwards” The direction may never be reversed straight away. The starter code already checks this in the event handler (if event.key == pygame.K_UP and dy == 0). If the ninja wrote the controls himself, point at that check.

“The snake doesn’t grow when it eats fruit” Every step you remove the last element (slang.pop()). When it eats fruit you skip that pop(). Check that the ninja skips the pop() on a collision with fruit.

“Fruit appears on the snake” The ninja spawns fruit without checking whether the position is already taken. Hint: use a while loop that repeats as long as pos in slang.

“A collision with itself isn’t detected” nieuw_hoofd in slang also matches the current head (the first element before the move). Use nieuw_hoofd in slang[1:].

“The score text doesn’t show up” font.render() returns a Surface. You still have to put it on the screen with screen.blit(). Many ninjas forget the second step.


Hints per level

Give these hints when a ninja gets stuck. No full code.

βœ… Basic

  • Store the direction as two numbers (dx, dy), for example (1, 0) for right.
  • Every step: work out nieuw_hoofd = (slang[0][0] + dx, slang[0][1] + dy), add it to the front, remove the last one.
  • Growing: skip slang.pop() when the new head equals fruit_pos. Then call nieuw_fruit().
  • Showing text: tekst = font.render(f"Score: {score}", True, ZWART) + screen.blit(tekst, (6, 4)).

⭐ Stretch

  • After working out the new head, check: outside the grid? In slang[1:]? Then game_over = True and return.
  • Restart: catch pygame.K_r in the event handler when game_over is True, then call reset().

πŸ”₯ Expert

  • Speed: STAP_MS = max(60, BASISINTERVAL - score * 10). At a score of 9 the interval is only 60 ms. Recalculate this every time before you check.
  • High score: keep it outside reset(). Only update when score > hoogste_score.
  • Fruit not on the snake: while True: pos = ...; if pos not in slang: break.