-
Notifications
You must be signed in to change notification settings - Fork 0
snake
First test game. Chosen for simplicity + the properties we need.
- Discrete grid: No continuous state to discretize
- Clear events: Move, eat, grow, die — small, unambiguous event vocabulary
- Conditional rules: Self-collision only matters when length > 1. Wall-collision is always death. Simple conditional logic for the model to learn.
- Emergent play styles: Wall-huggers, center-players, food-chasers, cautious coilers — distinct strategies exist even in a simple game
- Single entity focus: One player entity that changes over time (grows). Simpler than multi-entity games but not trivial.
- Variable episode length: Good games last longer. Episode length itself correlates with skill/strategy.
- Size: 10x10 (configurable)
- Walls on all borders
- Interior is open
- Snake: Head position + body segment positions. Moves one cell per tick in current direction.
- Food: Single position. Respawns at random empty cell when collected.
{UP, DOWN, LEFT, RIGHT}
No "do nothing" — snake always moves. Cannot reverse direction (instant 180 = death in classic rules, or just disallowed).
head enters wall cell → DEATH
head enters own body cell → DEATH (only possible when length > 1)
head enters food cell → GROW (body extends by 1), SCORE +1, food respawns
otherwise → MOVE (tail retracts, head advances)
snake_head: (x, y)
snake_body: [(x, y), ...] # ordered from head to tail
snake_dir: {U, D, L, R}
food_pos: (x, y)
score: int
alive: bool
INPUT_U, INPUT_D, INPUT_L, INPUT_R # player action
MOVE # snake moved without collision
EAT # head entered food cell
GROW # body extended (always follows EAT)
FOOD_SPAWN # new food appeared
DIE_WALL # head hit wall
DIE_SELF # head hit own body
MOVE player (x, y) # new head position
EAT food@(x, y) # food position collected
FOOD_SPAWN (x, y) # new food position
DIE_WALL @(x, y) # where the collision happened
DIE_SELF @(x, y) # where self-collision happened
HEAD (x, y)
BODY [(x1,y1), (x2,y2), ...] # or just length if body tracking is too verbose
DIR {U|D|L|R}
FOOD (x, y)
SCORE n
LEN n
Body is tricky — it can be long. Options:
- Full body positions (verbose, complete)
- Head + direction + length (compact, body is deterministic from move history)
- Head + tail + length (compromise)
Start with head + length + direction. Full body can be reconstructed from move history if needed.
Even with simple agents:
- Wall hugger: Follows walls, avoids center. Safe but slow food collection.
- Direct chaser: Beelines toward food. Fast scoring, high death risk at longer lengths.
- Coiler: Stays in a compact area, moves in tight patterns. Very safe but slow.
- Space filler: Systematically covers the grid. Efficient but rigid.
These should emerge as distinct patterns in the event sequences without any labels.
Pick a random legal action each tick. Won't produce strategies but will explore the event space (and die a lot).
Always move toward food (Manhattan distance). Simple heuristic, produces "direct chaser" traces.
Prefer moves that keep a wall adjacent. Produces "wall hugger" traces.
Follow a fixed Hamiltonian cycle through the grid. Never dies (if the cycle exists), produces "space filler" traces. Boring but perfect — useful baseline.
Randomly switch between strategies every N ticks. Produces stance-shifting data.
- Grid size: 10x10 is standard but might produce too-long body sequences. 8x8? 6x6 for initial experiments?
- Food count: Single food is classic. Multiple food items add complexity (which to target?) but also richer strategy.
- Speed: Does the snake speed up as it grows? Classic Snake does. Adds a temporal dynamic.
- Body in tokens: How to represent the growing body efficiently? This is a Snake-specific tokenization challenge.
Snake was the first game tested. The proof-of-concept trained a 31K-parameter transformer on 200 episodes from mixed agents.
| Parameter | Value |
|---|---|
| Model | 2-layer, 32-dim, 4-head transformer |
| Context window | 64 tokens |
| Vocabulary | 74 tokens |
| Training data | 200 episodes (40% Random, 40% Greedy, 20% WallFollower) |
| Training steps | 5000 |
| Metric | Result |
|---|---|
| Loss | 4.47 → 0.25 (random baseline: ln(74) ≈ 4.3) |
| Physical validity | 95% — moves are adjacent cells, positions in bounds |
| Rule validity | 100% — EAT→GROW+FOOD_SPAWN, DIE→EOS |
| Structural validity | 45%* |
*Structural validity is low because the model often hits the 64-token context limit mid-sequence without generating EOS. The test expects complete BOS→EOS episodes — the model generates valid gameplay that simply runs longer than the context window allows.
Sampled sequences read like real Snake games. The model learned:
- Movement: One cell per tick in the direction of input
- Eating: Food collection triggers growth and food respawn
- Death: Wall collision and self-collision end the episode
- Physics: Positions stay within bounds, no teleportation
BOS SNAP PLAYER X5 Y5 DIR_R LEN1 FOOD X8 Y6 SCORE V0
TICK INPUT_L MOVE X4 Y5
TICK INPUT_D MOVE X4 Y6
TICK INPUT_D MOVE X4 Y7
TICK INPUT_R MOVE X5 Y7
TICK INPUT_R MOVE X6 Y7
...