Phaser AE

Shelf sorting puzzles

Pro packv3.0.0-3Grid

The rules of goods-sorting puzzles: shelves of mixed items, a little working space, and hidden stock that comes forward as each front empties. A seeded generator builds whole levels with a winning route it has checked against the rules, and lays out the shelves for you.

This is a Pro pack. Upgrade to Phaser Pro to use it in your games.See Phaser Pro →

Released October 14, 2026 · updated October 19, 2026

Shelf sorting puzzles: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

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 and isUnlocked; unique within the config.
  • slots: (string | null)[] — The visible front, one entry per slot: a goods key, or null when empty. At least matchSize and 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, from matchSize to 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 until unlockAfter groups 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 with levelIndex. 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, from matchSize to 12. Default matchSize.

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 from config, 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 against maxMoves.
  • 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's unlockAfter lock.