The pack
hexaSort
function hexaSort(): Pack<{ hexaSort: typeof kit; }>
Compose the hexa sort rules, hex board helpers and hint player under game.hexaSort.
Helpers
createHexaSort
function createHexaSort(config: HexaSortConfig): HexaSort
Make a hexa sort level from a config. Throws, naming the field, on a config it cannot play.
HEX_DIRECTIONS
const HEX_DIRECTIONS: readonly HexCell[]
The six axial neighbour offsets, clockwise from the right on a pointy layout.
hexagonCells
function hexagonCells(radius: number): HexCell[]
Every cell within radius steps (0 to 12) of the centre: a hexagonal board of 3r(r+1)+1 cells.
hexaHint
function hexaHint(game: HexaSort): HexaHint | null
The placement worth making now: the most tiles cleared, then the most tiles gathered, then the emptiest board, then the placement whose top colour matches a stack nearby. A one-move greedy look, not a solver; pure.
hexaSortLevel
function hexaSortLevel(n: number, options: HexaLevelOptions): HexaSortConfig
The n-th level (1 to 999) of a ladder: the board grows, colours are added, the goal rises, and from level 4 some cells open only as tiles are cleared and a few stacks start on the board. A preset of the configuration, not a rule: every field it sets is the game's to change.
hexAt
function hexAt(x: number, y: number, size: number, orientation: HexOrientation = 'pointy'): HexCell
The cell under a point, for hexes of circumradius size: the inverse of hexPoint, rounded to the nearest cell.
hexDistance
function hexDistance(a: HexCell, b: HexCell): number
Steps between two cells.
hexFromKey
function hexFromKey(key: string): HexCell
The cell a key names.
hexKey
const hexKey = (q: number, r: number): string=> …
The text key a cell is stored under: "q,r".
hexNeighbours
function hexNeighbours(q: number, r: number): HexCell[]
The six neighbours of a cell, in HEX_DIRECTIONS order (whether or not they are on a board).
hexPoint
function hexPoint(q: number, r: number, size: number, orientation: HexOrientation = 'pointy'): { x: number; y: number }
The centre of a cell in world units for hexes of circumradius size, with the cell 0,0 at the origin.
playHexa
function playHexa(config: HexaSortConfig, options: { maxMoves?: number } = {}): HexaPlay
Play a level with hexaHint until it is won or lost, or maxMoves (default 2000) stacks are placed.
rectangleCells
function rectangleCells(width: number, height: number): HexCell[]
A rectangular board width × height (1 to 20 each) for a pointy layout, every other row shifted half a cell
right (odd rows), in axial coordinates.
Classes
HexaSort
class HexaSort
A hexa sort level in play: place dealt stacks of hex tiles on the board; touching stacks with the same top colour
gather those tiles onto one stack, chain on as new tops are uncovered, and a top run of clearAt tiles clears.
Turn based and seeded, so a placement resolves at once into a list of events to animate. Get one from
createHexaSort.
constructor(config: HexaSortConfig)get config(): HexaSortConfig— A copy of the config.get state(): HexaSortState— A copy of the state: everything a save needs.get status(): HexaSortState['status']—'playing','won'or'lost'.get hand(): (string[] | null)[]— The hand: a copy of each slot's stack, bottom first, or null for a used slot.get cleared(): number— Tiles cleared.get score(): number— Points.get toGo(): number | null— Tiles still to clear for the goal, or null when endless.stackAt(q: number, r: number): string[] | null | undefined— The stack on a cell (bottom first), null when the cell is empty, undefined when it is not on the board.lockedAt(q: number, r: number): number— Tiles cleared before a shut cell opens, or 0 when it is open or not on the board.openCells(): HexCell[]— The open, empty cells a stack could go on now.refusal(slot: number, q: number, r: number): HexaRefusal | null— Why placing hand slotslotonq,rwould be refused, or null if it would be accepted.place(slot: number, q: number, r: number): HexaResult— Place hand slotsloton cellq,rand resolve every merge and clear it sets off.preview(slot: number, q: number, r: number): { events: HexaEvent[]; state: HexaSortState } | null— Whatplacewould do, without doing it: the events and the state after. Null when it would be refused.hammer(q: number, r: number): HexaResult— Booster: take the stack off a cell, tiles and all.reroll(): HexaResult— Booster: deal a whole new hand.continueRun(count = 3): HexaResult— After a loss: take thecounttallest stacks off the board (1 to 20, default 3) and play on.restore(state: HexaSortState): void— Put back a saved state of this level. Throws on one that does not fit.
Types
HexaEvent
type HexaEvent = | { type: 'placed'; cell: string; slot: number; tiles: string[] } | { type: 'moved'; from: string; to: string; color: string; count: number } | { type: 'cleared'; cell: string; color: string; count: number; chain: number; points: number } | { type: 'unlocked'; cell: string } | { type: 'dealt'; slots: number[] } | { type: 'removed'; cell: string; tiles: string[] } | { type: 'won' } | { type: 'lost' }
What a placement or booster did, in order. Cells are keys ("q,r").
HexaHint
interface HexaHint
A suggested placement, from hexaHint.
slot: number— Hand slot.q: number— Cell column.r: number— Cell row.clears: number— Tiles it would clear.moves: number— Tiles it would move between stacks.
HexaLevelOptions
interface HexaLevelOptions
Options for hexaSortLevel.
colors: string[]— The colours to draw from, in the order levels add them; at least 3.shape?: 'hexagon' | 'rectangle'— Board shape; default'hexagon'.seed?: number— Seed for the level's own choices (locked cells, starting stacks, deals); default the level number.
HexaLockedCell
interface HexaLockedCell extends HexCell
A cell that opens once enough tiles are cleared.
unlockAt: number— Tiles cleared before it opens, 1 to 100000.
HexaPlay
interface HexaPlay
How a played level ended, from playHexa.
won: boolean— Whether the goal was met.moves: number— Stacks placed.cleared: number— Tiles cleared.score: number— Points.
HexaRefusal
type HexaRefusal = 'not-playing' | 'bad-slot' | 'empty-slot' | 'off-board' | 'locked' | 'occupied' | 'empty-cell' | 'not-lost' | 'bad-count'
Why an action was refused.
HexaResult
type HexaResult = { valid: true; reason?: undefined; events: HexaEvent[] } | { valid: false; reason: HexaRefusal; events: [] }
The answer to an action: done with its events, or refused with a reason and nothing changed.
HexaSortConfig
interface HexaSortConfig
A hexa sort level.
cells: HexCell[]— The board's cells, 1 to 400, distinct.hexagonCellsandrectangleCellsmake the usual shapes.locked?: HexaLockedCell[]— Cells that stay shut until enough tiles are cleared. Each must be one ofcells.stacks?: HexaStartStack[]— Stacks on the board at the start, on distinct open cells.colors: string[]— The colours deals draw from, CSS colour strings, 1 to 12, distinct.schedule?: { at: number; colors: number }[]— How many ofcolorsdeals use, rising with tiles cleared:{ at, colors }steps; default all from the start.hand?: number— Stacks offered at once, 1 to 5; default 3.refill?: 'all' | 'each'—'all'(default): a new hand once every stack is placed.'each': a placed stack is replaced at once.height?: [number, number]— Tiles in a dealt stack,[min, max]from 1 to 20; default [2, 6].runs?: [number, number]— Colour runs in a dealt stack,[min, max]from 1 to 6; default [1, 3].clearAt?: number— A top run of one colour this long clears, 2 to 40; default 10.target?: HexaTarget— Which stack gathers matching tiles; default'most'(the one with the longest top run, the placed one on a tie).goal?: number— Tiles to clear to win, 0 to 100000; default 0, endless.seed?: number— Seed for the deals, 0 to 4294967295; default 1.
HexaSortState
interface HexaSortState
Everything that changes in play; a save holds exactly this.
stacks: Record<string, string[]>— Stacks on the board by cell key ("q,r"), bottom first.hand: (string[] | null)[]— The hand: a stack per slot, bottom first, or null for a used slot.locked: Record<string, number>— Cells still shut, by key, with the tiles cleared they wait for.cleared: number— Tiles cleared.score: number— Points: each clear scores its tiles times its place in the placement's chain of clears.moves: number— Stacks placed.bestChain: number— Longest chain of clears from one placement.status: 'playing' | 'won' | 'lost'—'playing','won'(the goal is met) or'lost'(no open cell for the next stack).rng: number[]— The deals' random stream.
HexaStartStack
interface HexaStartStack extends HexCell
A stack already on the board when the level starts.
tiles: string[]— Its tiles, bottom first, each a colour; 1 to 40.
HexaTarget
type HexaTarget = 'most' | 'placed'
Which stack gathers the tiles when stacks with the same top colour touch.
HexCell
interface HexCell
A hex cell in axial coordinates: q across, r down the diagonal rows.
q: number— Axial column.r: number— Axial row.
HexOrientation
type HexOrientation = 'pointy' | 'flat'
How hexes are drawn: 'pointy' (a point at the top, rows run across) or 'flat' (a flat top, columns run down).


