Phaser AE

Falling-piece puzzles

Pro packv3.0.0-42DGrid

The rules engine for falling-piece games: line-clearing blocks, colour-matching jewels, capsules and viruses, chaining blobs, and any variation on them. It is frame-timed and deterministic, presets reproduce the classic rules, and every rule is a field you can change.

This is a Pro pack. Upgrade to Phaser Pro to use it in your games.See Phaser Pro →

Released October 14, 2026 · updated October 20, 2026

Falling-piece puzzles: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

The pack

faller

function faller(): Pack<{ faller: FallerApi }>

The faller pack. Pure logic, so it needs no backend and runs headless.

Helpers

capsuleRotation

function capsuleRotation(): RotationSystem

Dr. Mario capsules: two halves in a 2x2 box whose bottom-left cell is always filled. States: 0 = horizontal [a b], 1 = vertical a over b, 2 = horizontal [b a], 3 = vertical b over a (clockwise order). A turn to horizontal that is blocked on the right kicks one column left (NES); a turn to vertical has no kick. Halves carry links so they stay joined after locking until one of them is cleared. Spawn: horizontal, left half at column floor(cols / 2) - 1 (columns 4-5 of 8), row 0.

COLUMNS_GRAVITY_FRAMES

const COLUMNS_GRAVITY_FRAMES: readonly [40, 34, 29, 25, 21, 18, 15, 13, 11, 9, 8, 7, 6, 5, 4, 3, 2]

Columns frames per row by level (est.: no published table was found; tuned to the arcade's pace).

COLUMNS_JEWELS_PER_LEVEL

const COLUMNS_JEWELS_PER_LEVEL: { readonly easiest: 50; readonly easy: 40; readonly normal: 35; readonly hard: 25; }

Jewels per level by the arcade's difficulty DIP switch (Sega "Columns Conversion" manual, Figure 9).

COLUMNS_START

const COLUMNS_START: { readonly 0: 0; readonly 5: 20000; readonly 10: 50000; }

The arcade's start choices (KLOV screenshot of the level select): level and starting bonus.

columnsArcade

function columnsArcade(opts: ColumnsOptions = {}): FallerRules

Columns (Sega 1990): three-jewel columns that fall in half-row steps and cycle their order with the button; matches of three or more in rows, columns and diagonals; cascades scored 30 x step x lines-of-three x (level + 1); a level every 35 jewels (DIP); the start-level bonus; the magic jewel that clears every jewel of the colour it lands on; game over when jewels are left above the well.

columnsScoring

function columnsScoring(): ScoringFn

Columns scoring, after the Mega Drive analysis on TASVideos (forum topic 5090): each chain step scores 30 x step x lines-of-three x (level + 1), where a run of four is two lines of three and five is three. Soft drop scores (level + 1) per half row, i.e. 2 x (level + 1) per row. A magic-jewel clear has no documented value; this scores 10 x (level + 1) per jewel, the per-jewel rate of a line of three (an estimate, noted in the docs).

CRASH

const CRASH: 100

Colour offset that marks a crash gem: a crash gem of colour c is stored as CRASH + c.

CRASH_DAMAGE

const CRASH_DAMAGE: CrashDamage

Damage as estimated by players of the original (no published formula exists): a broken gem is worth one counter gem, a power gem cell 1.25 (a 2 x 2 power gem and its crash gem send about six), each gem in chain step n counts n times, a destroyed counter gem 0.5, and the diamond's break is halved (HD Remix: 80 %).

crashGemDeal

function crashGemDeal(r: CrashGemRules): PieceSpec[]

Builds the pre-generated drop order: both boards on one seed get the same pairs (as in the original).

crashGemFallerRules

function crashGemFallerRules(r: CrashGemRules): FallerRules

The FallerRules a crash-gem board runs its pair with (matching off; this module resolves moves).

createCrashGems

function createCrashGems(rules: CrashGemRules): CrashGemGame

Creates a crash-gem board. Throws a RangeError for an unsupported configuration.

createFaller

function createFaller(rules: FallerRules): FallerGame

Creates a game from a ruleset.

cycleRotation

function cycleRotation(length = 3, spawnColumn?: (cols: number) => number): RotationSystem

Columns: a vertical column of length jewels (origin = the bottom jewel). Rotating does not move the footprint; it cycles the colours (clockwise moves every jewel down one place and the bottom one to the top). A magic column does not cycle.

deal

function deal(rule: DealRule, memory: DealMemory, rng: Rng): PieceSpec

Deals the next piece by rule, updating memory.

dealMemory

function dealMemory(): DealMemory

A fresh dealer memory.

DIAGONAL_SEND_RATE

const DIAGONAL_SEND_RATE = (raw: number): number=> …

The original's send rate for the two diagonal patterns: floor(x / 4) + floor(x / 2), about 75 %.

DIAMOND

const DIAMOND: 200

Colour code of the diamond.

DR_MARIO_BASE

const DR_MARIO_BASE: readonly [100, 200, 300]

Dr. Mario base points per virus by speed: LOW, MED, HI.

DR_MARIO_GRAVITY_FRAMES

const DR_MARIO_GRAVITY_FRAMES: readonly [70, 68, 66, 64, 62, 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, 30, 28, 26, 24, 22, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1]

Dr. Mario NES NTSC frames per row by speed index 0..80 (https://tetris.wiki/Dr._Mario).

DR_MARIO_MULTIPLIERS

const DR_MARIO_MULTIPLIERS: readonly [1, 2, 4, 8, 16, 32]

Dr. Mario per-virus multipliers for the 1st..6th+ virus of one move (https://www.mariowiki.com/Dr._Mario_(game)).

DR_MARIO_SPEED_START

const DR_MARIO_SPEED_START: readonly [15, 25, 31]

Starting speed index for LOW, MED and HI.

drMario

function drMario(opts: DrMarioOptions = {}): FallerRules

Dr. Mario (Nintendo 1990): 8 x 16 bottle, viruses placed by the NES algorithm (count 4 x (level + 1), max 84; rows by level; the distance-two colour rule), capsules from a 128-entry loop of the nine colour pairs, the 2x2 rotation with the left kick, four in a row or column clears (viruses included), unsupported halves fall at four rows a second, LOW/MED/HI speeds with a speed-up every ten capsules, scoring by viruses per move, level clear and top-out when the bottle's neck is blocked.

drMarioScoring

function drMarioScoring(): ScoringFn

Dr. Mario scoring: only viruses score. The k-th virus cleared in one move (a capsule and every cascade it causes) is worth base x 1, 2, 4, 8, 16, 32 (sixth and later), with base 100 / 200 / 300 for LOW / MED / HI.

DROP_PATTERNS

const DROP_PATTERNS: Readonly<Record<string, DropPattern>>

The eleven drop patterns of Super Puzzle Fighter II Turbo (1996), read from the original-pattern charts in David Sirlin's "Balancing Puzzle Fighter" (2014; he rebalanced the game for HD Remix and published the old patterns), cross-checked against the row layout in the X' rebalance patch source (github.com/KScl/puzzle-fighter-x-prime). Keys are neutral descriptions; source names the fighter that used the pattern.

GEM_LETTERS

const GEM_LETTERS: Readonly<Record<string, number>>

Colour letters used by drop patterns: R G B Y = 0 1 2 3 (the original's red, green, blue, yellow).

gemColor

function gemColor(c: Cell): number

The base colour of a cell (crash gems and counter gems included); -1 for the diamond.

gemPairRotation

function gemPairRotation(spawnColumn: (cols: number) => number): RotationSystem

The pair rotation of a crash-gem board: Puyo-style orbit with wall and floor kicks, except that a vertical pair wedged in a one-wide well flips 180 degrees on a single press (the original; Sirlin 2014, "the diamond trick").

gemType

function gemType(c: Cell): GemType

The gem type of a cell.

guidelineAttack

function guidelineAttack(e: LinesEvent): number

Lines of garbage sent by a Guideline line clear (the common Guideline attack table): single 0, double 1, triple 2, tetris 4; T-spin single 2, double 4, triple 6 (mini: 0 / 1); +1 back-to-back; combo bonus by count 0,1,1,2,2,3,3,4,4,4,5. A versus layer can supply its own table; the faller pack only reports it.

guidelineGravity

function guidelineGravity(level: number): number

Guideline Marathon gravity (https://tetris.wiki/Marathon): (0.8 - (level - 1) x 0.007)^(level - 1) seconds per row, as rows per 60 Hz frame; 20G (instant) from level 20.

guidelineScoring

function guidelineScoring(opts: GuidelineScoringOptions = {}): ScoringFn

Tetris Guideline scoring (https://tetris.wiki/Scoring): single 100, double 300, triple 500, tetris 800; T-spin mini 100 (no lines) / 200 (single) / 400 (double); T-spin 400 / 800 / 1200 / 1600; all times the level BEFORE the clear. Difficult clears in a row earn x1.5 (back-to-back); combos add 50 x combo x level; soft drop 1 per row, hard drop 2 per row (never multiplied).

NES_GRAVITY_FRAMES

const NES_GRAVITY_FRAMES: readonly [48, 43, 38, 33, 28, 23, 18, 13, 8, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1]

NES Tetris NTSC frames per row by level 0..29 (https://tetris.wiki/Tetris_(NES,_Nintendo)).

nesAre

function nesAre(ctx: TimingContext): number

NES entry delay: 10 frames for a lock in the bottom two rows, 2 more for each 4 rows above, up to 18.

nesScoring

function nesScoring(): ScoringFn

NES Tetris scoring (https://tetris.wiki/Scoring, meatfighter's ROM analysis): 40 / 100 / 300 / 1200 x (level + 1), using the level AFTER the clear is counted, plus push-down points (rows of the final soft drop, not multiplied).

NRS_SHAPES

const NRS_SHAPES: Record<string, XY[][]>

NES (Nintendo Rotation System) orientations, relative to the pivot, from the orientation table in meatfighter's analysis of the ROM. Each list is in CLOCKWISE order; NRS_SPAWN gives the spawn index.

NRS_SPAWN

const NRS_SPAWN: Record<string, number>

NES spawn orientations (Td, Jd, Zh, O, Sh, Ld, Ih) as indices into NRS_SHAPES.

nrsRotation

function nrsRotation(): RotationSystem

The Nintendo Rotation System of NES Tetris: pivot rotation, right-handed, no kicks, spawn at column 5, row 0.

nuisanceFromScore

function nuisanceFromScore(score: number, targetPoints = 70, carry = 0): { nuisance: number; carry: number }

Puyo nuisance from a chain score (https://puyonexus.com/wiki/Scoring): NP = score / targetPoints + carry; the whole part is sent, the fraction carries to the next chain. Target points are 70 in Tsu.

pairRotation

function pairRotation(spawnColumn?: (cols: number) => number): RotationSystem

Puyo pairs: the axis puyo stays put and the child orbits it. A turn into a wall or another puyo pushes the pair one cell away; a turn into the floor lifts it one row. A vertical pair wedged between walls cannot turn; a second press then flips it 180 degrees (the quick turn), lifting it if the child would go into the floor. Colours are [axis, child]. spawnColumn defaults to the third column of six (floor((cols - 1) / 2)).

placeViruses

function placeViruses(field: Field, rng: Rng, level: number, rule: VirusRule): number

Places Dr. Mario viruses by the NES algorithm (https://tetris.wiki/Dr._Mario): pick a row up to the level's maximum and a column; colour = remaining % 4 (0 yellow, 1 red, 2 blue; 3 = random from the 16-entry table); step right (wrapping up a row) past occupied cells and past cells whose distance-two neighbours already hold all three colours; change the colour (yellow -> blue, red -> yellow, blue -> red) until no distance-two neighbour shares it. Rows count from 1 at the bottom. Returns the number placed.

PUYO_COLOR_BONUS

const PUYO_COLOR_BONUS: readonly [0, 3, 6, 12, 24, 48]

Puyo colour bonus by colours cleared in one step (1..6), Tsu (https://puyonexus.com/wiki/Scoring).

PUYO_GRAVITY_FRAMES

const PUYO_GRAVITY_FRAMES: readonly [36, 30, 26, 22, 19, 16, 14, 12, 10, 8, 7, 6, 5, 4, 3, 2]

Puyo frames per row by level (est.: endless is not a Tsu mode; this is a gentle curve from Tsu's opening pace).

PUYO_GROUP_BONUS

const PUYO_GROUP_BONUS: readonly [0, 2, 3, 4, 5, 6, 7, 10]

Puyo group bonus by group size 4..11+ (https://puyonexus.com/wiki/Scoring).

PUYO_LETTERS

const PUYO_LETTERS: Record<string, number>

Letter-to-colour map for Puyo layouts: R, G, B, Y, P.

PUYO_MISSIONS

const PUYO_MISSIONS: readonly PuyoMission[]

Authored missions (original puzzles in the nazo-puyo style; solvable, verified by the tests).

PUYO_TSU_CHAIN_POWER

const PUYO_TSU_CHAIN_POWER: readonly [0, 8, 16, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 480, 512, 544, 576, 608, 640, 672]

Puyo Puyo Tsu chain power for chains 1..24 (https://puyonexus.com/wiki/Chain_Power_Table); 24+ stays at 672...

puyoStepScore

function puyoStepScore(chain: number, groups: readonly number[], colors: number, popSize = 4): number

The Puyo Puyo Tsu score of one chain step: 10 x PC x clamp(CP + CB + GB, 1, 999). popSize is the minimum group (4).

puyoTsu

function puyoTsu(opts: PuyoOptions = {}): FallerRules

Puyo Puyo Tsu single-player field rules: 6 x 12 with a hidden 13th row (it holds puyos but never pops) and a 14th row where puyos vanish; pairs that orbit their axis with wall and floor kicks and the double-press quick turn; groups of four or more pop, nuisance next to a pop clears, cascades score 10 x PC x (chain power + colour bonus + group bonus) with Tsu's tables; all clears; the X in the third column ends the game. Nuisance (versus) follows Tsu: 30 at most per move, whole rows first then random distinct columns, 70 target points, +30 after an all clear. Modes: endless (speed rises every piecesPerLevel pieces: a house mode, Tsu has none) and missions.

puyoTsuScoring

function puyoTsuScoring(popSize = 4): ScoringFn

Puyo Puyo Tsu scoring: chain steps by puyoStepScore; soft drop 1 per row.

puzzleFighter

function puzzleFighter(opts: PuzzleFighterOptions = {}): CrashGemRules

Super Puzzle Fighter II Turbo (Capcom, 1996) board rules. From the PlayStation manual (Capcom): pairs of gems drop from the top centre; a crash gem breaks the gems of its colour linked to it; four or more same-coloured gems in a rectangle form a power gem; counter gems start at 5 and turn into normal gems at 0, counting down as each pair falls; counter gems that still fall after some are countered start at 3; counter gems touching a break are destroyed whatever their colour; the rainbow gem (diamond) breaks every gem of one colour for fewer counter gems; the round is lost when gems pile into the gap where new gems fall. From Wikipedia and Sirlin (2014): a 6 x 12 board whose fourth column takes the new pair; the diamond every 25 pairs; diamond damage 50 % (HD Remix 80 %); the 70 % (ROM: floor(x/4) + floor(x/2)) send rate of the two diagonal patterns; one press flips a pair wedged in a one-wide well. Estimates (no published figures): the damage values above, the crash gem rate (1 in 8 gems), the drop speed table, the lock delay, flash and fall timings, the columns a partial counter row lands in (random), and the score (10 a gem x chain step).

SRS_KICKS_I

const SRS_KICKS_I: Record<string, readonly (readonly [number, number])[]>

SRS wall kicks for the I piece, same convention as SRS_KICKS_JLSTZ.

SRS_KICKS_JLSTZ

const SRS_KICKS_JLSTZ: Record<string, readonly (readonly [number, number])[]>

SRS wall kicks for J, L, S, T and Z, keyed from>to (0, 1 = R, 2, 3 = L), in the wiki's convention (+x right, +y UP). Five tests, tried in order.

SRS_SHAPES

const SRS_SHAPES: Record<string, XY[][]>

SRS shapes, per kind, per rotation state (0 = spawn, R, 2, L), relative to the top-left of the bounding box.

srsRotation

function srsRotation(): RotationSystem

The Super Rotation System (Tetris Guideline): SRS shapes, spawn centred (rounded left) in rows 21-22, and the full five-test kick tables, I piece included. 180-degree rotation is not part of SRS: it is two 90-degree turns' worth of state with only the plain test, and the engine offers it only when a ruleset enables it.

tableRotation

function tableRotation(opts: TableRotationOptions): RotationSystem

Builds a rotation system from shape tables (and optional kick tables): SRS, NRS or your own pieces.

tetrisGuideline

function tetrisGuideline(opts: GuidelineOptions = {}): FallerRules

Tetris by the Guideline: SRS with full kicks, 7-bag, hold, a next queue, ghost, 0.5 s lock delay with 15 move resets (restored on reaching a new lowest row), soft drop at 20x gravity (1 point a row), hard drop (2 points a row), T-spins (3-corner, mini by the pointing side, upgraded by the fifth kick), back-to-back x1.5, combos, the Marathon gravity curve, 10 lines a level, block-out / lock-out top-out, and a 20-row buffer above the well.

tetrisNes

function tetrisNes(opts: NesOptions = {}): FallerRules

NES Tetris (Nintendo 1989): the Nintendo Rotation System (right-handed, no kicks), no hold, no hard drop, no ghost, the NES gravity table, DAS 16/6 (charge kept between pieces, frozen during ARE, charged by a wall, shifting ignored while Down is held), soft drop at 1/2G after 3 frames that must be re-pressed for each piece, no lock delay, ARE 10-18 by lock height plus 17-20 frames for a line clear, 40/100/300/1200 x (level + 1) scoring plus push-down points, the NES level-up rule, the reroll randomiser, and a 999,999 score cap.

TETROMINOES

const TETROMINOES: readonly ["I", "O", "T", "S", "Z", "J", "L"]

The seven tetromino kinds in Guideline colour order: I cyan, O yellow, T purple, S green, Z red, J blue, L orange.

Classes

CrashGemGame

class CrashGemGame

A crash-gem board (Super Puzzle Fighter II Turbo rules). Drive it with frame(input) at 60 Hz; read the cells, power gems, counter timers and the falling pair; drain events. Versus hooks: queueCounters, offsetCounters, pendingCounters, and the crash / moveEnd events' attack.

  • readonly rules: CrashGemRules — The ruleset.
  • readonly faller: FallerGame — The pair engine (its well is this board's well).
  • constructor(rules: CrashGemRules) — Creates a board.
  • reset(): void — Starts the board over with the same rules and seed.
  • get phase(): CrashGemPhase — Resolution phase ('piece' while a pair is in play or about to be).
  • get phaseProgress(): number — Progress through the current resolution phase, 0..1.
  • get piecePhase(): string — The pair engine's phase ('ready', 'are', 'active', ...).
  • get frameCount(): number — Frames since the board started.
  • get score(): number — Score.
  • get chain(): number — Current chain step (0 when none).
  • get bestChain(): number — Best chain this round.
  • get pendingCounters(): number — Counter gems waiting to fall here.
  • get pendingBatches(): readonly CounterBatch[] — The batches waiting (count, pattern, short countdown).
  • get over(): boolean — True once the round is lost.
  • get reason(): string — Why it ended.
  • get busy(): boolean — True while a move is resolving (breaks, falls, counter gems landing).
  • get active(): FallerGame['active'] — The falling pair (colours encode the gem type; see gemType), or null.
  • next(): PieceSpec[] — The next pair(s).
  • cell(x: number, y: number): Cell | null — The cell at (x, y) (row 0 = top visible row).
  • counterTimer(id: number): number | undefined — The countdown of a counter gem (by cell id), or undefined.
  • powerOf(id: number): number | undefined — The power gem a cell belongs to (by cell id), or undefined.
  • get flashing(): readonly XY[] | null — Gems flashing before they break.
  • get falling(): ReadonlyMap<number, number> | null — Cells falling: id -> rows still to fall at the start of this step (multiply by 1 - phaseProgress).
  • powerGems(): PowerGem[] — Every power gem on the board.
  • drain(): CrashGemEvent[] — Removes and returns the pending events.
  • queueCounters(count: number, pattern: DropPattern = this.rules.pattern): void — Queues counter gems from an attacker with its drop pattern. They fall after this board's next move resolves.
  • offsetCounters(count: number): number — Cancels up to count waiting counter gems (newest first); the survivors start at the shorter countdown. Returns how many were cancelled.
  • snapshot(): CrashGemSnapshot — A deep copy of the whole board.
  • restore(snap: CrashGemSnapshot): void — Restores a snapshot taken from a board with the same rules.
  • frame(input: FallerInput = {}): void — Advances one 60 Hz frame.
  • placements(): { x: number; rot: number }[] — Every distinct final drop of the falling pair (see FallerGame.placements).
  • place(x: number, rot: number): boolean — Teleports the pair to a placement and drops it (bots and tests). The move resolves on later frames.
  • settleMove(maxFrames = 20000): void — Runs frames with no input until the next pair is in play or the round ends (bounded).
  • preview(x: number, rot: number): CrashGemPreview — What placing the pair at (x, rot) would do, without changing this board: counter gems sent, chain, gems broken, whether the round would end, and the resulting board.

FallerGame

class FallerGame

A falling-piece game. Drive it with frame(input) at 60 Hz; read its state; drain its events.

  • readonly rules: FallerRules — The ruleset (treat as read-only; build a new game to change it).
  • readonly field: Field — The well.
  • constructor(rules: FallerRules) — Creates a game. Throws a RangeError describing any unsupported configuration.
  • reset(): void — Starts the game over with the same rules and seed.
  • get phase(): FallerPhase — Current phase.
  • get phaseProgress(): number — Progress through the current phase, 0..1.
  • get frameCount(): number — Frames since the game started.
  • get score(): number — Score.
  • get level(): number — Level.
  • get lines(): number — Lines cleared (rows games).
  • get cleared(): number — Cells cleared (Columns jewels, Puyo puyos, Dr. Mario cells).
  • get pieces(): number — Pieces locked.
  • get speedIndex(): number — Speed index (Dr. Mario).
  • get viruses(): number — Viruses left (Dr. Mario).
  • get chain(): number — Current chain step during a cascade (0 when none).
  • get bestChain(): number — Best chain this game.
  • get combo(): number — Combo counter (-1 = none).
  • get backToBack(): boolean — True when the next difficult clear earns back-to-back.
  • get held(): PieceSpec | null — Held piece.
  • get holdUsed(): boolean — Hold already used for this piece.
  • get pendingNuisance(): number — Nuisance waiting to fall.
  • get stats(): Readonly<Record<string, number>> — Pieces dealt by kind.
  • get over(): boolean — True once the game has ended either way.
  • get reason(): string — Why the game ended ('' while running).
  • get goalProgress(): number — Best progress toward the objective (chain reached, lines, score).
  • get targetPoints(): number — Target points used for nuisance (Puyo). Sets target points (a versus layer lowers them during margin time).
  • set targetPoints(v: number) — Target points used for nuisance (Puyo). Sets target points (a versus layer lowers them during margin time).
  • next(): PieceSpec[] — The upcoming pieces, rules.previews of them.
  • cell(x: number, y: number): Cell | null — The cell at (x, y) (row 0 = top visible row).
  • get active(): ActiveView | null — The falling piece, or null.
  • ghost(): XY[] — Where the falling piece would land.
  • get clearing(): { cells: XY[]; rows: number[]; magic: boolean } | null — Cells being cleared (flashing), with the rows for a rows game.
  • get falling(): ReadonlyMap<number, number> | null — Cells currently falling in a cascade: id -> rows still to fall at the start of this step (multiply by 1 - phaseProgress for the draw offset).
  • drain(): FallerEvent[] — Removes and returns the pending events.
  • snapshot(): FallerSnapshot — A deep copy of the whole game state (events excluded). Restore with restore.
  • restore(snap: FallerSnapshot): void — Restores a snapshot taken from a game with the same rules.
  • queueNuisance(count: number): void — Queues nuisance to fall after the player's next move resolves (Puyo rules: at most maxPerDrop per move).
  • offsetNuisance(count: number): number — Cancels up to count pending nuisance (offsetting). Returns how many were cancelled.
  • queueGarbage(rows: number, hole?: number): void — Queues garbage rows (rows games) that rise after the next piece locks without clearing lines.
  • frame(input: FallerInput = {}): void — Advances one 60 Hz frame.
  • advance(seconds: number, input: FallerInput = {}): void — Advances seconds of game time at 60 Hz with the same held input (edges apply to the first frame).
  • placements(): { x: number; rot: number }[] — Every final placement of the falling piece reachable by rotating at spawn height and dropping (a bot's move list). For cycling pieces, rot counts colour rotations.
  • place(x: number, rot: number): boolean — Puts the falling piece at a placement and drops it there: a teleport for bots and tests (no scoring for the movement). Returns false when the placement does not fit. The move then resolves on later frames.
  • settleMove(maxFrames = 20000): void — Runs frames with no input until the next piece is in play or the game ends (bounded).
  • preview(x: number, rot: number): PlacementPreview — What placing the current piece at (x, rot) would do, without changing this game: points, chains, attack, lines and whether the game would end. The AI search surface for a versus opponent.

Field

class Field

The well grid. Rows run from -hidden (top of the buffer) to rows - 1 (bottom); row 0 is the top visible row.

  • readonly cols: number — Width in cells.
  • readonly rows: number — Visible height in cells.
  • readonly hidden: number — Buffer rows above the visible well.
  • cells: (Cell | null)[] — Cells, row-major from the top of the buffer. Exposed for snapshots; use get/set.
  • nextId: number — The next id to hand out.
  • constructor(cols: number, rows: number, hidden: number) — Creates an empty well.
  • inside(x: number, y: number): boolean — True when (x, y) is inside the well, buffer included.
  • get(x: number, y: number): Cell | null — The cell at (x, y), or null when empty or outside.
  • set(x: number, y: number, cell: Cell | null): void — Writes a cell (null clears). Ignored outside the well.
  • put(x: number, y: number, color: number, kind: CellKind = 'block', link?: Link): Cell — Creates and places a new cell, returning it.
  • fits(cells: readonly XY[]): boolean — True when every cell is inside the well and empty.
  • each(fn: (cell: Cell, x: number, y: number) => void): void — Calls fn for every occupied cell.
  • count(kind?: CellKind): number — Number of occupied cells, optionally of one kind.
  • top(): number — The highest occupied row, or rows when the well is empty.
  • clear(): void — Empties the well.
  • fullRows(): number[] — Rows that are completely filled, top to bottom.
  • collapseRows(rows: readonly number[]): void — Removes the given rows and drops everything above them (Tetris "naive" gravity).
  • lineMatches(n: number, diagonals: boolean, minY = -this.hidden): LineMatch — Runs of n or more same-coloured cells horizontally and vertically (and diagonally when asked), from row minY down. Nuisance and garbage never match. Viruses match like blocks (Dr. Mario).
  • floodMatches(n: number, minY = -this.hidden): MatchGroup[] — Orthogonally connected same-coloured groups of n or more blocks at or below row minY (Puyo).
  • adjacentNuisance(cells: readonly XY[], minY = -this.hidden): XY[] — Nuisance cells orthogonally next to any of cells, at or below minY (they clear with a popping group).
  • remove(cells: readonly XY[]): Cell[] — Removes cells. A capsule half whose partner is removed becomes a free half. Returns the removed cells.
  • gravityStep(mode: 'cells' | 'linked'): number[] — One step of gravity: every unsupported unit falls one row. cells mode moves each cell on its own; linked mode keeps joined capsule halves together. Viruses never fall. Returns the ids that moved.
  • unsupported(mode: 'cells' | 'linked'): boolean — True when some unit would fall in a gravityStep.
  • dropNuisance(count: number, rng: Rng, ceiling = -this.hidden): { id: number; x: number; y: number; rows: number }[] — Drops count nuisance into the well by the Puyo rules: whole rows of cols first, the remainder into distinct columns chosen at random. Each nuisance stacks on its column; any that would land above row ceiling vanishes. Returns each placed cell with how far it fell from just above the buffer.
  • raiseGarbage(rows: number, hole: number): boolean — Pushes the stack up by rows and fills the bottom with garbage rows holed at hole. Returns true if anything was pushed out of the top of the buffer (a top-out).

Types

ActiveView

interface ActiveView

The falling piece as the renderer sees it.

  • kind: string — Piece kind.
  • rot: number — Rotation state.
  • x: number — Origin column.
  • y: number — Origin row.
  • sub: number — Sub-row progress towards the next row (0..1), for smooth drawing of half-row systems.
  • blocks: { x: number; y: number; color: number; link?: PieceBlock['link'] }[] — Absolute blocks.
  • magic: boolean — Magic column (Columns).
  • serial: number — Unique per piece; changes when a new piece appears (snap sprites on change).
  • grounded: boolean — True while resting on the stack.
  • lockProgress: number — Lock-delay progress 0..1 (for a lock flash).

Cell

interface Cell

A locked cell. id is stable for the cell's lifetime (it survives falls), so a renderer can key sprites by it.

  • id: number — Stable identity for this cell while it exists.
  • color: number — Colour index (-1 for colourless cells such as nuisance).
  • kind: CellKind — What the cell is.
  • link?: Link — The capsule half this one is still joined to.

CellKind

type CellKind = 'block' | 'virus' | 'nuisance' | 'garbage'

What a locked cell is. Viruses never fall; nuisance never matches on its own; garbage is Tetris filler.

ChainEvent

interface ChainEvent

One step of a cascade in a matching game (Columns, Dr. Mario, Puyo).

  • type: 'chain' — Event kind.
  • chain: number — Chain step, 1 for the first clear of a move.
  • cleared: number — Coloured cells cleared in this step (nuisance excluded).
  • colors: number — Distinct colours cleared in this step.
  • groups: number[] — Size of each group that popped (flood matching) or each colour set (line matching).
  • linesOfN: number — Lines-of-N counted in this step (line matching: a run of four is two lines of three).
  • viruses: number — Viruses cleared in this step.
  • virusesBefore: number — Viruses already cleared earlier in this move.
  • magic: boolean — True when the step was a Columns magic-jewel clear.
  • level: number — Current level.
  • speed: number — Speed setting (Dr. Mario: 0 low, 1 med, 2 hi).

ColumnsOptions

interface ColumnsOptions

Options for columnsArcade.

  • difficulty?: FallerDifficulty — Difficulty. Arcade: DIP Normal (35 jewels a level), full speed table. Normal: DIP Easy (40), speed capped at level 9. Easy: DIP Easiest (50), capped at level 5.
  • seed?: number — Seed.
  • startLevel?: number — Start level: 0, 5 (+20,000) or 10 (+50,000) as the arcade offers; any other level starts with no bonus.
  • jewelsPerLevel?: number — Jewels per level (overrides the difficulty's DIP setting).
  • colors?: number — Jewel colours. Default 6.
  • length?: number — Column length. Default 3.
  • match?: number — Match length. Default 3.
  • diagonals?: boolean — Diagonal matches. Default true.
  • magic?: boolean — Magic jewel. Default true.
  • cols?: number — Well width. Default 6.
  • rows?: number — Visible well height. Default 13.

CounterBatch

interface CounterBatch

Counter gems waiting to fall on this board, with the pattern of the board that sent them.

  • count: number — How many.
  • rows: string[] — Pattern rows (top to bottom).
  • short: boolean — Some were offset: these start at the shorter countdown.

CrashDamage

interface CrashDamage

How breaks turn into counter gems. Every value is a counter-gem value before the send rate.

  • normal: number — Per normal gem broken.
  • crash: number — Per crash gem broken.
  • powerCell: number — Per cell of a power gem broken.
  • counter: number — Per counter gem destroyed next to a break.
  • chain: (step: number) => number — Multiplier for chain step n (1 = the first break).
  • diamond: number — Multiplier on the diamond's own break (the chain it sets off is not reduced).

CrashGemEvent

type CrashGemEvent = | { type: 'ready' } | { type: 'spawn'; serial: number } | { type: 'move'; dx: number } | { type: 'rotate'; dir: number } | { type: 'land' } | { type: 'lock'; cells: XY[] } | { type: 'power'; gem: PowerGem } | { type: 'crash'; chain: number; cells: XY[]; gems: number; power: number; counters: number; diamond: boolean; attack: number; points: number } | { type: 'diamond'; color: number; cleared: number } | { type: 'tech' } | { type: 'counterTurn'; cells: XY[] } | { type: 'counters'; count: number } | { type: 'moveEnd'; chains: number; attack: number; gems: number } | { type: 'gameOver'; reason: string }

Events reported by CrashGemGame.drain().

CrashGemPhase

type CrashGemPhase = 'piece' | 'fall' | 'flash' | 'counter' | 'gameOver'

Resolution sub-phase.

CrashGemPreview

interface CrashGemPreview

What a placement would do, from CrashGemGame.preview.

  • ok: boolean — The placement fits.
  • attack: number — Counter gems it would send.
  • chains: number — Longest chain.
  • gems: number — Gems it would break.
  • over: boolean — It would end the round.
  • field: (Cell | null)[] — The resulting board (row-major from the top of the buffer).

CrashGemRules

interface CrashGemRules

A complete crash-gem ruleset. Build one with puzzleFighter and override any field.

  • name: string — Name, for reports.
  • cols: number — Board width.
  • rows: number — Visible board height.
  • hiddenRows: number — Rows above the board that hold gems (the spawn rows).
  • spawnColumn: number — Column the pair appears in (its axis gem; the original: the fourth column). Blocking it ends the round.
  • colors: number — Gem colours (2..4 with the pattern letters; more colours need patterns that use them).
  • crashChance: number — Chance that a dealt gem is a crash gem.
  • diamondEvery: number — A diamond replaces one gem of every Nth pair (0 = never).
  • dealLength: number — Pairs generated before the deal repeats.
  • speed: number — Drop speed 1..5 (the original's VS handicap: five gem drop rates).
  • powerMin: number — Smallest power gem (width and height).
  • counterTimer: number — Counter gem countdown.
  • offsetTimer: number — Countdown of counter gems that still fall after some were offset.
  • remainder: 'random' | 'left' — Columns that take a partial row of counter gems: chosen at random or filled from the left.
  • pattern: DropPattern — This board's own drop pattern (what it sends).
  • damage: CrashDamage — Damage model.
  • timing: CrashGemTiming — Timings.
  • sequence?: readonly PieceSpec[] — An authored drop order (puzzles, tests), dealt in order and repeated; replaces the generated deal.
  • seed: number — Seed for the deal (shared by both boards for an identical drop order) and the board's own randomness.

CrashGemSnapshot

interface CrashGemSnapshot

A whole crash-gem board state: the FallerGame snapshot plus the resolution state.

  • faller: FallerSnapshot — The pair engine.
  • state: unknown — Resolution state (plain data).

CrashGemTiming

interface CrashGemTiming

Frame timings of a crash-gem board (60 Hz frames).

  • gravity: readonly number[] — Frames per row of the falling pair at drop speeds 1..5 (index 0 = speed 1).
  • softDrop: number — Soft drop in rows per frame.
  • das: number — Delayed auto shift and auto repeat rate.
  • arr: number — Auto repeat rate.
  • lockDelay: number — Frames a landed pair waits before locking (soft drop locks at once).
  • are: number — Entry delay before the next pair.
  • ready: number — READY pause.
  • flash: number — Frames broken gems flash before they go.
  • fall: number — Frames per row when gems fall after a break.
  • counterFall: number — Frames per row for falling counter gems.

DealMemory

interface DealMemory

A dealer's memory (carried by snapshots).

  • bag: string[] — Remaining bag (7-bag).
  • last: string | null — The last kind dealt (NES reroll).
  • index: number — Position in a sequence or loop.
  • table: PieceSpec[] — The generated loop table (Dr. Mario).

DealRule

type DealRule = | { kind: 'bag'; pieces: readonly string[] } | { kind: 'nes'; pieces: readonly string[] } | { kind: 'uniform'; pieces: readonly string[] } | { kind: 'colors'; piece: string; blocks: number; palette: readonly number[] } | { kind: 'loop'; piece: string; blocks: number; palette: readonly number[]; length: number } | { kind: 'sequence'; items: readonly PieceSpec[]; then?: DealRule }

How pieces are dealt.

DrMarioOptions

interface DrMarioOptions

Options for drMario.

  • difficulty?: FallerDifficulty — Difficulty. Arcade: up to 49 speed-ups a level. Normal: up to 20. Easy: up to 10.
  • seed?: number — Seed.
  • level?: number — Virus level 0..20 (and beyond, at 84 viruses). Default 0.
  • speed?: 0 | 1 | 2 — Speed: 0 LOW, 1 MED, 2 HI. Default 1.
  • virusCount?: (level: number) => number — Viruses for a level (default 4 x (level + 1), at most 84).
  • colors?: number — Colours. Default 3.
  • match?: number — Match length. Default 4.
  • cols?: number — Bottle width. Default 8.
  • rows?: number — Bottle height. Default 16.

DropPattern

interface DropPattern

A counter-gem drop pattern: the colours the attacker's counter gems take, as the opponent sees them (rows top to bottom; letters R G B Y; the pattern repeats upward and across). Row k of a drop (k = 0 at the bottom) takes the pattern's k-th row from the bottom, so a small attack always uses the bottom row.

  • name: string — Display name.
  • rows: readonly string[] — Rows, top to bottom. Each row is a string of colour letters.
  • sendRate?: (raw: number) => number — Counter gems actually sent for raw counter gems (send-rate handicap). Default: all of them.
  • source?: string — Where the pattern comes from.

FallerAction

type FallerAction = 'left' | 'right' | 'softDrop' | 'hardDrop' | 'rotateCW' | 'rotateCCW' | 'rotate180' | 'hold'

The input actions.

FallerApi

type FallerApi = typeof kit

game.faller — every function and type this pack carries, as one namespace.

FallerDifficulty

type FallerDifficulty = 'easy' | 'normal' | 'arcade'

Easy / Normal / Arcade. Arcade is the original tables; Normal keeps the original handling and the original early speeds but caps the top speed at a human-playable level; Easy caps it lower (per preset, documented).

FallerEvent

type FallerEvent = | { type: 'ready' } | { type: 'spawn'; kind: string; serial: number } | { type: 'move'; dx: number } | { type: 'rotate'; dir: number; kick: number } | { type: 'land' } | { type: 'hold'; kind: string } | { type: 'lock'; serial: number; cells: XY[]; tspin: TSpin } | { type: 'lines'; lines: number; rows: number[]; tspin: TSpin; backToBack: boolean; combo: number; perfectClear: boolean; points: number; attack: number } | { type: 'chain'; chain: number; cells: XY[]; cleared: number; colors: number; viruses: number; magic: boolean; points: number; attack: number } | { type: 'moveEnd'; chains: number; points: number; attack: number; viruses: number } | { type: 'score'; points: number; reason: 'softDrop' | 'hardDrop' | 'pushDown' | 'lines' | 'chain' } | { type: 'level'; level: number } | { type: 'speed'; speedIndex: number } | { type: 'allClear' } | { type: 'magic'; color: number; cleared: number } | { type: 'nuisance'; count: number } | { type: 'garbage'; rows: number } | { type: 'stageClear'; level: number } | { type: 'complete' } | { type: 'gameOver'; reason: string }

Events reported by drain(), in order.

FallerInput

interface FallerInput

One frame of player input. Held states; edges are derived from the previous frame unless pressed is given.

  • left?: boolean — Shift left held.
  • right?: boolean — Shift right held.
  • softDrop?: boolean — Soft drop held.
  • hardDrop?: boolean — Hard drop held (acts on the press).
  • rotateCW?: boolean — Rotate clockwise held (acts on the press).
  • rotateCCW?: boolean — Rotate counter-clockwise held (acts on the press).
  • rotate180?: boolean — Rotate 180 degrees held (acts on the press; only when the ruleset allows it).
  • hold?: boolean — Hold held (acts on the press).
  • pressed?: Partial<Record<FallerAction, boolean>> — Explicit press edges for this frame (for presses shorter than a frame). Merged with derived edges.

FallerPhase

type FallerPhase = 'ready' | 'are' | 'active' | 'clear' | 'fall' | 'stageClear' | 'gameOver' | 'complete'

The phase a game is in.

FallerRules

interface FallerRules

A complete ruleset. Build one with a preset (tetrisGuideline, tetrisNes, columnsArcade, drMario, puyoTsu) and override any field.

  • name: string — Name, for reports.
  • cols: number — Well width.
  • rows: number — Visible well height.
  • hiddenRows: number — Buffer rows above the well.
  • rotation: RotationSystem — Pieces and rotation.
  • deal: DealRule — Randomiser.
  • previews: number — Pieces shown ahead.
  • hold: boolean — Hold slot available.
  • hardDrop: boolean — Hard drop available.
  • rotate180: boolean — 180-degree rotation input available.
  • initialActions: boolean — Initial rotation/hold: a rotate or hold pressed during the entry delay applies as the piece appears.
  • match: MatchRule — Matching rule.
  • matchMinY?: number — Rows above this one never take part in matches (Puyo's hidden 13th row). Default: every row matches.
  • vanishAboveY?: number — Cells that come to rest above this row vanish (Puyo's 14th row).
  • cascade: 'rows' | 'cells' | 'linked' — Gravity after clears: 'rows' collapses cleared rows (Tetris), 'cells' drops each cell, 'linked' keeps capsule halves joined.
  • timing: FallerTiming — Timings.
  • scoring: ScoringFn — Scoring rule.
  • attack?: (e: LinesEvent) => number — Attack sent for a rows clear (versus); omitted = 0.
  • progression: ProgressionRule — Level progression.
  • startLevel: number — Starting level.
  • startScore?: number — Starting score (Columns start-level bonus).
  • speed?: number — Speed setting passed to scoring (Dr. Mario 0/1/2).
  • startSpeedIndex?: number — Starting speed index (Dr. Mario: LOW 15, MED 25, HI 31).
  • topOut: TopOutRule — Top-out rules.
  • viruses?: VirusRule — Viruses (Dr. Mario).
  • magic?: MagicRule — Magic jewel (Columns).
  • nuisance?: NuisanceRule — Nuisance (Puyo).
  • objective: Objective — Victory condition.
  • scoreCap?: number — Score cap (NES: 999999).
  • layout?: readonly string[] — Authored starting stack: rows of characters, bottom-aligned; '.' empty, digits are colours, 'n' nuisance, 'g' garbage, and letters map through layoutColors.
  • layoutColors?: Record<string, number> — Letter-to-colour map for layout (e.g. { R: 0, G: 1, B: 2, Y: 3, P: 4 }).
  • seed: number — Seed.

FallerSnapshot

interface FallerSnapshot

The whole mutable state of a game: plain data, so a snapshot is a deep copy.

  • frame: number — Frame counter.
  • phase: FallerPhase — Phase.
  • timer: number — Frames left in the phase.
  • timerTotal: number — Phase length.
  • cells: (Cell | null)[] — Well cells.
  • nextId: number — Next cell id.
  • rng: RngState — Where the well's stream stands (viruses, nuisance columns, garbage holes): a kernel Rng snapshot.
  • dealRng: RngState — Where the piece deal's stream stands (kept separate so two games on one seed get the same pieces).
  • dealer: DealMemory — Dealer memory.
  • queue: PieceSpec[] — Upcoming pieces.
  • held: PieceSpec | null — Held piece.
  • holdUsed: boolean — Hold used since the last lock.
  • active: Active | null — Falling piece.
  • serial: number — Piece serial counter.
  • acc: number — Gravity accumulator.
  • lockTimer: number — Lock timer.
  • lockResets: number — Lock resets used.
  • lowestY: number — Lowest row the piece has reached.
  • landed: boolean — The piece has touched the stack at least once.
  • lastRotate: boolean — The last successful action was a rotation.
  • lastKick: number — Kick test index of the last rotation.
  • rotMemory: RotationMemory — Rotation memory (Puyo quick turn).
  • prev: Record<FallerAction, boolean> — Held inputs last frame.
  • dasDir: number — Auto-repeat direction.
  • dasCounter: number — Auto-repeat counter.
  • arrCounter: number — ARR counter.
  • downArmed: boolean — NES soft drop armed by a fresh press.
  • downCounter: number — NES soft-drop counter.
  • pushDown: number — NES push-down rows.
  • bufferedRotate: number — Buffered initial rotation (0 none).
  • bufferedHold: boolean — Buffered initial hold.
  • score: number — Score.
  • level: number — Level.
  • lines: number — Lines cleared (rows games).
  • cleared: number — Cells cleared (matching games: jewels, puyos).
  • pieces: number — Pieces locked.
  • dealtThisLevel: number — Pieces dealt into play this level (Dr. Mario speed-ups).
  • speedIndex: number — Speed index.
  • speedUps: number — Speed-ups this level.
  • combo: number — Combo counter (-1 = none).
  • backToBack: boolean — Back-to-back armed.
  • chain: number — Current chain step in this move.
  • bestChain: number — Best chain this game.
  • movePoints: number — Points this move.
  • moveAttack: number — Attack this move.
  • moveViruses: number — Viruses cleared this move.
  • lastLines: number — Lines cleared by the last piece.
  • lockRow: number — Row of the last lock.
  • clearing: { cells: XY[]; rows: number[]; magic: boolean } | null — Cells being cleared.
  • falling: [number, number][] | null — Cells falling: id -> rows.
  • pendingNuisance: number — Nuisance waiting to fall.
  • nuisanceCarry: number — Fractional nuisance carried between chains.
  • allClearBonus: boolean — The next chain carries the all-clear bonus.
  • nuisanceDropped: boolean — Nuisance has fallen this turn.
  • pendingGarbage: { rows: number; hole: number }[] — Garbage waiting (rows games).
  • viruses: number — Viruses left.
  • virusesStart: number — Viruses at the start of the level.
  • stats: Record<string, number> — Pieces dealt by kind (NES statistics).
  • magicCooldown: number — Pieces before a magic column may appear again.
  • targetPoints: number — Target points (Puyo; a versus layer can lower it for margin time).
  • reason: string — Why the game ended.
  • goalProgress: number — Objective progress: best value toward the goal.
  • events: FallerEvent[] — Pending events.

FallerTiming

interface FallerTiming

All the frame timings of a ruleset. Values are 60 Hz frames.

  • gravity: (ctx: TimingContext) => number — Gravity in rows per frame for the current state (1/48 = one row every 48 frames; 20 = instant).
  • softDrop: SoftDropTiming — Soft drop.
  • shift: ShiftTiming — Auto-repeat.
  • lock: LockTiming — Locking.
  • are: number | ((ctx: TimingContext) => number) — Entry delay after a lock (or after the line clear) before the next piece appears.
  • lineClear: number | ((ctx: TimingContext) => number) — Line-clear delay (rows games).
  • matchFlash: number — Frames a matched set flashes before it is removed (matching games).
  • fall: number — Frames per row of cascade gravity after a clear (and for split pieces).
  • nuisanceFall: number — Frames per row for falling nuisance.
  • spawnDrop: boolean — Guideline: a new piece drops one row as soon as it appears, if it can.
  • ready: number — Frames of the READY pause before the first piece.
  • stageClear: number — Frames of the stage-clear pause (Dr. Mario).
  • speedUp?: SpeedUpRule — Dr. Mario speed-ups: the speed index rises every every pieces dealt, at most max times per level.

FitsFn

type FitsFn = (cells: readonly XY[]) => boolean

Collision callback: true when every given cell is free and inside the well.

GemType

type GemType = 'normal' | 'crash' | 'diamond' | 'counter'

What a cell on a crash-gem board is.

GuidelineOptions

interface GuidelineOptions

Options for tetrisGuideline.

  • difficulty?: FallerDifficulty — Difficulty. Arcade: the full curve to 20G. Normal: gravity capped at the level-12 speed. Easy: capped at level 8, 1 s lock delay.
  • seed?: number — Seed.
  • startLevel?: number — Start level, 1..15. Default 1.
  • goal?: number — Lines to finish (Marathon: 150). 0 = endless. Default 150.
  • previews?: number — Next-queue length. Default 5.
  • hold?: boolean — Hold. Default true.
  • hardDrop?: boolean — Hard drop. Default true.
  • rotate180?: boolean — Allow a 180-degree rotate input (not in SRS proper). Default false.
  • das?: number — Delayed auto shift in frames. Default 10 (the Guideline's recommended 167 ms).
  • arr?: number — Auto repeat rate in frames. Default 2 (33 ms).
  • are?: number — Entry delay in frames. Default 6 (the Guideline's recommended ARE).
  • lineClear?: number — Line-clear delay in frames. The Guideline leaves it to each game; default 24 (est.).
  • lockDelay?: number — Lock delay in frames. Default 30 (0.5 s).
  • lockResets?: number — Move resets before a forced lock. Default 15.
  • perfectClear?: boolean — Score perfect clears (later Guideline games). Default false.
  • cols?: number — Well width. Default 10.
  • rows?: number — Visible well height. Default 20.

GuidelineScoringOptions

interface GuidelineScoringOptions

Options for guidelineScoring.

  • perfectClear?: boolean — Add the perfect-clear bonus used by some Guideline games (800/1200/1800/2000, 3200 for a B2B tetris). Default false.

HardDropEvent

interface HardDropEvent

Rows moved by a hard drop.

  • type: 'hardDrop' — Event kind.
  • rows: number — Rows moved.
  • level: number — Current level.

LineMatch

interface LineMatch

Line-of-N matching result: groups by colour plus the Columns "lines of three" count.

  • groups: MatchGroup[] — One group per colour, every cell that is part of a qualifying run.
  • linesOfN: number — Sum over every maximal run of (length - (n - 1)): a run of 4 counts as two lines of three.

LinesEvent

interface LinesEvent

A piece locked in a rows game (Tetris), with or without lines.

  • type: 'lines' — Event kind.
  • lines: number — Lines cleared by this piece.
  • tspin: TSpin — T-spin classification of the lock.
  • difficult: boolean — True when this clear is "difficult" (four or more lines, or a T-spin that cleared lines).
  • backToBack: boolean — True when the previous line clear was also difficult (back-to-back applies).
  • combo: number — Consecutive line-clearing pieces minus one (0 for the first, -1 when this piece cleared nothing).
  • perfectClear: boolean — True when the clear left the well empty.
  • levelBefore: number — Level before this clear.
  • levelAfter: number — Level after this clear's lines are counted.
type Link = 'left' | 'right' | 'up' | 'down'

Which neighbour a capsule half is joined to (Dr. Mario), or undefined for a free cell.

LockTiming

interface LockTiming

Locking rule.

  • mode: 'move' | 'step' | 'gravity' — 'move': lock delay that moves and rotations reset, up to resets times (restored on reaching a new lowest row; Guideline extended placement). 'step': lock delay reset only by falling a row. 'gravity': lock as soon as a gravity or soft-drop step is blocked (NES Tetris, Dr. Mario).
  • delay: number — Frames a grounded piece waits before locking.
  • resets: number — Maximum resets: 'move' mode counts moves and rotations on the ground; 'step' mode counts floor kicks that lift a landed piece (which would otherwise allow endless stalling). 0 = unlimited in 'step' mode.
  • softDropLocks?: boolean — Holding soft drop on a grounded piece locks it at once (Puyo, Columns).

MagicRule

interface MagicRule

Columns' magic jewel.

  • dangerRows?: number — Offer a magic column when the stack has reached within this many rows of the top.
  • every?: number — Also offer one every N pieces (0 = never).
  • cooldown: number — Pieces before another can be offered.
  • minLevel?: number — Lowest level at which it can appear.

MatchGroup

interface MatchGroup

A matched set of cells found after a lock.

  • cells: XY[] — Cells in the group.
  • color: number — Their colour (-1 for full rows).

MatchRule

type MatchRule = | { kind: 'rows' } | { kind: 'lines'; length: number; diagonals: boolean } | { kind: 'groups'; size: number }

What counts as a match after a lock.

NesOptions

interface NesOptions

Options for tetrisNes.

  • difficulty?: FallerDifficulty — Difficulty. Arcade: the full table to the 1-frame level 29. Normal: never faster than level 18 (3 frames a row). Easy: never faster than level 9 (6).
  • seed?: number — Seed.
  • startLevel?: number — Start level 0..19 (the NES menu offers 0-9, and 10-19 with A held). Default 0.
  • cols?: number — Well width. Default 10.
  • rows?: number — Visible well height. Default 20.
  • preview?: boolean — Show the next piece (the NES SELECT toggle). Default true.

NuisanceRule

interface NuisanceRule

Puyo nuisance rules (used by the versus layer).

  • maxPerDrop: number — Most nuisance that falls after one piece (Tsu: 30, five rows of six).
  • targetPoints: number — Score per nuisance sent (Tsu: 70).
  • allClearBonus: number — Extra nuisance sent on the chain after an all clear (Tsu: 30).

Objective

type Objective = | { kind: 'endless' } | { kind: 'lines'; lines: number } | { kind: 'chain'; chain: number; pieces?: number } | { kind: 'allClear'; pieces?: number } | { kind: 'clearColor'; color: number; pieces?: number } | { kind: 'score'; score: number; pieces?: number }

A goal that ends the game in victory.

PieceBlock

interface PieceBlock

One block of a piece, relative to the piece origin.

  • dx: number — Column offset from the piece origin.
  • dy: number — Row offset from the piece origin (y down).
  • color: number — Colour index.
  • link?: Link — The partner this block is joined to, for pieces whose halves stay joined after locking (capsules).

PiecePose

interface PiecePose

A piece's position and orientation.

  • rot: number — Rotation state index.
  • x: number — Origin column.
  • y: number — Origin row.
  • colors: number[] — Block colours in order (cycling systems change these instead of the shape).

PieceSpec

interface PieceSpec

What the randomiser deals: a piece kind and its colours (one per block for coloured pieces).

  • kind: string — The piece kind, e.g. 'T', 'pair', 'capsule', 'column'.
  • colors: number[] — Colours, one per block (or one shared colour for tetrominoes).
  • magic?: boolean — Columns' magic jewel column: clears every jewel of the colour it lands on.

PlacementPreview

interface PlacementPreview

What placing a piece would do, from FallerGame.preview.

  • ok: boolean — The placement fits.
  • points: number — Points it would score (drops, clears, chains).
  • chains: number — Longest chain it would set off.
  • attack: number — Attack it would send (nuisance or garbage lines).
  • lines: number — Lines it would clear (rows games).
  • over: boolean — It would end the game.
  • field: (Cell | null)[] — The resulting well cells (row-major from the top of the buffer).

PowerGem

interface PowerGem

A power gem: a fused rectangle of one colour.

  • id: number — Power gem id (stable while it exists; changes when it grows).
  • color: number — Colour.
  • x: number — Left column.
  • y: number — Top row.
  • w: number — Width in cells.
  • h: number — Height in cells.

ProgressionRule

type ProgressionRule = | { kind: 'lines'; perLevel: number; base: number; maxLevel?: number; nesFirst?: boolean } | { kind: 'cleared'; perLevel: number; maxLevel?: number } | { kind: 'pieces'; perLevel: number; maxLevel?: number } | { kind: 'viruses'; maxLevel?: number } | { kind: 'none' }

How the level advances.

PushDownEvent

interface PushDownEvent

NES push-down points: rows of the final continuous soft drop, added when the piece locks.

  • type: 'pushDown' — Event kind.
  • rows: number — Rows moved.
  • level: number — Current level.

PuyoMission

interface PuyoMission

A Puyo mission (a "nazo puyo" style puzzle): a starting stack, the pairs to play, and a goal.

  • name: string — Short name.
  • text: string — The goal, shown to the player.
  • layout: readonly string[] — Starting stack, bottom-aligned rows of R G B Y P (and n for nuisance).
  • pairs: readonly string[] — The pairs, as two letters each: axis then child (e.g. 'RG').
  • goal: Objective — The objective (with a piece limit).

PuyoOptions

interface PuyoOptions

Options for puyoTsu.

  • difficulty?: FallerDifficulty — Difficulty. Arcade: Tsu Normal's four colours, full speed curve. Normal: four colours, speed capped at level 8. Easy: three colours (Tsu Easy), capped at level 4.
  • seed?: number — Seed.
  • colors?: number — Colours dealt (3 Easy, 4 Normal, 5 Hard in Tsu).
  • popSize?: number — Group size that pops. Default 4.
  • piecesPerLevel?: number — Endless: pieces per speed level. Default 30.
  • mission?: number | PuyoMission — A mission instead of endless play (index into PUYO_MISSIONS, or your own).
  • cols?: number — Well width. Default 6.
  • rows?: number — Visible well height. Default 12.

PuzzleFighterOptions

interface PuzzleFighterOptions

Options for puzzleFighter.

  • difficulty?: 'easy' | 'normal' | 'arcade' — Easy / Normal / Arcade. Arcade: the given drop speed. Normal: one slower (min 1). Easy: speed 1 and longer lock delay.
  • seed?: number — Seed.
  • pattern?: string | DropPattern — Drop pattern key in DROP_PATTERNS, or your own. Default 'bands'.
  • speed?: number — Drop speed 1..5. Default 3.
  • diamond?: number — Diamond damage factor: 0.5 original, 0.8 HD Remix.
  • cols?: number — Board width. Default 6.
  • rows?: number — Board height. Default 12.
  • colors?: number — Colours. Default 4.
  • crashChance?: number — Crash gem chance per gem. Default 0.125 (estimate).
  • diamondEvery?: number — Diamond every N pairs (0 = off). Default 25.
  • powerMin?: number — Smallest power gem side. Default 2.

RotationMemory

interface RotationMemory

Per-piece rotation state the engine keeps between rotations (Puyo's quick-turn flag).

  • quickTurn: boolean — Set by a blocked rotation of a wedged vertical pair; the next press turns it 180 degrees.

RotationResult

interface RotationResult extends PiecePose

The result of a successful rotation. kick is the index of the kick test used (0 = no offset).

  • kick: number — Index of the kick test that succeeded (0 = the plain rotation).

RotationSystem

interface RotationSystem

A rotation system: shapes, spawn rule and rotation resolution for a family of pieces.

  • readonly id: string — Identifier, e.g. 'srs', 'nrs', 'cycle', 'pair', 'capsule'.
  • readonly kinds: readonly string[] — The piece kinds this system defines, in their canonical order.
  • states(kind: string): number — Number of rotation states for a kind.
  • blocks(kind: string, pose: PiecePose): PieceBlock[] — The blocks of a piece at a pose.
  • spawn(spec: PieceSpec, cols: number): PiecePose — Where a piece spawns in a well cols wide.
  • rotate(kind: string, pose: PiecePose, dir: 1 | -1 | 2, fits: FitsFn, memory: RotationMemory): RotationResult | null — Resolve a rotation: dir 1 = clockwise, -1 = counter-clockwise, 2 = 180 degrees. Null when it fails.
  • spinCentre?(kind: string, pose: PiecePose): XY | null — The centre of a piece's bounding box for the 3-corner T-spin test, or null if the kind has none.

ScoringEvent

type ScoringEvent = SoftDropEvent | HardDropEvent | PushDownEvent | LinesEvent | ChainEvent

Everything a scoring function is asked to price.

ScoringFn

type ScoringFn = (event: ScoringEvent) => number

A scoring rule: points for an event (0 for events it does not score).

ShiftTiming

interface ShiftTiming

Horizontal auto-repeat. modern = DAS then ARR; nes = the NES counter (shift at 16, reload 10, charged by a wall).

  • style: 'modern' | 'nes' — Style of auto-repeat.
  • das: number — Frames before auto-repeat starts (NES: 16).
  • arr: number — Frames between repeats (0 = instantly to the wall; NES: 6).
  • chargeInDelay: boolean — Keep charging DAS while no piece is in play (ARE, line clear). NES freezes it.
  • blockedBySoftDrop?: boolean — NES: left/right do nothing while soft drop is held.

SoftDropEvent

interface SoftDropEvent

Rows moved by a soft drop (Guideline, Columns, Puyo: scored as they happen).

  • type: 'softDrop' — Event kind.
  • rows: number — Rows moved.
  • level: number — Current level.

SoftDropTiming

type SoftDropTiming = | { kind: 'factor'; factor: number; min?: number } | { kind: 'rate'; rowsPerFrame: number } | { kind: 'nes'; first: number; repeat: number }

Soft drop speed.

SpeedUpRule

interface SpeedUpRule

Speed-ups within a level (Dr. Mario).

  • every: number — Pieces dealt between speed-ups.
  • max: number — Most speed-ups per level.

TableRotationOptions

interface TableRotationOptions

Options for a table-driven rotation system (tableRotation).

  • id: string — Identifier reported by RotationSystem.id.
  • shapes: Record<string, XY[][]> — Shapes per kind, one list of cells per rotation state, in clockwise order.
  • colors?: Record<string, number> — Colour per kind (tetrominoes are one colour). Defaults to the kind's index.
  • spawnRot?: Record<string, number> — Spawn rotation per kind. Default 0.
  • spawnMode: 'box' | 'pivot' — Spawn origin. 'box' centres the bounding box (rounded left) with its top at spawnY (SRS); 'pivot' places the pivot at column floor(cols / 2), row spawnY (NES).
  • spawnY: number — Spawn row of the origin.
  • kicks?: (kind: string, from: number, to: number) => readonly (readonly [number, number])[] — Kick tests for a rotation, in y-DOWN cell offsets, tried in order. Default: only [0, 0].
  • spinCentres?: Record<string, XY> — Bounding-box centre per kind for the 3-corner T-spin test.

TimingContext

interface TimingContext

Context passed to timing functions.

  • level: number — Current level.
  • speedIndex: number — Current speed index (Dr. Mario speed-ups; otherwise 0).
  • pieces: number — Pieces locked so far.
  • frame: number — Frame counter.
  • lockRow: number — Row of the last lock (piece origin, 0 = top visible row).
  • rows: number — Visible rows in the well.
  • lines: number — Lines cleared by the last piece (rows games).

TopOutRule

interface TopOutRule

Top-out rules.

  • blockOut?: boolean — Game over when a new piece overlaps the stack (Tetris, Dr. Mario, Columns).
  • lockOut?: boolean — Game over when a piece locks entirely above the visible well (Guideline).
  • deathCells?: readonly XY[] — Game over when any of these cells is filled after a move resolves (Puyo: column 3 of the top row).
  • overflow?: boolean — Game over when anything is left above the visible well after a move resolves (Columns).

TSpin

type TSpin = 'none' | 'mini' | 'full'

How a T-spin was classified at lock (Guideline 3-corner rule with the pointing-side mini test).

VirusRule

interface VirusRule

Dr. Mario virus placement.

  • count?: (level: number) => number — Viruses for a level. Default min(84, 4 x (level + 1)).
  • maxRow?: (level: number) => number — Highest row (1 = bottom) a virus may occupy at a level (NES table: 10, then 11/12/13 from level 15).
  • palette: readonly number[] — Virus colours.
  • spacing?: boolean — The NES distance-two colour rule (no virus shares a colour with the cells two away in each direction).

XY

interface XY

A grid coordinate: x is the column (0 = left), y is the row (0 = top visible row; hidden rows are negative).

  • x: number — Column, 0 = left.
  • y: number — Row, 0 = the top visible row; rows above the well are negative.