What it does
You load a level from its rows of text and call move with a direction. The keeper walks one cell, pushing the crate
ahead if the cell beyond it is free, and each move reports whether a crate was pushed, whether it landed on a dead
square, whether the level is now deadlocked and whether it is solved. A move into a wall or a blocked crate changes
nothing. The whole game is a history, so undo goes all the way back, redo replays, and a new move clears the redo
branch.
Pushes cannot be taken back in the puzzle itself, so the real kindness is telling the player when they have lost. At
load the pack works out the dead squares, the floor cells from which no crate could ever reach a goal, by pulling
a crate backwards from every goal. During play it spots frozen crates, stuck for good against walls, dead squares
and each other. If any crate off its goal is on a dead square or frozen, the level is deadlocked, and the game can
say so at once instead of leaving the player to push on.
For hints, solution runs a breadth-first search over crate positions, one push per step, treating the keeper's
walking between pushes as free and skipping pushes into dead squares or frozen positions. It finds the solution with
the fewest pushes, proves a level unsolvable when it is, and says plainly when it ran out of budget instead.
Features
- The standard rules — push one crate at a time, never pull, every crate on a goal to win.
- Standard level text — walls, floor, goals, crates and the keeper in the usual characters, with ragged rows allowed.
- Your own legend — swap in any characters for walls, floor, goals, crates and the keeper.
- Level collections —
parseLevelsreads whole collection files, with each level's title and comments skipped. - Full undo and redo — back to the start, with reset.
- Moves and pushes — counted separately, for the two classic scores.
- Dead squares — found once at load;
isDeadtells you which cells to shade. - Frozen crates —
isFrozenanddeadlockedcatch the common lost positions the moment they happen. - A fewest-pushes solver — for hints, for checking levels, and as a proof when a level cannot be solved.
Good for
- Classic crate-pushing puzzle games, in the style of Sokoban or Boxxle, played through a level collection.
- Crate-pushing rooms inside a larger game, such as a dungeon's puzzle chamber.
- Teaching versions that shade dead squares and warn the moment a push loses the level.
- Level design tools, where the solver checks that a new level can be won and counts its pushes.
- Any grid game with irreversible pushes that needs undo and a "you can no longer win" check.
Make it yours
A level is just rows of text, so a new level is data: hand-written, pasted from a collection or loaded from a file
with parseLevels. The legend lets you use your own characters, for instance when levels come from a tile editor
that writes letters for each tile. Moves and pushes are reported separately, so a game can score on either, and the
solver's budget can be set per call.
Two quite different uses: a classic puzzle game that loads a collection file, shows each level's title, shades dead squares and offers a hint arrow from the solver; and a single crate room in an adventure game, written with a custom legend, that warns the player when a crate is stuck and offers to reset the room.
A quick look
A collection loaded, a move played, a deadlock caught, and a hint:
const levels = game.pusher.parseLevels(collectionText);
const room = game.pusher.createPusher({ level: levels[0]!.level });
const r = room.move('right'); // { moved, pushed, ontoDeadSquare, deadlocked, solved }
if (r.deadlocked) say('That crate can never reach a goal: undo.');
score(room.metrics); // { moves, pushes }
const hint = room.solution();
if (hint.status === 'solved') showArrow(hint.moves[0]!);
Works with
sliding-blocks— for blocks that slide until they hit something, or that the player drags.board— to place the level's cells in the world.tween— to animate the keeper's steps and the crates' pushes.
Good to know
- There is no level generator: levels are authored or read from a collection.
- The solver handles small and medium levels, a handful of crates in a few dozen cells in well under a second; large competition levels will exhaust its budget, so run it away from the frame for anything big.
- The deadlock check catches dead squares and frozen crates, not every lost position: crates corralled in a closed room are not detected.
- No variants: no pulling, no multiple keepers and no coloured crates matched to coloured goals.
- Levels are capped at 65,535 cells.






