from enum import Enum
class Direction(Enum): UP = (0, -1) # x right, y down DOWN = (0, 1) LEFT = (-1, 0) RIGHT = (1, 0)
class GameState: def init(self, width=20, height=20, rng=None, body=None, food=None, pending_growth=0): # rng: a random.Random instance. All randomness goes through it. # body: optional list of (x, y), head first. For tests. # food: optional (x, y). For tests. # pending_growth: segments still to be added. For tests. ...
snake: list # (x, y) tuples, head first
food: tuple # (x, y)
score: int # foods eaten
game_over: bool
def step(self, direction) -> None
- Default construction (no body override): the snake spawns with length 2, head at (width // 2, height // 2), tail one cell to its left, heading RIGHT, score 0, pending_growth 0, and food spawned via rng on a cell not occupied by the body.
- One step() call is one tick. The head advances one cell.
- 180-degree reversal input is ignored; the snake continues in its current direction (current direction = head minus neck).
- Eating food: score += 1, pending_growth += 1, new food spawns via rng on a cell not occupied by the body.
- Growth: while pending_growth > 0, the tail is not removed on that tick and pending_growth decrements.
- The tail cell is vacated in the same tick the head moves. Moving the head into the cell the tail is vacating is legal, UNLESS the snake is growing that tick (tail stays put), in which case it is a collision.
- Hitting a wall or any body cell that is not vacating = game_over. No wraparound.
- Speed ramp is the renderer's concern (tick rate derived from score). The engine knows nothing about time.