The pack
shapePacking
function shapePacking(): Pack<{ shapePacking: typeof kit }>
Install pure rules; no renderer, DOM or simulation hook is required.
Helpers
cellAt
function cellAt(mode: PackingConfig['mode'], point: Cell, step = 0.5): Cell
The board cell under a point in board units, for turning a pointer into a placement: the grid square containing it,
the nearest hex cell (by cube rounding), or in polygon mode the point snapped to multiples of step (default 0.5,
the lattice generated tangrams use). Throws on a non-finite point or a step that is not positive.
cellCentre
function cellCentre(mode: PackingConfig['mode'], cell: Cell): Cell
The centre of a board cell in board units: grid cells are unit squares (centre x + 0.5, y + 0.5), hex cells use hexCentre, and polygon mode's points are themselves.
createShapePacking
function createShapePacking(config: PackingConfig, certificate?: Placement[]): ShapePacking
Create a packing puzzle. Pieces may touch along edges; any overlap of area is refused. Throws on a bad mode, 0 or more than 32 pieces, duplicate ids, invalid cells or polygons (self-intersecting, repeated vertices, under 3 or over 64 corners), a rotation the mode does not allow, an invalid candidate, or a certificate that does not fill the target.
generateShapePacking
function generateShapePacking(options: PackingGenerationOptions = {}): PackingGeneration
Cut a seeded silhouette into connected pieces and return them with a replay-checked exact cover; it makes no uniqueness claim. Throws on out-of-range options, a layout the mode does not have, or a cut into more than the 32 pieces a puzzle may have.
hexCentre
function hexCentre(cell: Cell): Cell
The centre of hex cell (q, r) in board units, the distance between neighbouring centres being 1: pointy-top hexes, q across and r down-right, so { x: q + r / 2, y: r * sqrt(3) / 2 }. Scale and offset it to draw.
Classes
ShapePacking
class ShapePacking
A packing puzzle's rules: square, hex or polygon pieces placed into a silhouette, with undo, redo and a solver. Get one from createShapePacking.
constructor(config: PackingConfig, private certificate?: Placement[])solveBest(maxStates = 20000): ReturnType<ShapePacking['solve']>— A winning continuation, without changing anything: the certificate's remaining placements when they still fit around the pieces already placed, otherwisesolve(maxStates). Its first placement is the hint.get state(): PackingState— A copy of the state: the placements and the move count.get target(): Cell[]— A copy of the silhouette to fill.get pieces(): PackingPiece[]— A copy of the pieces.points(p: Placement): Cell[]— The cells (grid, hex) or vertices (polygon) a placement covers on the board: the piece is mirrored (if asked), rotated, moved so its smallest x and y are 0, then moved byx,y. Empty for an unknown piece.preview(p: Placement): ActionResult<PackingEvent>— Check a placement without making it. The result is valid, or invalid with a reason:unknown-piece,invalid-transform,off-grid,outside-targetoroverlap.place(p: Placement): ActionResult<PackingEvent>— Place a piece, or move it if it is already placed, as one move in the history. Returns{ valid: true, events }, or{ valid: false, reason, events: [] }for the reasonspreviewgives.remove(id: string): ActionResult<PackingEvent>— Take a piece off the board as one move in the history. An id that is not placed is invalid with the reasonnot-placed.get unfilled(): Cell[]— The target cells no piece covers yet; always empty in polygon mode.get remainingArea(): number— Target area still uncovered: a count of cells, or an area in polygon mode.get solved(): boolean— Whether every piece is placed and together they cover the target exactly.solve(maxStates = 20000): SearchResult<Placement> | { status: 'unsupported'; actions: []; visited: 0 }— Find placements for the pieces not yet placed that cover the rest of the target, by breadth-first search over at mostmaxStatespositions (default 20,000), placing pieces in config order; changes nothing. Grid and hex modes try every pose at every target cell; polygon mode searches only the config'scandidatesand reportsunsupportedwithout them.unsolvableis a proof only over those placements;budget-exhaustedis no claim.undo(): boolean— Undo the last move. Returns false when there is nothing to undo.redo(): boolean— Redo the move last undone. Returns false when there is nothing to redo.reset(): void— Return to the empty table and clear the history.
Types
PackingConfig
interface PackingConfig
A puzzle: the silhouette to fill, the pieces to fill it with and the moves allowed.
mode: 'grid' | 'hex' | 'polygon'— The kind of board: square cells, hex cells (axial q/r) or continuous polygons.target: Cell[]— The silhouette to fill: its cells (grid, hex) or the vertices of a simple polygon (3 to 64 corners).pieces: PackingPiece[]— The pieces, 1 to 32, with unique ids.rotations?: number[]— Rotations allowed, in degrees; default 0, 90, 180 and 270 (hex: every 60). Grid needs multiples of 90 and hex of 60; polygons take any finite angle. Must not be empty.reflect?: boolean— Whether pieces may be mirrored; default false.candidates?: Placement[]— Placementssolvesearches among instead of generating them; polygon mode needs these to be solved at all. Each must be valid on an empty table.
PackingEvent
type PackingEvent ={ type: 'placed'; placement: Placement } | { type: 'removed'; id: string }
What a successful move reports: a piece placed (with its placement) or removed (by id).
PackingGeneration
interface PackingGeneration
A generated puzzle: the configuration, a winning set of placements and what was measured.
config: PackingConfig— The puzzle, ready forcreateShapePacking; pieces are shuffled and shown in a random pose.solution: Placement[]— One placement per piece that fills the target exactly; replayed before it is returned.seed: number— The seed asked for.metrics: { /** Pieces. */ pieces: number; /** Target cells, or the target's area in polygon mode. */ area: number; /** Poses each piece can take: rotations, times 2 with reflection. */ orientations: number; /** Pieces with distinct stored shapes. */ uniqueShapes: number; }— The puzzle's structure; not a uniqueness or human-difficulty claim.
PackingGenerationOptions
interface PackingGenerationOptions
Options for generateShapePacking; every field may be left out.
mode?: PackingConfig['mode']— The kind of board; defaulthex.difficulty?: 'easy' | 'medium' | 'hard' | 'very-hard'— Sets the default size, piece size and reflection; defaultmedium. Anything else throws.seed?: number— Seed of the random stream, an unsigned 32-bit whole number; default 1.layout?: string— The silhouette: gridrectangle(default),crossorsteps; hexhexagon(default),diamondortriangle; polygonsquare(default) ordiamond. Ignored whentargetis given.size?: number— Side (grid) or radius (hex) of the layout in cells: 2 to 12 for grid (default 4, 5, 6, 7 by difficulty) and 1 to 6 for hex (default 2, 2, 3, 3). Polygon ignores it.target?: Cell[]— Grid and hex: your own silhouette, as unique whole-number cells (hex: axial q/r), instead oflayoutandsize. A part with fewer cells thanminPieceSizebecomes a smaller piece.pieceSize?: number— Cells a piece grows to, 1 to 12; default 5, 5, 4 and 4 by difficulty. Pieces alternate between this and one less, and merging leftovers can make one larger. Polygon ignores it.minPieceSize?: number— Fewest cells in a piece, 1 topieceSize: smaller leftovers merge into a neighbour. Default 3 (orpieceSize, if smaller). Polygon ignores it.rotate?: boolean— False fixes every piece's orientation (the only rotation is 0); default true.reflect?: boolean— Whether pieces may be mirrored; default true fromhardup.
PackingPiece
interface PackingPiece
A piece of the puzzle, described in its own local coordinates.
id: string— Unique id, non-empty; placements refer to it.cells?: Cell[]— Grid and hex modes: the cells the piece covers, in whole-number coordinates (hex: axial q/r).polygon?: Cell[]— Polygon mode: the vertices of a simple polygon with 3 to 64 corners and no holes.
PackingState
interface PackingState
The puzzle's state, plain data.
placements: Placement[]— The pieces now placed, at most one each, in the order they were placed.moves: number— Moves made so far, counting each placement and removal.
Placement
interface Placement
How a piece lies on the target.
id: string— Id of the piece placed.x: number— Position across of the transformed piece's minimum-x corner: a whole number of cells in grid and hex modes.y: number— Position down of the transformed piece's minimum-y corner: a whole number of cells in grid and hex modes.rotation?: number— Rotation in degrees; default 0. It must be one of the configuration'srotations.mirrored?: boolean— Whether the piece is reflected before it is rotated; default false. It needs the configuration'sreflect.
ShapePackingApi
type ShapePackingApi = typeof kit
Public headless shape packing puzzles helpers.


