TinyGames · how it works
Boulder Dash. Dig through the earth for diamonds, reach the exit before the clock does, and do not be underneath anything heavy when it lets go.
Open index.html. No build step, no dependencies, no images — the cave is drawn.
rockfall/
index.html markup, and the d-pad
style.css the frame around the canvas
js/cave.js gravity, the cave generator and the arithmetic
- no pixels, no engine, no DOM
js/rockfall.js the playable layer
test-cave.js node test-cave.js
settle() runs gravity for one tick. Both of the mistakes below produce exactly
the same symptom — something moving further in one tick than it should — and
only one of them is the famous one.
Walk the grid top to bottom, move a falling boulder down one cell, and you meet that same boulder again further down the same pass. It falls again. And again. It reaches the floor in a single tick and the player never sees it move.
Scanning from the bottom up fixes it: a boulder that moves down lands in a row the pass has already finished with.
A boulder resting on another boulder rolls sideways into a gap. Rolling right moves it to a higher x in the row currently being scanned — which the scan has not reached yet. It gets picked up again and falls as well. Two moves, one tick.
Bottom-up says nothing about left-to-right, so this needs its own guard: a cell moved into this tick is skipped.
Two lines of code, two different bugs, one symptom.
Nothing moves more than one cell per tick.
The main test builds three thousand small caves containing exactly one boulder — so there is no question of which one moved where — and measures its displacement after a single tick. It also asserts the worst case observed was one, because a test where nothing ever moves would pass just as quietly.
Then each trap is rebuilt on purpose and watched breaking it:
| rebuilt mistake | what the test sees |
|---|---|
| top-down scan | the boulder is on the floor after one tick, not one cell down |
| no moved-guard | the same boulder rolls right and falls, two cells of travel |
Alongside: nothing is ever created or destroyed over thirty ticks of a crowded cave, nothing ends up inside the steel, and a cave that has finished falling stays where it is.
$ node test-cave.js
69 passed, 0 failed
A boulder only kills what is under it if it was already falling. One that has merely come to rest on your head is furniture — you can walk out from under it.
That is the original's rule and it is why every rounded object carries a falling flag instead of the code inferring danger from "is the cell below the player". Killing on contact would make half the caves unplayable, and the test builds both situations to prove the difference is real.
caveFor generates and rejects: a cave ships only if the exit can be dug to
and there are at least as many reachable diamonds as it asks for. The quota is then
set from that count rather than guessed, so need is always a promise the cave
can keep.
reachable() is deliberately pessimistic — boulders count as walls — so what it
approves is approved without relying on physics at all.
It is not a promise the cave is winnable. Boulder Dash is a game about a cave rearranging itself, and no cheap check can tell you whether a particular collapse will trap you. Saying otherwise would be claiming something this does not do.
What it does rule out is the failure a generator actually produces: a diamond bricked up behind bedrock, or an exit sealed off. Caves also never start with a boulder poised directly over the player, which the test checks across 320 of them.
Eight mutants. Seven broke something real; the eighth changed nothing on purpose.
| mutation | result |
|---|---|
settle scans top-down |
caught (6 assertions) |
| boulders can be pushed vertically | caught (4) |
settle without the moved-guard |
caught (2) |
| kills on contact rather than on impact | caught (2) |
reachable walks through boulders |
caught (1) |
| the quota is a fixed number, not a measured one | caught (1) |
settle wrapped, behaviour unchanged |
passes, as it should |
The vertical-push mutant survived the first version of the suite. The test put steel directly beyond the boulder, so it was only ever observing that the wall was in the way — a game that happily pushed boulders upward passed it. Both cases now leave somewhere for the push to go, and check the boulder did not move.
Arrows or WASD, held rather than tapped — Rockfall is played by leaning on a
direction and letting go at the right moment. On a touch screen, the same
three-by-three d-pad as Stow and Crossing. Tapping the cave also steps towards the
tap. R gives up the cave, Escape or P pauses.
Push boulders sideways into space; you cannot push them up, down, or into another. Fireflies patrol with their left hand on the wall, kill you on contact, and turn into diamonds if you drop a boulder on them.
Advertisement