(exponent + torus) — a keyboard-first, one-handed match-3 / merge puzzle. Tiles are powers of two; matching three or more merges them into the next power. The board wraps into a cylinder, and the keyboard controls let you roll the whole grid around a fixed centre cursor like a torus — so you can play a long session comfortably with one hand on a laptop, no trackpad aiming.
A variant of ExponenTile by Mike Bellika, reworked for that one-handed feel. Vanilla JavaScript, no framework, no build step.
ES modules need to be served over HTTP (opening index.html from file://
won't load them):
npm start # python3 -m http.server 8000
# then open http://localhost:8000
Your game auto-saves and resumes on your next visit (via localStorage);
"New game" starts over.
- Touch: flick a tile toward a neighbour to swap it (edge flicks wrap the seam); two-finger drag to roll the grid.
- Keyboard (one-handed): arrow keys / WASD roll the whole grid under a fixed centre cursor, wrapping both ways like a torus. Space/Enter picks up the tile under the cursor, then a direction swaps it; Esc cancels.
- Mouse: click a tile then a neighbour (or drag it) to swap.
Only swaps that create a match are accepted. The game ends when no swap can make a match.
The game is a cylinder: columns wrap (the left and right edges are neighbours, so a match can cross the seam), but rows do not — tiles fall from the top under gravity, which needs a real top and bottom. So a swap wraps horizontally but never vertically.
The cursor navigation is a torus: because the board is 9×9 there's a true centre cell, and rolling the grid under that fixed centre wraps in both directions. The torus is a navigation convenience (it's why play is one-handed); it doesn't change how matches or gravity work.
- 9×9 board (a
--nconstant insrc/style.cssandROWS/COLSinsrc/sim.js). New tiles are drawn uniformly from{2, 4, 8, 16}. - A run of
nsame-value tiles (≥3) merges tovalue × 2^(n−2). An L/T/+ where a horizontal and vertical run share a cell is one combined match ofn = h + v − 1cells. - Scoring matches the original game: each match awards points equal to the merged tile's value, summed over every match in every cascade pass.
src/sim.js— pure game logic, no DOM: board generation, cylinder-aware match detection, merges, gravity/refill, cascades, scoring, game-over.src/game.js— rendering, animation, input (mouse + keyboard), and thelocalStoragesave/resume.src/style.css,index.html— presentation.test/— headless checks.npm test(needs Node).
A variant of ExponenTile by Mike Laumann Bellika (site · source, MIT). Match and scoring rules were cross-checked against the original and against brianolson/exponentile. The core was independently reimplemented for this variant.
MIT — see LICENSE.