15

Sokoban is easy to render and brutal to generate: random box/goal placement gives you puzzles that are either trivial or literally unsolvable (a box shoved into a corner it can never leave). I wanted procedurally generated levels that are always solvable and actually interesting.

The naive "scatter boxes and goals" prompt produced unsolvable boards constantly. The fix was to generate levels backwards: start from the solved state and pull boxes away from goals with legal reverse-moves, so every level is solvable by construction. I also had it score difficulty by solution length.

One answer below improved the difficulty tuning. Sharing the generator prompt.

THE PROMPT
Write a Sokoban game with a procedural level generator in a single HTML canvas file, vanilla JS. Grid of walls/floor, a player that pushes boxes (never pulls), goal tiles; a level is solved when every box sits on a goal. Arrow keys/WASD to move, U to undo, R to restart.

Generate levels so they are ALWAYS solvable, using reverse generation (do NOT scatter boxes randomly):
1. Start from a solved state: place N boxes directly on N goal tiles inside a bounded room with some interior walls.
2. Repeatedly apply legal REVERSE moves (player 'pulls' a box, which is the inverse of a push) to walk boxes away from their goals, recording moves.
3. The resulting scrambled state is guaranteed solvable because the recorded reverse moves, played backward, are a valid solution.
4. Difficulty = length of that solution path and number of boxes; expose a target difficulty and keep pulling until it's met (with a max-attempts cap).
5. Reject boards where a box starts already on its goal (too easy) or where the player can't reach the required push positions.

Rendering + feel: crisp tile art, the player faces its move direction, boxes on goals tint differently, a soft 'thunk' on a push, an undo that animates in reverse, a move counter, and a satisfying flash + chime when the last box lands. Show the seed and difficulty. Framerate-independent input (one move per keypress, with key-repeat handling). Explain in a comment why reverse generation guarantees solvability.
1the 'reject boards where a box starts on its goal' check is a nice cheap filter. saves generating a batch of freebies.canvas_carl 3 months ago
add a comment

1 Answer

10

Reverse generation is the only sane way to do this, nice writeup. Solution length alone is a weak difficulty signal though, you can get long-but-mindless push chains. I weight difficulty by the number of 'turns' the boxes take and how many boxes must be interleaved (you have to park one to move another), which correlates much better with how hard it actually feels. Also cap how close boxes start to walls, or you generate a lot of visually cramped-but-trivial boards.

THE PROMPT
Score difficulty as: solutionLength*0.4 + boxTurns*1.5 + interleavedBoxMoves*2, where boxTurns counts direction changes per box and interleavedBoxMoves counts times you must move box B between two moves of box A. Keep pulling until this exceeds the target.

Your Answer