The pack
containerSort
function containerSort(): Pack<{ containerSort: typeof kit }>
Install pure rules; no renderer, DOM or simulation hook is required.
Helpers
createContainerSort
function createContainerSort(config: SortConfig, certificate?: SortAction[]): ContainerSort
Create a sorting puzzle. Throws on 1 or more than 24 containers, a capacity outside 1 to 32 (or not one per container), an overfull container, an empty colour key, a bad maxMoves or transfer, or a certificate that does not replay to a sorted position.
generateContainerSort
function generateContainerSort(options: SortGenerationOptions = {}): SortGeneration
Deal a seeded puzzle with a proven solution. By default (fullStart) it shuffles every colour into full containers plus empty spares and keeps the first of attempts candidates that solveGuided solves and that meets minFragmentation (else the most fragmented one solved). Throws when no candidate is solved within searchBudget, and on out-of-range options.
Classes
ContainerSort
class ContainerSort
Capacity-limited run transfers, with undo and bounded hints.
constructor(config: SortConfig, private certificate?: SortAction[])get state(): SortState— A copy of the current position; changing it does not affect the puzzle.capacity(index: number): number— Capacity of containerindex; 0 when there is no such container.get solved(): boolean— Whether the position is sorted: every non-empty container holds one colour and is full, or, withrequireFull: false, holds one colour with at most one part-filled container per colour.get lost(): boolean— Whether the puzzle is unsolved and the move limit is reached or no legal move is left.legalActions(): SortAction[]— Every legal move from the current position.preview(a: SortAction): ActionResult<SortEvent>— The outcome of moveawithout playing it; changes nothing.apply(a: SortAction): ActionResult<SortEvent>— Play movea. An illegal move changes nothing and returnsvalid: falsewith a reason:invalid-container,move-limit,empty-source,full-destinationordifferent-color.solve(maxStates = 20000): SearchResult<SortAction>— Exact breadth-first search from the current position, visiting at mostmaxStatespositions (default 20000); changes nothing.solveGuided(maxStates=20000):SearchResult<SortAction>— Bounded depth-first continuation search with equivalent empty tubes collapsed. No optimality claim.solveBest(maxStates=20000):SearchResult<SortAction>— Revalidate the construction route from the live position before returning it.undo(): boolean— Take back the last move; false when there is none.redo(): boolean— Replay the move last taken back; false when there is none.reset(): void— Return to the dealt position and clear the undo and redo history.
Types
ContainerSortApi
type ContainerSortApi = typeof kit
Public headless container sorting puzzles helpers.
SortAction
interface SortAction
A move: pour from one container into another.
from: number— Index of the container poured from.to: number— Index of the container poured into; must differ fromfrom.
SortConfig
interface SortConfig
Contents are bottom-first. Capacity may differ between containers.
containers: string[][]— The colour keys in each container, bottom first: 2 to 24 containers, none holding more than its capacity, every key a non-empty string.capacity: number | number[]— Cells in every container (one number), or one capacity per container; each 1 to 32.requireFull?: boolean— True (the default): sorted means every non-empty container is full of one colour. False: containers may be part full, but a colour may leave only one of its containers part full, so it is gathered as tightly as the containers allow (three reds in tubes of four sit in one tube, five fill one and part of another).maxMoves?: number— Most moves allowed, at least 1; the puzzle is lost when it is reached unsolved. Default none.transfer?: 'run'|'single'—runpours the whole matching top run that fits;singlemoves one top cell. Defaultrun.
SortEvent
interface SortEvent
What a move did.
type: 'transferred'— The kind of event; alwaystransferred.from: number— Index of the container poured from.to: number— Index of the container poured into.color: string— The colour key moved.count: number— How many cells moved.
SortGeneration
interface SortGeneration
A generated deal: the puzzle, a winning solution and how mixed it is.
config: SortConfig— The puzzle, ready forcreateContainerSort.solution: SortAction[]— A replay-checked winning sequence of moves; pass it tocreateContainerSortsosolveBestcan use it.scrambled: number— Reverse-scramble moves used (fullStart: false); 0 for a full-start deal.seed: number— The seed asked for.metrics: { /** Runs of one colour, counted over every container. */ runs: number; /** Containers holding more than one colour. */ mixed: number; /** \runs` minus the number of colours: 0 when sorted. / fragmentation: number; /* Fewest moves any solution needs, from fragmentation: a move merges at most one pair of runs. / lowerBound: number; /* Whether `fragmentation` reached `minFragmentation`. */ targetMet: boolean; }` — How mixed the deal is; none of it is a shortest-solution claim.
SortGenerationOptions
interface SortGenerationOptions
What generateContainerSort takes; every field may be left out.
colors?: string[]— The distinct colour keys to deal, at least 2. Default 3, 5, 7 or 9 numbered keys by difficulty.capacity?: number— Cells in every container, 2 to 12. Default 4 (5 on very-hard).empty?: number— Spare empty containers, 1 to 6. Default 2.steps?: number— Reverse-scramble moves, 1 to 200; used only whenfullStartis false. Default 24, 48, 88 or 120 by difficulty.seed?: number— Unsigned 32-bit seed; the same seed and options give the same deal. Default 1.transfer?: 'run'|'single'— Transfer rule the deal is proved under. Defaultrun.difficulty?: 'easy'|'medium'|'hard'|'very-hard'— Tier that sets the defaults for colours, capacity, steps andminFragmentation. Defaultmedium.minFragmentation?: number— Fragmentation (colour runs minus colours) to aim for, 0 to 200; attempts continue until it is met, keeping the best. Default 3, 8, 14 or 20 by difficulty.attempts?: number— Candidate deals to try, 1 to 64. Default 12.fullStart?: boolean— Deal full mixed containers plus spares and prove them solvable (default true); false builds by reverse scrambling, which can leave containers part filled.searchBudget?: number— Positions the solver may visit per candidate when proving it, 1 to 1,000,000. Default 10,000.
SortState
interface SortState
A position: what each container holds and how many moves have been played.
containers: string[][]— The colour keys in each container, bottom first.moves: number— Moves played from the dealt position.


