The pack
placeMerge
function placeMerge(): Pack<{ placeMerge: PlaceMergeApi }>
The place-merge pack. Pure logic, so it needs no backend and runs headless.
Helpers
createPlaceMerge
function createPlaceMerge(cfg: PlaceMergeConfig): PlaceMerge
Creates a placement-merge board.
TRIPLE_TOWN_CHAINS
const TRIPLE_TOWN_CHAINS: readonly MergeChain[]
Triple Town's three chains: grass to the triple castle, tombstone to the treasure chest, rock to the mountain, with their points per tier.
TRIPLE_TOWN_DEAL
const TRIPLE_TOWN_DEAL: readonly DealEntry[]
A deal like Triple Town's: mostly grass, some bushes and trees, a bear now and then, rare crystals and bombs.
Classes
PlaceMerge
class PlaceMerge
A Triple Town board.
readonly cols: number— Columns.readonly rows: number— Rows.readonly chains: readonly MergeChain[]— The chains in force.gameOver: boolean— True once the board is full.score: number— Score.turns: number— Turns played.constructor(cfg: PlaceMergeConfig)item(x: number, y: number): Item | null— The item at (x, y), or null.get deal(): Item— The item to place next.get storage(): Item | null— The item in the storage slot, or null.get topTier(): number— The highest block tier on the board.place(at: XY): PlaceResult— Place the deal. Blocks, walkers, wildcards and generators need an empty cell; a bomb needs an occupied one.store(): void— Swap the deal with the storage slot (an empty slot takes the deal, and a new one is dealt).findPlaces(): { at: XY; merges: number; tier: number }[]— Every legal placement for the deal, best first: the biggest tier made, then the most merges.autoplay(maxTurns = 500): PlaceResult[]— Play the best placement each turn until the board fills ormaxTurnspass.snapshot(): PlaceMergeSnapshot— The whole state as plain data.restore(snap: PlaceMergeSnapshot): void— Return to asnapshot()of a board with the same size and rules.debugGrid(): string— The board as text, for tests:.empty,Bwalker,Ggenerator, otherwise the chain letter (a, b, c…) and tier.
Types
DealEntry
type DealEntry = | { kind: 'block'; type: number; tier: number; weight: number } | { kind: 'walker' | 'wildcard' | 'bomb'; weight: number } | { kind: 'generator'; type: number; tier: number; every: number; charges: number; weight: number }
One entry of the deal table.
Item
interface Item
A placed or dealt item.
id: number— Unique id for the item's life.kind: ItemKind— What it is.type: number— A block's chain (an index into the config'schains); a generator's output chain; -1 otherwise.tier: number— A block's tier in its chain; a generator's output tier; 0 otherwise.charges?: number— A generator: drops left.every?: number— A generator: turns between drops.wait?: number— A generator: turns until the next drop.
ItemKind
type ItemKind = 'block' | 'walker' | 'wildcard' | 'bomb' | 'generator'
What an item is. Only blocks merge.
MergeChain
interface MergeChain
One merge chain: how many tiers it has and what each scores when made.
name?: string— Name, for presenters ('grass').tiers: number— Tiers in the chain: a merge into the last tier makes it, and it never merges again.score?: readonly number[]— Points for making each tier, by tier. Missing entries use the last one.
PlaceMergeApi
type PlaceMergeApi = typeof kit
game.placeMerge — every function, class and table this pack carries, as one namespace.
PlaceMergeConfig
interface PlaceMergeConfig
Configures createPlaceMerge.
cols: number— Columns.rows: number— Rows.seed?: number— Seed for the deal and the walkers. Default 1.chains?: readonly MergeChain[]— The merge chains, by type. Default:TRIPLE_TOWN_CHAINS.deal?: readonly DealEntry[]— The deal table. Default:TRIPLE_TOWN_DEAL.tombstone?: { type: number; tier: number }— What a trapped walker becomes. Default: tier 0 of chain 1 (the tombstone chain).rock?: { type: number; tier: number }— What a wildcard becomes when nothing would merge. Default: tier 0 of chain 2 (a rock).overMerge?: number— Score multiplier for a merge of four or more. Default 2.start?: readonly [number, number, Omit<Item, 'id'>][]— Items on the board at the start, as[x, y, item].
PlaceMergeSnapshot
interface PlaceMergeSnapshot
The plain-data state of a board.
grid: (Item | null)[]— Cells, row by row.s: { score: number; turns: number; gameOver: boolean; deal: Item; stored: Item | null; nextId: number; rng: RngState }— The other state.
PlaceResult
interface PlaceResult
The result of one place().
valid: boolean— False when the placement was refused: nothing changed.steps: PlaceStep[]— The turn's steps, in order.merges: number— Merges the turn caused, cascades and trapped walkers included.points: number— Points the turn scored.topTier: number— The highest tier on the board after the turn.gameOver: boolean— True once the board is full.
PlaceStep
type PlaceStep = | { type: 'place'; at: XY; item: Item } | { type: 'wildcard'; at: XY; becomes: Item } | { type: 'bomb'; at: XY; removed: Item } | { type: 'merge'; at: XY; from: XY[]; result: Item; points: number } | { type: 'walk'; moves: { id: number; from: XY; to: XY }[] } | { type: 'trapped'; cells: XY[] } | { type: 'generatorSpawn'; from: XY; at: XY; item: Item } | { type: 'generatorSpent'; at: XY } | { type: 'gameOver' }
One journal step of a turn, in play order.
XY
interface XY
A cell: column x, row y, both from 0 at the top left.
x: number— Column.y: number— Row.


