Snake Game
Level: Advanced 60–90 minConcepts: StateAlgorithmsBoundariesEdge Cases
Solutions: C# | TypeScript | Python
Implement the game logic for the classic Snake game. No UI required — focus on the rules, state transitions, and collision detection.
Requirements
The game takes place on a rectangular grid of configurable size (e.g. 10x10).
The Snake
- The snake starts at position
(0, 0)moving right, with a length of 1 - The snake moves one cell per tick in its current direction
- Valid directions: Up, Down, Left, Right
- The snake cannot reverse direction (e.g. moving Right cannot change to Left)
Movement
Each tick:
- The snake moves one cell in its current direction
- If the new head position contains food, the snake grows by 1 (tail doesn’t move)
- If the new head position is empty, the snake moves (tail follows)
- If the new head position is a wall or the snake’s own body, the game is over
Food
- One piece of food exists on the grid at a time
- When the snake eats food, a new piece of food appears at a random empty cell
- Food cannot spawn on the snake’s body
- Accept an injectable random source for testability
Scoring
- Each food eaten scores 1 point
- The game tracks the current score
Grid
- The grid has walls on all four edges
- The snake wraps around OR dies at walls (choose one and document it)
Test Cases
| Scenario | Setup | Action | Result |
|---|---|---|---|
| Initial state | New game 5x5 | — | Snake at (0,0), length 1, moving right |
| Move right | Snake at (0,0) | tick | Snake at (1,0) |
| Move down | Snake at (0,0), direction Down | tick | Snake at (0,1) |
| Eat food | Snake at (0,0), food at (1,0) | tick | Snake length 2, score 1 |
| Grow correctly | Snake [(0,0)], food at (1,0) | tick | Snake [(1,0),(0,0)] |
| Hit wall | Snake at (4,0) moving right, 5x5 grid | tick | Game over |
| Hit self | Snake [(3,0),(2,0),(1,0),(0,0)], direction Down then Left then Up | tick x3 | Game over (hits own body) |
| Cannot reverse | Moving right | change direction Left | Direction stays Right |
| Food respawns | Snake eats food | — | New food at random empty cell |
Bonus
- Implement wall-wrapping as an alternative to wall-death (configurable)
- Add speed levels — the snake moves faster as the score increases
- Add obstacles — static blocks on the grid that kill the snake on contact
- Track high scores across multiple games
Hint
Represent the snake as a list of coordinates (head first). Movement is: prepend the new head position, remove the tail (unless eating). This makes growth trivial — just skip the tail removal. Test the movement logic before adding food or collisions.
Reference Walkthrough
Full C#, TypeScript, and Python implementations live at tddbuddy-reference-katas/snake-game. Twenty-three scenarios across initial state, basic movement, direction changes, eating food, wall collisions, self collision, game-over behavior, and winning — shared across all three languages — with a mutable Game aggregate backed by an immutable Snake (head-first body list), Position value type, Direction enum, and injectable FoodSpawner for deterministic testing. BoardBuilder and SnakeBuilder make test setup readable.
- C# (.NET 8, xUnit, FluentAssertions) — walkthrough
- TypeScript (Node 20, Vitest, strict types) — walkthrough
- Python (3.11, pytest, frozen dataclass, Enum) — walkthrough
This kata ships in Agent Full-Bake (F3) mode: one commit per language with the full domain design landing together. The walkthroughs read as design rationale — why the snake is a head-first list (movement = prepend head, optionally pop tail), why Snake.Move() returns a new instance (collision detection reads pre-move state), why FoodSpawner is a function not an interface (single-method collaborator), why direction reversal is silently ignored rather than throwing (spec says “direction stays”). The reference chooses wall-death over wall-wrapping; bonus items (wrapping, speed levels, obstacles) are deliberately out of scope. See the repo’s Gears section for when middle gear is the right call.
Related reading
- Examples Pin Intent. Properties Pin the Invariants.
Example-based tests anchor scenarios in the team's vocabulary. Property-based tests anchor invariants across the input space. The agent needs both axes, and most suites only carry one. - Katas Are Rehearsal, Not Performance
You don't practice TDD on production code. You practice on katas and bring the muscle memory to production. The gap between knowing TDD and doing TDD is reps. - The Test Pyramid Was an Economic Argument
The test pyramid was not a quality law. It was a cost structure: unit tests were cheap, integration tests were expensive, so you wrote many of the first and few of the second. Agents collapsed the cost of writing tests at every level, and the cheapest test that still tells the truth is the one that pins a seam the agent cannot fake.