The pack
shelfSort
function shelfSort(): Pack<{ shelfSort: typeof kit }>
Install renderer-independent puzzle rules and level generation.
Helpers
createShelfSort
function createShelfSort(config: ShelfConfig, solution: ShelfAction[] = []): ShelfSort
Create a shelf sorting puzzle and optionally install a validated route for hints.
generateShelfLayout
function generateShelfLayout(shelves: {id:string;slots:unknown[]}[], options: {seed?:number;layout?:ShelfLayout} = {}): ShelfBoardLayout
Arrange any valid set of shelf fronts deterministically, with gaps and no intersecting compartments.
generateShelfSort
function generateShelfSort(options: ShelfGenerateOptions = {}): ShelfLevel
Construct ordered stock waves, then reverse-certified scramble them. Runtime work is bounded by requested moves and shelf limits.
Classes
ShelfSort
class ShelfSort
Pure goods sorting: legal drag previews, matching, ordered hidden stock, locks, bounded hints and history.
constructor(config: ShelfConfig, solution: ShelfAction[] = [])get state(): ShelfState— Owned complete snapshot, including stock and matching progress.get solved(): boolean— All fronts and hidden stock cleared.get lost(): boolean— No legal move or move limit reached. An available move does not guarantee a solution.isUnlocked(id: string): boolean— Current lock state. Unknown shelf IDs return false.legalActions(): ShelfAction[]— Legal inter-shelf moves; same-shelf reordering is accepted by apply but omitted as an unhelpful hint.preview(action: ShelfAction): ActionResult<ShelfEvent>— Non-mutating drag/drop legality and resulting ordered events.apply(action: ShelfAction): ActionResult<ShelfEvent>— Commit one legal move and resolve clear/refill/unlock immediately.solve(maxStates = 20000): SearchResult<ShelfAction>— Use a matching certificate position when possible, otherwise run bounded search ordered towards matching groups.undo(): boolean— Restore full fronts, hidden stock, unlock progress and counters.redo(): boolean— Restore an undone state.reset(): void— Restore the exact initial level.
Constants
shelfLayouts
const shelfLayouts: readonly ["cabinet", "staggered", "islands", "pyramid", "procedural"]
Compartment arrangement families; adjacency is visual and never restricts a legal goods move.
Types
Shelf
interface Shelf
A shelf front and its ordered hidden stock. Locks open after a global number of matched groups.
id: string— Stable shelf name used by actions andisUnlocked; unique within the config.slots: (string | null)[]— The visible front, one entry per slot: a goods key, or null when empty. At leastmatchSizeand at most 12 slots.stock?: string[]— Hidden goods keys, in the order they fill the front (left to right) once it is empty; at most 180. Default none.unlockAfter?: number— Groups that must have been matched anywhere on the board before this shelf can be used, 0 to 120. Default 0, open from the start.
ShelfAction
interface ShelfAction
Move one front item to an empty front slot.
from: string— Id of the source shelf.slot: number— Source slot index, 0 at the left; it must hold a good.to: string— Id of the destination shelf.target: number— Destination slot index, 0 at the left; it must be empty.
ShelfBoardLayout
interface ShelfBoardLayout
Generated board bounds and compartments, suitable for 2D cabinets or 3D shelves.
family: ShelfLayout— The arrangement family used.compartments: ShelfCompartment[]— One compartment per shelf, in shelf order, none overlapping.width: number— Width of the whole board in slot-width units.height: number— Height of the whole board in slot-width units.
ShelfCompartment
interface ShelfCompartment
Renderer-independent shelf centre and bounds in slot-width units. Slots run left to right.
id: string— The id of the shelf this compartment holds.x: number— Centre across the board, in slot-width units; the board as a whole is centred on 0.y: number— Centre down the board, in slot-width units; later rows have larger y, and the board is centred on 0.width: number— Width in slot-width units: the shelf's slot count plus a 0.25 margin.depth: number— Depth in slot-width units; 1.5 for every compartment.
ShelfConfig
interface ShelfConfig
Generic goods sorting. Stock refills left-to-right only when a front becomes empty.
shelves: Shelf[]— The shelves, 2 to 16 of them, with unique ids.matchSize?: number— Identical keys on one front that clear together; a whole number, 2 to 8. Default 3.maxMoves?: number— Most moves allowed, 1 to 10000. Default none, so play is unlimited.
ShelfEvent
type ShelfEvent = { type: 'moved'; key: string; action: ShelfAction; } | { type: 'matched'; shelf: string; key: string; slots: number[]; } | { type: 'refilled'; shelf: string; keys: string[]; } | { type: 'unlocked'; shelf: string; }
Immediate ordered move, match, refill and unlock events.
ShelfGenerateOptions
interface ShelfGenerateOptions
Public seed, difficulty and runtime layout controls.
seed?: number— Unsigned 32-bit integer, 0 allowed. Default 1.difficulty?: 'easy' | 'medium' | 'hard'— Preset for the source count, waves, mixing steps and locked bays:'easy','medium'or'hard'. Default'medium'.sources?: number— Shelves that start with goods and stock, frommatchSizeto 8; sources, buffers and locked buffers together are at most 16. Default 3, 4 or 5 by difficulty.waves?: number— Stock waves per source shelf, 1 to 5; the first is its front and the rest its hidden stock. Default 1, 2 or 3 by difficulty.matchSize?: number— Identical goods that clear together, 2 to 5. Default 3.buffers?: number— Empty working shelves open from the start, 1 to 3. Default 1.lockedBuffers?: number— Extra empty working shelves locked untilunlockAftergroups are cleared, 0 to 3. Default 0, 1 or 2 by difficulty.unlockAfter?: number— Groups that must be cleared to open the locked shelves, from 1 to sources times waves. Default 2.scrambleSteps?: number— Reversible legal moves used to mix the fronts, 0 to 80; mixing stops early when no safe move is left. Default 5, 16 or 32 by difficulty.layout?: ShelfLayout | 'auto'— Compartment arrangement family, or'auto'to cycle the families withlevelIndex. Default'auto'.levelIndex?: number— Level number that selects the family under'auto', 0 to 1,000,000. Default 0.layoutPeriod?: number— Levels each family lasts under'auto', 1 to 1000. Default 1.bufferSlots?: number— Slots on each working shelf, frommatchSizeto 12. DefaultmatchSize.
ShelfLayout
type ShelfLayout = typeof shelfLayouts[number]
One of the arrangement families in shelfLayouts.
ShelfLevel
interface ShelfLevel
Replay-validated level and measurements; solution length is not an optimality claim.
config: ShelfConfig— The playable puzzle, with its fronts already mixed.solution: ShelfAction[]— A winning route fromconfig, validated by replaying it through the rules; it is not the shortest.seed: number— The seed the level was generated from.difficulty: 'easy' | 'medium' | 'hard'— The difficulty preset used.parameters: Required<ShelfGenerateOptions>— Every option as resolved, defaults filled in, so the same request regenerates this level.layout: ShelfBoardLayout— Compartment poses for drawing the shelves, in slot-width units.metrics: { items: number; stockItems: number; sources: number; waves: number; initialChoices: number; mixedShelves: number; initiallyLocked: number; scrambled: number; solutionMoves: number; refills: number; }— Measurements of the level: goods, hidden stock, sources, waves, opening choices, mixed fronts, locked bays, mixing moves made, solution length and refills.
ShelfSortApi
type ShelfSortApi = typeof kit
Public pure shelf-sort rules and runtime seeded generation API.
ShelfState
interface ShelfState
Complete owned state, including remaining stock and progress.
shelves: Shelf[]— Every shelf as it stands: current front and remaining stock.moves: number— Moves played so far, counted againstmaxMoves.cleared: Record<string, number>— Groups cleared so far, counted by goods key.matches: number— Groups cleared so far in total; this is the count that opens each shelf'sunlockAfterlock.


