The pack
snake
function snake(): Pack<{ snake: typeof api }>
Install the renderer-independent Snake/Nibbler rules API as game.snake.
Helpers
createSnake
function createSnake(config: SnakeConfig)
Create an independent Snake board; a SnakeGame or the host chooses step timing.
createSnakeGame
function createSnakeGame(config: SnakeGameConfig)
Create a Snake/Nibbler session.
DEFAULT_SNAKE_LEGEND
const DEFAULT_SNAKE_LEGEND: SnakeMazeLegend
# wall, . food, space empty, > < ^ v the head and its heading.
NIBBLER_WAVES
const NIBBLER_WAVES: readonly SnakeWave[]
Six authored 31×21 Nibbler-style waves. Rock-Ola's 32 original maze layouts are not published as data, so these are credible approximations built on the documented structure: one-cell corridors, no dead ends (a halted snake can always be steered on), waves 3 and 6 are open fields bounded only by the outer wall, and the cycle repeats faster (wave 7 is wave 1 at a higher speed).
parseSnakeMaze
function parseSnakeMaze( maze: readonly string[], legend: SnakeMazeLegend = DEFAULT_SNAKE_LEGEND, ): Partial<SnakeConfig> & { cols: number; rows: number }
Parse an ASCII maze into board fields: cols, rows, obstacles, foods and, when a head
marker (> < ^ v) is present, start and direction. The body trails straight behind the
head for startLength cells, shortened at a wall. Unknown characters are an error.
snakeGamePreset
function snakeGamePreset( kind: SnakeGameKind = "classic", options: SnakeGameOptions = {}, ): SnakeGameConfig
A complete session preset. classic: walled board, one food at a time, speed rising per food to
a cap, one life, play until you crash or fill the board. wrap: the same with wrapping edges and
three foods. nibbler: Rock-Ola 1982 rules (see the pack docs). Every field can be overridden;
board overrides merge into the preset board.
snakeObstacles
function snakeObstacles(layout: SnakeObstacleLayout, cols: number, rows: number): Cell[]
Obstacle cells for a cols × rows board: box an inner border with a gap in the middle of each
side, pillars 2×2 blocks on a regular lattice, bars two horizontal bars at the quarter heights
with a central gap. The centre row (floor(rows/2), where the default start lies) is always kept
clear. Small boards simply get fewer cells.
snakePreset
function snakePreset( kind: "classic" | "orchard" | "maze" = "classic", options: Partial<SnakeConfig> = {}, ): SnakeConfig
Public authored board presets; dimensions and every rule remain overrideable.
Classes
Snake
class Snake
Owns grid growth, food placement, turn buffering, wall policy and collision/win rules. The host (or a
SnakeGame session) decides when to call step().
readonly cols: number— Board width in cells.readonly rows: number— Board height in cells.readonly config: SnakeConfig— A copy of the configuration the board was built from.constructor(cfg: SnakeConfig)get state()— Copied serialisable state.get body()— Legacy mutable body array (head first); preferstate.body.get food(): Cell— First food, or {-1,-1} when none remains.get foods()— A copy of the foods on the board.get dead()— Whether the snake has died.get won()— Whether the goal has been met.get score()— Points scored on this board.get eaten()— Food eaten on this board.get stalled()— Consecutive steps halted against a wall (stoppolicy).get direction()— Current heading (the direction of the last move, or the initial heading).isObstacle(p: Cell)— True whenpis a wall/obstacle cell.next(from: Cell, d: Direction): Cell— The cell one step fromfromind, wrapped when wrap is on; may be off-board otherwise.input(d: Direction): boolean— Buffer a turn. Up toqueueSizeturns are remembered and applied one per step in the order pressed; each is checked against the LAST queued heading, so up-then-left inside one move works while any reversal into the neck is refused. Returns whether the turn was accepted.step(): SnakeStep— Advance one cell. Food, growth, wall policy, collisions and outcome are resolved here.autoInput()— Safe one-step steering helper; useful for an optional bot, not a claimed solver.snapshot()— A copy of the serialisable state, to save; the same asstate.restore(s: SnakeState)— Replaces the state with a saved one fromsnapshot(). Throws if the body, foods, queue, counters or status are invalid, or the queue is longer thanqueueSize.reset()— Rebuilds the board from its configuration, as at construction: the starting body, foods and seed, with the score and counters at zero.
SnakeGame
class SnakeGame
A complete Snake/Nibbler session around a Snake board. It owns every rule above the
board: the title/ready/playing/paused/dying/clear/over phases, lives and extra lives, wave
progression and looping, the wave timer and its stall acceleration, the speed curve and cap,
score multipliers, time bonus and the high score. The host only calls update(dt), forwards
turns with input(), start() and pause(), and draws state.
readonly config: SnakeGameConfig— The session configuration: a copy of what the constructor was given.constructor(config: SnakeGameConfig)indexFor(n: number)— Index of the authored wave played as waven.waveFor(n: number): SnakeWave | undefined— The authored wave played as waven, if any.boardFor(n: number, game = this.s?.games ?? 0): SnakeConfig— The full board config for waven: base board, then the wave's maze, then its overrides.limitFor(n: number)— Time limit in seconds for waven(Infinity without a timer).speedFor(n: number, eaten: number)— Cells per second for the current wave and eaten count, capped.get board(): Snake— The live board (rules for movement, food and collisions).get state(): SnakeGameState— Copied session state, including the board snapshot.get phase()— The session's lifecycle phase.start(): boolean— Begin a new game from title, game over or a won game; ignored mid-game.newGame()— Begin a new game unconditionally (keeps the high score).title()— Return to the title phase showing the first wave (keeps the high score).pause(on?: boolean): boolean— Pause or resume (toggle whenonis omitted). Only ready/playing can pause.input(d: Direction): boolean— Buffer a turn; accepted during READY too, so a pre-pressed turn applies on the first move.update(dt: number): SnakeGameEvent[]— Advance the session bydtseconds (clamped to 0.25 per call). Returns what happened.snapshot(): SnakeGameState— Serializable copy of the whole session.restore(state: SnakeGameState)— Restore a snapshot taken from a session with the same config.
Types
SnakeConfig
interface SnakeConfig
Dimensions, body/obstacles/foods, turn buffering, wrap, wall and growth/objective policies.
cols: number— Board width in cells; a whole number of at least 1. Width times height may not exceed one million cells.rows: number— Board height in cells; a whole number of at least 1. Width times height may not exceed one million cells.seed?: number— Seed for food placement.wrap?: boolean— Edges join to the opposite side instead of being walls.start?: Cell— Head cell whenbodyis not authored. Defaults to the board centre.body?: Cell[]— Authored body, head first, each cell adjacent to the previous one.startLength?: number— Length of the generated straight body behindstartwhenbodyis absent (default 3).direction?: Direction— Initial heading (default right).obstacles?: Cell[]— Solid cells.foods?: SnakeFood[]— Authored foods. When given, no random food is placed at construction.foodCount?: number— Random foods kept on the board whilerespawnFoodis on (default 1).foodGrowth?: number— Segments added per food unless the food overrides it (default 1).foodScore?: number— Points per food unless the food overrides it (default 10).respawnFood?: boolean— Replace eaten food with a random free cell (default true; ignored forclear-food).goal?: "fill" | "clear-food" | "score"— Win condition besides filling the board:fill(default),clear-foodorscore.targetScore?: number— Score needed for thescoregoal (default 100).queueSize?: number— Buffered turns (default 3). Rapid key presses inside one move are honoured in order.wallPolicy?: SnakeWallPolicy— Wall/obstacle/edge behaviour (defaultdie).autoTurn?: boolean— Withstop, follow single-exit corners without input (default true forstop).
SnakeDeath
type SnakeDeath = "wall" | "obstacle" | "self"
Why a snake died.
SnakeFood
interface SnakeFood extends Cell
An authored food cell with optional appearance kind, growth and score overrides.
kind?: string— Appearance/category tag for the renderer; rules ignore it. Defaults tofood.growth?: number— Segments added when eaten; defaults tofoodGrowth.score?: number— Base points when eaten; defaults tofoodScore. A session may multiply it.
SnakeGameConfig
interface SnakeGameConfig
Session rules around a Snake board: waves, lives, timer, speed curve, scoring and banners.
board: SnakeConfig— Base board rules for every wave (a wave's maze andboardoverride it).waves?: SnakeWave[]— Authored waves. Absent: one endless board (classic Snake).loopWaves?: boolean— After the last wave, repeat from the first with the wave number (speed, timer, multiplier) still rising (default true).lives?: number— Lives at the start (default 3).maxLives?: number— Life cap (default 99).extraLifeEveryWaves?: number— Award a life after clearing every Nth wave (0 = never).extraLifeScores?: number[]— Award a life when the score first reaches each of these totals.speed?: SnakeSpeed— Movement speed curve (default 8 cells/s flat).timer?: SnakeTimer | null— Wave countdown, or null for none.scoreMultiplier?: "none" | "wave"—wavemultiplies food and time-bonus points by the wave number (Nibbler);none(default) does not.keepEatenFood?: boolean— After a death, keep the food already eaten on authored (clear-food) boards (default true).readyTime?: number— Seconds of READY before moving (default 1).deathTime?: number— Seconds the death banner shows (default 1.2).clearTime?: number— Seconds the wave-clear banner shows (default 2).highScore?: number— Initial high score (default 0).startWave?: number— First wave number (default 1).legend?: SnakeMazeLegend— Maze legend; defaults toDEFAULT_SNAKE_LEGEND.
SnakeGameEvent
type SnakeGameEvent = | { type: "go" } | { type: "move" } | { type: "blocked" } | { type: "ate"; food: SnakeFood; points: number } | { type: "died"; cause: SnakeLifeLoss } | { type: "cleared"; wave: number; bonus: number } | { type: "wave"; wave: number } | { type: "extra-life"; lives: number } | { type: "game-over" } | { type: "won" }
Something that happened during SnakeGame.update.
SnakeGameKind
type SnakeGameKind = "classic" | "wrap" | "nibbler"
Named session presets.
SnakeGameOptions
type SnakeGameOptions = Omit<Partial<SnakeGameConfig>, "board"> & { board?: Partial<SnakeConfig>; }
Options accepted by snakeGamePreset: any session field, with partial board overrides.
SnakeGamePhase
type SnakeGamePhase = | "title" | "ready" | "playing" | "paused" | "dying" | "clear" | "over" | "won"
Lifecycle of a SnakeGame.
SnakeGameState
interface SnakeGameState
Copied, serialisable session state.
phase: SnakeGamePhase— The session's lifecycle phase;titlebefore the first game starts.phaseTime: number— Seconds spent in the current phase.resume: SnakeGamePhase— Phase to return to when unpaused.wave: number— 1-based wave number (keeps rising when waves loop).waveIndex: number— Index intowaves(0 without waves).lives: number— Lives left, including the one in play.score: number— Points scored this game.highScore: number— Best score so far across games in this session; starts at the configuredhighScore.timeLeft: number— Seconds left on the wave timer (Infinity without a timer).timeLimit: number— This wave's time limit in seconds (Infinity without a timer).speed: number— Current cells per second.carry: number— Fractional progress towards the next move.stall: number— Seconds the snake has been halted against a wall.bonus: number— Bonus scored by the last wave clear.cause?: SnakeLifeLoss— Cause of the last lost life.eatenTotal: number— Food eaten in this game.awarded: number— Score thresholds already rewarded.games: number— Games started by this session; varies the food seed so each new game differs.board: SnakeState— Snapshot of the current board's state.
SnakeLifeLoss
type SnakeLifeLoss = SnakeDeath | "time"
Why a life was lost: a board death or the wave timer running out.
SnakeMazeLegend
type SnakeMazeLegend = Record<string, "wall" | "empty" | Omit<SnakeFood, "x" | "y">>
Character legend for parseSnakeMaze: a wall, an empty cell or a food template.
SnakeObstacleLayout
type SnakeObstacleLayout = "none" | "box" | "pillars" | "bars"
Named obstacle layouts for open boards.
SnakeSpeed
interface SnakeSpeed
Movement rate in cells per second: start + perWave·(wave−1) + perFood·eaten, capped at max.
start: number— Cells per second on wave 1 before anything is eaten.perFood?: number— Added per food eaten on the current board (classic speed-up).perWave?: number— Added per wave after the first (Nibbler speed-up).max: number— Hard cap in cells per second.
SnakeState
interface SnakeState
Serializable body, foods, turn queue, pending growth, the food stream's kernel state and terminal outcome.
body: Cell[]— Cells the snake occupies, head first.foods: SnakeFood[]— Foods currently on the board.direction: Direction— Heading of the last move, or the initial heading before the first.queue: Direction[]— Buffered turns not yet applied, oldest first; at mostqueueSizelong.growth: number— Segments still to be added: the tail stays put for this many more steps.score: number— Points scored on this board.eaten: number— Foods eaten on this board.status: "playing" | "won" | "lost"—playingwhile the round runs,wononce the goal is met,lostafter a fatal move.random: number[]— The food stream: four words of the kernel'screateRngstate, advanced in place as food is placed.ticks: number— Steps taken, including steps spent halted against a wall.stalled?: number— Consecutive steps spent halted against a wall (stoppolicy).held?: boolean— The queue head was refused by a wall and is being held for the next opening (stoppolicy).cause?: SnakeDeath— Cause of the loss, when lost.
SnakeStep
interface SnakeStep
Result of one Snake.step.
moved: boolean— Whether the head advanced a cell this step.ate: boolean— Whether the head moved onto a food this step.dead: boolean— Whether the snake died this step.won: boolean— Whether the goal was met this step.blocked?: boolean— The snake halted against a wall (stoppolicy).food?: SnakeFood— The food eaten this step.cause?: SnakeDeath— Cause of death this step.
SnakeTimer
interface SnakeTimer
Per-wave countdown. Running out costs a life. limit = max(min, seconds + perWave·(wave−1))
unless the wave authors its own time. Seconds left when a wave is cleared score
bonusPerSecond × multiplier each. While the snake is halted against a wall for more than
stallGrace seconds the clock speeds up, doubling every stallDoubling seconds (0 disables).
seconds: number— Time limit of wave 1, in seconds; at least 0.1.perWave?: number— Seconds added to the limit for each wave after the first; negative values shorten it. Default 0.min?: number— Lowest limit the per-wave change may reach, in seconds. Default 0.1.bonusPerSecond?: number— Points per whole second left when a wave is cleared, before the wave multiplier. Default 0.stallGrace?: number— Seconds halted against a wall before the clock starts running faster. Default 0.stallDoubling?: number— Seconds of further stalling over which the clock's rate doubles, up to 64 times normal; 0 (default) disables the acceleration.
SnakeWallPolicy
type SnakeWallPolicy = "die" | "stop"
What happens when the head meets a wall, obstacle or (without wrap) the board edge.
die: classic Snake. The move is fatal.stop: Nibbler. The snake halts in place (time keeps running) and, withautoTurn, follows a corridor round a corner that has exactly one way on. Biting its own body is always fatal.
SnakeWave
interface SnakeWave
One authored wave: an ASCII maze and/or board overrides, plus optional time/speed overrides.
name?: string— Display name.maze?: string[]— ASCII maze, parsed withparseSnakeMazeand the session'slegend.board?: Partial<SnakeConfig>— Board overrides applied after the maze.time?: number— Time limit in seconds for this wave.speed?: number— Starting speed for this wave (replacesspeed.start; per-wave/food increments still apply).


