The pack
trafficRelease
function trafficRelease(): Pack<{ trafficRelease: TrafficReleaseApi }>
Install pure rules; no renderer, DOM or simulation hook is required.
Helpers
createTrafficRelease
const createTrafficRelease = (config: TrafficConfig, solution?: TrafficAction[])=> …
Create a traffic puzzle. Throws on a configuration it refuses (out-of-range sizes, overlaps, a route that does not start at its piece, jumps or does not leave the board) or a solution that does not replay to a win.
generateTrafficRelease
function generateTrafficRelease(options: TrafficGenerateOptions): TrafficLevel
Generate a seeded level whose solution is replayed before it is returned. Candidates are tried until one has at most maxInitialChoices open routes and a blocking chain at least minDependencyDepth long; throws generation: no ... level met ... when attempts run out, and on out-of-range options.
straightTrafficRoute
function straightTrafficRoute(cells: Voxel[], direction: Voxel, bounds: Pick<TrafficConfig, 'width' | 'height' | 'depth'>, id = 'exit'): TrafficRoute
A straight route for cells: one step at a time along direction (a unit axis such as { x: 1, y: 0, z: 0 }) until wholly outside bounds. Throws on any other direction or a route longer than 64 steps.
Classes
TrafficRelease
class TrafficRelease
A traffic puzzle's rules, with undo, redo and a solver. Get one from createTrafficRelease.
constructor(config: TrafficConfig, solution: TrafficAction[] = [])get config(): TrafficConfig— A copy of the configuration.get state(): TrafficState— A copy of the current state.get solved(): boolean— True when every piece has left the board.get lost(): boolean— True whenmaxMistakesblocked releases have been made with pieces still on the board. A release-only puzzle has no other way to lose.cells(id: string): Voxel[]— The cells pieceidcovers now (part way along its route if it is moving); empty once it has left or for an unknown id.hazardCells(): { id: string; cells: Voxel[]; }[]— Where each hazard is at the current tick.inspect(id: string, route: string): TrafficClearance— What stands in the way of pieceidleaving byroutenow, step by step; changes nothing.preview(a: TrafficAction): ActionResult<TrafficEvent>— Whatapplywould return fora, without playing it; never costs a mistake.apply(a: TrafficAction): ActionResult<TrafficEvent>— Play a move. Returns{ valid: true, events }, or{ valid: false, reason, events: [] }with a reason:blocked,reserved-lane,hazard,already-moving,unknown-piece,unknown-routeormistake-limit. A blocked release counts as a mistake (and goes in the history) whenmaxMistakesis set.legalActions(): TrafficAction[]— Every release that is clear now, then{ type: 'tick' }while the puzzle is unsolved.solve(maxStates = 20000): SearchResult<TrafficAction>— A winning continuation, without changing anything: the rest of the creation certificate when it still applies, otherwise a greedy plan (release the first clear piece, else wait a tick). Greedy is exact here: a piece that leaves never blocks anything again, so no release can spoil the puzzle.unsolvableafter 120 idle ticks or at the mistake limit;budget-exhaustedaftermaxStatessteps (default 20,000).undo(): boolean— Take back the last move (a tick, a release or a mistake); false when there is none.redo(): boolean— Replay the move last taken back; false when there is none.reset(): void— Return to the start and clear the undo and redo history.
Types
TrafficAction
type TrafficAction = { type: 'release'; id: string; route: string; } | { type: 'tick'; }
A move: release piece id along its route route, or let one tick of time pass (moving pieces advance a step, hazards a phase).
TrafficClearance
interface TrafficClearance
What stands in a route's way, from inspect.
clear: boolean— True when nothing does: the piece can be released by this route now.blockers: { /** A parked \piece`, a `wall`, a moving piece's `reservation` or a `hazard` at the tick of that step. / kind: 'piece' | 'wall' | 'reservation' | 'hazard'; /* The blocking piece's or hazard's id; `'wall'` for a wall. / id: string; /* The cell where it blocks. / cell: Voxel; /* The route step (footprint index) that meets it. */ frame: number; }[]` — Everything in the way, by route step.swept: Voxel[]— Every cell the route sweeps, corners of turns included: what a presenter highlights.
TrafficConfig
interface TrafficConfig
A level: the board's size, the pieces and their routes, and optional walls, hazards and a mistake limit.
width: number— Cells across, 1 to 24.height: number— Cells deep (rows of a flat board), 1 to 24.depth: number— Cells high, 1 to 24; 1 for a flat board.pieces: TrafficPiece[]— The pieces, 1 to 256, with unique ids and no shared cells.walls?: Voxel[]— Cells that are always blocked (barriers, kerbs); inside the board and on no piece.hazards?: TrafficHazard[]— Moving obstacles, checked against each route step at the tick it would happen.maxMistakes?: number— Blocked release attempts allowed before the level is lost, 0 to 100; 0 (the default) means no limit.
TrafficDifficulty
type TrafficDifficulty = 'easy' | 'medium' | 'hard' | 'very-hard'
Generator tier: a larger board and, from medium up, fewer open routes and longer chains of blockers.
TrafficEvent
type TrafficEvent = { type: 'released' | 'departed'; id: string; } | { type: 'tick'; tick: number; } | { type: 'mistake'; reason: string; }
What a move did: a piece was released or has departed (left the board), a tick passed, or a blocked release cost a mistake.
TrafficGenerateOptions
interface TrafficGenerateOptions
What generateTrafficRelease takes; everything but seed has a default.
seed: number— Unsigned 32-bit seed (0 included); the same options give the same level.difficulty?: TrafficDifficulty— Tier; default'medium'.preset?: TrafficPreset— What to build; default'parking'.layout?: TrafficLayout— The shape filled; default'rectangle'(flat) or'cube'(voxels).size?: number— Board side in cells: 2 to 12 flat (default 4, 6, 8, 10 by tier; parking adds a one-cell border) or 2 to 6 for voxels (default 2, 3, 4, 5).density?: number— Share of the shape's places that get a piece, 0.35 to 1; default 1.hazards?: boolean— Flat presets: add a pedestrian just off the left edge of rowfloor(size / 2), there for 2 ticks in every 6, so a car leaving that way must wait for a gap; default false.maxMistakes?: number— Blocked releases allowed before the level is lost, 0 to 100; default 0 (no limit).attempts?: number— Candidate levels to try before throwing, 1 to 1,000; default 40.maxInitialChoices?: number— Most pieces that may be clear at the start, 1 to 256; default by tier (streets 256, 5, 3, 3; voxels 256, 5, 3, 2, and 6 for a diamond; parking 256).minDependencyDepth?: number— Least length of the longest chain of pieces blocking one another, 1 to 256; default by tier (1, 4, 7, 10; parking 1).
TrafficHazard
interface TrafficHazard
A moving obstacle (a pedestrian, a train) that repeats: at tick t it covers phases[t % phases.length].
id: string— Unique non-empty id.phases: Voxel[][]— 1 to 30 phases of cells it covers; an empty phase is a safe moment to cross. All hazards together repeat within 120 ticks.
TrafficLayout
type TrafficLayout = 'rectangle' | 'courtyard' | 'staggered' | 'cube' | 'pyramid' | 'arch' | 'diamond'
The shape filled: rectangle, courtyard (a hole in the middle) or staggered (a notched edge) for flat presets; cube, pyramid, arch or diamond for voxels.
TrafficLevel
interface TrafficLevel
A generated level: its configuration, a winning solution and what was measured.
seed: number— The seed asked for (the accepted candidate may have been drawn from a later one, but this seed reproduces it).difficulty: TrafficDifficulty— The tier asked for.parameters: Required<Omit<TrafficGenerateOptions, 'attempts' | 'maxInitialChoices' | 'minDependencyDepth'>>— The settings used, defaults filled in.config: TrafficConfig— The level, ready forcreateTrafficRelease.solution: TrafficAction[]— A replay-checked winning sequence of releases and ticks.metrics: { /** Pieces on the board. */ pieces: number; /** Pieces clear to leave at the start. */ initialChoices: number; /** Pieces with no clear route at the start. */ blockedPieces: number; /** Longest chain of pieces each blocking the next along the solution's routes. */ dependencyDepth: number; /** Solution routes that turn a corner (streets). */ turningRoutes: number; /** Whether the level met the targets below; always true for a returned level. */ difficultyTargetMet: boolean; /** The open-route target the level met. */ maxInitialChoices?: number; /** The chain-depth target the level met. */ minDependencyDepth?: number; }— The level's structure; none of it claims a shortest plan or a human difficulty.
TrafficMotion
interface TrafficMotion
A piece on its way out.
id: string— The piece's id.route: string— The route it took.frame: number— Which of the route's footprints it is on now.reserved: Voxel[]— Every cell its route sweeps, held until it is off the board, so nothing else may cross its path.
TrafficPiece
interface TrafficPiece
A car, block or voxel with the routes it can leave by.
id: string— Unique non-empty id.key?: string— Optional colour or kind for the presenter (and forboarding-queue's buses); the rules ignore it.cells: Voxel[]— The cells it covers, 1 to 64, inside the board and on no other piece or wall.routes: TrafficRoute[]— Its ways out, at least one. Releasing takes one of them whole.
TrafficPreset
type TrafficPreset = 'parking' | 'streets' | 'voxels'
What generateTrafficRelease builds: streets (Traffic Escape: cars on a road grid, some turning), voxels (Tap Away: a block of cubes, each leaving along one of six axes) or parking (a walled lot whose cars can leave either way along their axis; Bus Out's lot).
TrafficReleaseApi
type TrafficReleaseApi = typeof kit & { /** Same seeded generator off-thread; frame-delivered results require a started game. */ generateTrafficReleaseAsync(options?: kit.TrafficGenerateOptions, job?: { signal?: AbortSignal }): Promise<kit.TrafficLevel>; }
Public headless traffic release puzzles helpers.
TrafficRoute
interface TrafficRoute
One way out for a piece: its footprint at every step, from where it stands to wholly off the board.
id: string— Non-empty id, unique among the piece's routes; actions name it.frames: Voxel[][]— 2 to 65 footprints. The first is the piece'scells; each next one keeps the same cells in the same order, each moved at most two steps (so a turn can pivot); the last lies wholly outside the board.
TrafficState
interface TrafficState
Where a traffic puzzle stands.
remaining: string[]— Ids of the pieces still on the board, moving ones included.moving: TrafficMotion[]— The pieces on their way out.departed: string[]— Ids of the pieces that have left, in order.tick: number— Ticks played.moves: number— Releases played.mistakes: number— Blocked releases counted againstmaxMistakes.
Voxel
interface Voxel
A cell, in whole numbers from -64 to 64: x across, y the board's depth (rows on a flat board), z height (0 on a flat board).
x: number— Column across the board.y: number— Row, or depth into a volume.z: number— Layer up from the floor; 0 on a flat board.


