The pack
chainLink
function chainLink(): Pack<{ chainLink: typeof kit; }>
Compose the chain link rules under game.chainLink.
Helpers
createChainLink
function createChainLink(config: ChainLinkConfig): ChainLink
Make a chain link board from a config. Throws, naming the field, on a config it cannot play.
dotLevel
function dotLevel(n: number, colors: readonly string[]): ChainLinkConfig
The n-th colours level (1 to 999) of a ladder: a 6 × 6 board growing to 8 × 8, more colours, goals for two or three of them and a move limit. A preset of the configuration; the game can change any field.
numberChainConfig
function numberChainConfig(seed = 1): ChainLinkConfig
A numbers board with the usual settings: 5 × 8, diagonal links, chains rounded up to the next power of two.
Classes
ChainLink
class ChainLink
A chain link board in play: trace a path through neighbouring pieces and link them. In colours mode the pieces
clear (a closed loop clears every piece of its colour); in numbers mode they merge into one number at the end of
the path. Pieces above fall into the gaps and new ones are dealt from the seed. Turn based: a link resolves at once
into events to animate. Get one from createChainLink.
constructor(config: ChainLinkConfig)get config(): ChainLinkConfig— A copy of the config.get state(): ChainLinkState— A copy of the state: everything a save needs.get status(): ChainLinkState['status']—'playing','won'or'lost'.get score(): number— Points.get movesLeft(): number | null— Moves left, or null when unlimited.get best(): number— The biggest number on the board (numbers mode), or 0.goals(): (ChainGoal & { left: number })[]— Each goal with how many are still to clear.at(x: number, y: number): number | undefined— The piece on a cell (a colour index or a number), or undefined off the board.check(path: readonly ChainCell[]): ChainCheck— Check a path without linking it: every cell on the board, each next to the last, matching by the mode's rule, none visited twice, except that in colours mode the last cell may repeat an earlier one to close a loop.link(path: readonly ChainCell[]): ChainResult— Link a path: clear or merge it, let pieces fall, deal new ones, and settle the moves, goals and a stuck board.shuffle(): ChainResult— Booster: shuffle the pieces on the board (with at least one link left afterwards when one can be made).hint(budget = 20000): ChainCell[] | null— The best link now, by a bounded search from every cell: a loop first (colours), then the longest path; for numbers, the biggest result, then the longest. Null when there is none. Pure.restore(state: ChainLinkState): void— Put back a saved state of this board. Throws on one that does not fit.
Types
ChainCell
interface ChainCell
A board cell: x column from the left, y row from the top.
x: number— Column, from 0.y: number— Row, from 0 at the top.
ChainCheck
interface ChainCheck
A path checked against the rules, from check.
ok: boolean— Whetherlinkwould take it.reason?: ChainRefusal— Why not.loop: boolean— Whether it closes a loop (colours mode): its last cell repeats an earlier one.value?: number— Numbers mode: the number it would make.
ChainEvent
type ChainEvent = | { type: 'linked'; cells: ChainCell[]; loop: boolean; kind: number } | { type: 'cleared'; cells: ChainCell[] } | { type: 'merged'; x: number; y: number; value: number } | { type: 'fell'; moves: { from: ChainCell; to: ChainCell }[] } | { type: 'spawned'; cells: (ChainCell & { value: number })[] } | { type: 'raised'; spawnMin: number; removed: ChainCell[] } | { type: 'shuffled' } | { type: 'won' } | { type: 'lost'; reason: 'moves' | 'stuck' }
What a link did, in order, for animation.
ChainGoal
interface ChainGoal
A collection goal: clear count pieces of colour index kind.
kind: number— The colour's index incolors.count: number— Pieces to clear, 1 to 9999.
ChainLinkConfig
interface ChainLinkConfig
A chain link board.
mode: ChainMode—'colors'or'numbers'.width: number— Board width, 2 to 12.height: number— Board height, 2 to 14.adjacency?: 4 | 8— Links between orthogonal neighbours only (4) or diagonals too (8); default 4 for colours and 8 for numbers.colors?: string[]— Colours mode: the piece colours, CSS colour strings, 2 to 10. Pieces store an index into this list.loops?: boolean— Colours mode: closing a loop clears every piece of the loop's colour; default true.loopRefill?: boolean— Colours mode: after a loop, new pieces in that refill are never the loop's colour; default true.moves?: number— Colours mode: moves allowed, 1 to 999; default 0, unlimited.goals?: ChainGoal[]— Colours mode: pieces to collect to win; default none, so play runs until the moves are used.merge?: ChainMerge— Numbers mode: how a chain's result follows from its sum; default'sum-up', the next power of two at or above it.spawnMin?: number— Numbers mode: the smallest number dealt, a power of two from 2; default 2. It rises as the board grows (seerise).spawnSpan?: number— Numbers mode: how many doublings above the smallest are dealt, 1 to 8; default 5.rise?: number— Numbers mode: once the biggest number is this many doublings above the smallest dealt, the smallest dealt doubles; default 11, 0 never.board?: number[]— Starting board, row-major,width × heightentries: a colour index or number per cell; default dealt from the seed.stuck?: 'shuffle' | 'lose'— When no link is possible:'shuffle'the board (default for colours) or'lose'(default for numbers).seed?: number— Seed, 0 to 4294967295; default 1.
ChainLinkState
interface ChainLinkState
Everything that changes in play; a save holds exactly this.
grid: number[]— The board, row-major: a colour index (colours) or a number (numbers).moves: number— Moves made.score: number— Points: pieces cleared (colours) or the sum of the merged results (numbers).collected: number[]— Colours mode: pieces cleared by colour index.spawnMin: number— Numbers mode: the smallest number dealt now.loops: number— Loops closed.status: 'playing' | 'won' | 'lost'—'playing','won'(every goal met) or'lost'(out of moves, or stuck withstuck: 'lose').rng: number[]— The deal's random stream.
ChainMerge
type ChainMerge = 'sum-up' | 'sum-down' | 'double'
How a number chain's result is worked out from the sum of its numbers.
ChainMode
type ChainMode = 'colors' | 'numbers'
'colors': link two or more pieces of one colour; they clear, and closing a loop clears every piece of that colour.
'numbers': link equal numbers, each next number equal to the last or double it; they merge into one number.
ChainRefusal
type ChainRefusal = 'not-playing' | 'too-short' | 'off-board' | 'not-adjacent' | 'mismatch' | 'revisit' | 'after-loop'
Why a path is not a link.
ChainResult
type ChainResult = { valid: true; reason?: undefined; events: ChainEvent[] } | { valid: false; reason: ChainRefusal; events: [] }
The answer to link: done with its events, or refused with a reason and nothing changed.


