Pac-Man: Coach notes

Goal of the challenge

A ninja has finished the challenge when:

  • βœ… Basic: Pac-Man moves with the arrow keys, stops at walls and changes direction correctly.
  • ⭐ Stretch: dots are removed on touch, the score counts up, power dots activate powered_up, the game ends when all the dots are gone.
  • πŸ”₯ Expert: ghosts move through the maze and hunt Pac-Man; a collision leads to game over (or to eating the ghost when powered).

Preparation

  • Give the ninja the starter code. It contains main.py and the sounds/ folder.
  • Click Run: the maze and all the sprites appear straight away. Pac-Man stands still and walks through walls (that is the Basic assignment).
  • Check that pygame is available on the laptop (see Installing Thonny).

New concept: pixel movement with dt

This is the first time ninjas work with frame-rate independent movement. Explain it when they ask why dt:

nieuw_x = self.rect.x + self.dx * PACMAN_SNL * dt

dt is the time (seconds) the previous frame took. On a fast computer dt is small (for example 0.016 s at 60 FPS), on a slow computer dt is bigger. By multiplying by dt the game always moves at the same speed.


Common questions

“Pac-Man keeps walking through walls after Basic” The test rect has to be made before the move of self.rect. Check that the ninja creates test = self.rect.copy() and then sets test.x = nieuw_x, and not self.rect.x = nieuw_x followed by a test.

“Pac-Man can’t change direction” The direction change sits in a separate if self.volgende_dx != 0 or self.volgende_dy != 0 block. That block has to come before the movement. Check the order.

“The score doesn’t count up” pygame.sprite.spritecollide(self.pacman, self.pellets, True) removes the pellet and gives it back. If the ninja doesn’t add p.punten, the score is always 0. Check that the loop runs over the returned list.

“The ghosts move but don’t collide with walls” The test rect has to be checked against muren, not against alles. Check that the ninja uses the right group.

“A ghost touches Pac-Man but nothing happens” The ninja probably uses pygame.sprite.spritecollide without the more precise circle check. _cirkel_botsing is already there in the Spel class. Point it out to the ninja.


Hints per level

βœ… Basic

  • Wall collision: test = self.rect.copy(); test.x = nieuw_x; test.y = nieuw_y; botsing = any(test.colliderect(m.rect) for m in muren).
  • If not botsing: move self.rect.x and self.rect.y. If botsing: set self.dx = 0; self.dy = 0.
  • Direction change: the same logic, but test with self.volgende_dx and self.volgende_dy. If free: self.dx = self.volgende_dx; self.dy = self.volgende_dy; self.volgende_dx = 0; self.volgende_dy = 0.

⭐ Stretch

  • Pellets: geraakt = pygame.sprite.spritecollide(self.pacman, self.pellets, True) β†’ loop β†’ self.score += p.punten; self.alles.remove(p).
  • Power pellets: the same, plus self.powered_up = True; self.power_timer = POWER_DUUR.
  • Win: if len(self.pellets) == 0 and len(self.power_pellets) == 0: self.staat = GEWONNEN; self._stop_muziek().

πŸ”₯ Expert

  • Random movement: for richting in richtingen: rdx = richting[0]; rdy = richting[1]; test 4 px ahead; if free: pick that direction. Use random.shuffle(richtingen) so the choice is random.
  • Hunting: for every free direction work out afstand = ((toekomst_x - pacman.rect.centerx)**2 + (toekomst_y - pacman.rect.centery)**2)**0.5. Pick the direction with the smallest distance (or the biggest when vulnerable).
  • Collision: geest_hits = pygame.sprite.spritecollide(self.pacman, self.geesten, False); filter with self._cirkel_botsing; if powered_up: score += 200, remove ghost; otherwise: self.staat = GAME_OVER.