-
Notifications
You must be signed in to change notification settings - Fork 0
pacman
Second test game. Adds multi-entity dynamics, conditional rules, and spatial reasoning over Snake.
- Multiple entity types: Player, ghosts, pellets, power pellets, fruit — richer event vocabulary
- Conditional rules: The core mechanic. Power pellet flips the ghost interaction from death to kill. This is the simplest non-trivial conditional rule and the one from the premise.
- Enemy AI: Ghosts have distinct behaviors (chase, ambush, patrol, random in classic Pac-Man). The model must learn to predict enemy movement, not just player movement.
- Spatial structure: Maze topology constrains movement. Corridors, intersections, dead ends — the model must learn graph structure, not just grid boundaries.
- Phase transitions: Normal → powered → normal. Timed buff with visible countdown. Tests temporal dependency learning.
- Well-studied: Decades of AI research on Pac-Man. Known optimal strategies, known player behavior patterns. Easy to validate results against existing literature.
| Aspect | Snake | Pac-Man |
|---|---|---|
| Entities | 1 player + food | 1 player + 4 ghosts + many pellets + power pellets |
| Conditional rules | Self-collision (length) | Ghost interaction (buff state) |
| Enemy behavior | None | 4 distinct AI patterns |
| Map structure | Open grid + walls | Fixed maze with corridors |
| State complexity | Low | Medium |
| Play styles | Movement patterns | Risk/reward, routing, ghost manipulation |
- Classic Pac-Man maze or simplified variants
- For initial experiments: a smaller maze (15x15) with the essential topology — corridors, intersections, a few dead ends, ghost house in center
- Walls are fixed (no destructible terrain)
- Player: Single position, moves one cell per tick in current direction. Continues moving in last direction if no input (unlike Snake where you must always input).
-
Ghost 0-3: Each has position, current direction, and behavioral mode.
- Chase: Targets player position (or predicted position)
- Scatter: Targets fixed corner
- Frightened: Moves randomly (when player is powered)
- Eaten: Returns to ghost house (after being consumed by powered player)
- Pellet: Static positions. Removed on collection. ~100+ per level.
- Power Pellet: 4 per level, static positions. Triggers powered phase on collection.
- Fruit: Spawns periodically at fixed location. Bonus points.
{UP, DOWN, LEFT, RIGHT}
No "stop" — player keeps moving in last direction. Direction change only takes effect at intersections (or immediately if reversing).
player enters pellet cell → COLLECT, score +10, pellet removed
player enters power_pellet cell → COLLECT, score +50, power pellet removed, POWERED phase starts
player enters ghost cell:
if normal → DEATH, life lost
if powered → GHOST_EATEN, score +200/400/800/1600 (combo), ghost returns to house
if ghost is eaten (eyes) → no interaction
player enters fruit cell → COLLECT, bonus score
all pellets collected → LEVEL_COMPLETE
powered phase:
duration: N ticks (decreasing per level in classic)
ghosts enter FRIGHTENED mode
on expiry: ghosts return to normal mode
ghost reaches ghost house (eaten) → GHOST_RESPAWN, resumes normal behavior
player_pos: (x, y)
player_dir: {U, D, L, R}
player_alive: bool
powered: bool
power_timer: int # ticks remaining
ghost[0-3]_pos: (x, y)
ghost[0-3]_mode: {chase, scatter, frightened, eaten}
pellets: set[(x, y)] # remaining pellet positions
power_pellets: set[(x, y)]
fruit_active: bool
score: int
lives: int
level: int
# Player actions
INPUT_U, INPUT_D, INPUT_L, INPUT_R
# Movement
MOVE_PLAYER (x, y)
MOVE_GHOST0 (x, y)
MOVE_GHOST1 (x, y)
MOVE_GHOST2 (x, y)
MOVE_GHOST3 (x, y)
# Collections
COLLECT_PELLET @(x, y)
COLLECT_POWER @(x, y)
COLLECT_FRUIT
# Phase changes
POWERED_START
POWERED_END
GHOST_FRIGHTENED [ghost_id]
GHOST_NORMAL [ghost_id]
# Collisions
DEATH @(x, y) [ghost_id] # player killed by ghost
GHOST_EATEN [ghost_id] @(x, y) # player eats frightened ghost
GHOST_RESPAWN [ghost_id] # ghost returns from house
# Game flow
LEVEL_COMPLETE
GAME_OVER
LIFE_LOST
TICK = 0 MOVE_PLAYER, MOVE_GHOST*
COLLECTION = 2 COLLECT_PELLET (frequent but meaningful)
PHASE = 3 POWERED_START, POWERED_END, GHOST mode changes
CRITICAL = 4 DEATH, GHOST_EATEN, LEVEL_COMPLETE, GAME_OVER
COLLECT_PELLET is interesting — it's frequent (100+ per level) but individually low-information. At high salience threshold, you might drop individual pellet collections and only track pellet count in snapshots.
PLAYER (x, y) DIR POWERED [timer] SCORE LIVES LEVEL
GHOST0 (x, y) MODE
GHOST1 (x, y) MODE
GHOST2 (x, y) MODE
GHOST3 (x, y) MODE
PELLETS_REMAINING n
POWER_PELLETS_REMAINING n
Full pellet positions are too verbose for snapshots (100+ positions). Remaining count + power pellet positions is sufficient — the model can learn pellet density from collection events.
Systematically clears all pellets in a region before moving on. Avoids ghosts. Ignores power pellets until forced. Low risk, consistent scoring.
Beelines for power pellets, then immediately chases ghosts for combo points. High risk/reward. Traces show: long stretches of avoidance → power pellet → aggressive ghost pursuit → retreat.
Stays as far from ghosts as possible. Collects pellets only in safe areas. Never uses power pellets offensively. Traces show: high correlation between ghost proximity and direction changes.
Manipulates ghost AI by positioning to bait them into advantageous configurations. Advanced strategy — predicts ghost movement. Traces show: deliberate movement toward then away from ghosts, herding patterns.
Prioritizes fruit spawns for bonus points. Positions near fruit spawn location. Niche but distinct.
100+ pellets means either:
- Verbose snapshots (list all remaining positions)
- No pellet state in snapshots, rely on COLLECT events to track
- Only track count in snapshots, not positions
The hybrid approach works well here: pellet count in snapshots, individual COLLECT events for positions.
Each ghost has deterministic behavior in classic Pac-Man. The model should learn to predict ghost movement from mode + position. This is a non-trivial test — can the model learn 4 different movement algorithms from observing their outputs?
Ghost eat combo (200 → 400 → 800 → 1600) requires counting consecutive ghost eats within a single powered phase. Tests the model's ability to track a counter that resets on phase change.
The maze is fixed, but the model must learn which movements are legal from which positions. This is implicit in the MOVE events — if the player never moves up from position (5,3), there's probably a wall there. Can the model learn the maze from movement traces alone?
Random legal move each tick. Dies constantly. Good for coverage.
Moves toward nearest pellet (BFS shortest path). Ignores ghosts. Dies when ghosts happen to intersect path.
BFS toward nearest pellet, but won't enter any cell within Manhattan distance N of a ghost. Conservative.
Seeks power pellets, then switches to ghost-hunting during powered phase. Reverts to avoidance when not powered.
Randomly switches between avoidance and power chasing. Produces mixed-style traces.
- Maze complexity: Full classic Pac-Man maze or simplified? Simpler maze = smaller position vocab, faster learning, but less interesting spatial structure.
- Ghost AI fidelity: Implement real Pac-Man ghost AI (Blinky/Pinky/Inky/Clyde) or simplified? Real AI is deterministic and well-documented, but adds implementation complexity.
- Pellet density in tokens: Is per-pellet tracking worth the token cost, or should pellets be aggregated?
- Multi-life episodes: Classic Pac-Man has 3 lives. Is a life loss a sub-episode boundary? Or continuous?