Snake
1.0.0A grid snake with buffered turning, wall/wrap modes, a bonus pickup that times out, and speed that ramps with length.
Already have ADI? Install it from your panel.
What's included
As published. What actually installs is read off the pinned commit's own tree.
- Dashboards
- game-snake — The game itself.
About
Snake
A grid snake, as one dashboard — buffered turning so a fast corner never gets dropped or flips the snake into itself, speed that ramps with length, an occasional bonus pickup that times out, a Wall/Wrap mode toggle, pause, and a best score kept per mode. Nothing on the backend: every rule lives in the browser.
This bundle carries one element:
| kind | name | what it is |
|---|---|---|
| dashboard | game-snake | the game itself — frontend only, a one-route backend stub for the dashboard contract |
Install it:
adi-mono marketplace install <marketplace>/game-snakeIt arrives archived, same as every dashboard — start it from the Dashboards page, or:
adi-mono marketplace start <marketplace>/game-snake/dashboards/game-snakeThen open it by its hostname. No secret, no project, nothing else to configure.
Why no backend logic
Every rule — movement, collision, growth, pickups, scoring — runs client-side; there is nothing for a server to compute or to keep private. The backend half of this dashboard exists only because the dashboard contract (guides/dashboards.md) needs one: it is the template's own unmodified backend/index.ts, answering /api/health so the page's status dot has something to poll, and nothing more. No route file was added.
How it's built
frontend/modules/game.ts is the panel; frontend/modules/.logic.ts is the whole rulebook — pure functions over a plain grid, no DOM, no randomness that isn't passed in as an argument. The dot in its name keeps it off the shell's auto-mounted panel list (guides/dashboards.md's module discovery lists every modules/*.ts that doesn't start with .); the panel reaches it at runtime as a same-origin ./.logic.js import, which the frontend server resolves straight back to this file.
The rules:
- movement: a 20×20 grid. Each tick advances the head one cell in the current heading and
drops the tail — unless the move eats something, in which case the tail stays and the snake is one cell longer.
- input buffering: up to two turns may queue ahead of the one being applied. A turn is only
ever checked against the last already-queued direction (or the current heading, if the queue is empty), so a fast "down then left" to round a corner lands both turns in order — and a turn that would reverse into the snake's own neck is rejected at the moment it's queued, never silently applied two ticks later into a death that looks like a dropped input.
- collision: running the head into its own body is fatal. Running it into the cell the tail is
about to vacate is not — the tail moves away the same tick, unless this move also grows the snake, in which case the tail stays and the same move is lethal.
- wall vs wrap:
wallmode kills the snake against the board edge;wrapmode carries the
head to the opposite edge instead. A segmented control at the top switches modes (starting a fresh game), and each mode keeps its own best score.
- growth and scoring: the regular pickup is worth 10 and always respawns somewhere the snake
and the bonus (if one is live) aren't. Eating it has a chance of also spawning a bonus pickup worth 50, which disappears on its own after a fixed number of ticks if nobody gets to it.
- difficulty curve: tick speed ramps up with the snake's length (not with score or elapsed
time), floored so a very long run stays controllable rather than becoming unplayable.
- pause: Space or P, or the on-screen Pause button — the overlay offers Resume as well as New
game, and nothing ticks while it's up.
frontend/modules/game.ts renders this onto a board of real DOM elements kept alive across ticks — every element is keyed by its index in the snake array rather than by a stable per- segment id, which is what makes the whole body visibly crawl forward on a CSS left/top transition with no special-casing: a non-growing move produces [nextHead, ...snake.slice(0, -1)], so index i > 0's value after the move is exactly index i - 1's value before it, and just re-reading each index's new position off the updated array animates correctly. A growing move only ever adds one element at index 0; nothing is removed mid-run. The bonus pickup draws its own remaining-life as a shrinking ring. The board is drawn as CSS percentages off the COLS/ROWS constants, so it holds together at any panel width, and the same palette is used in light and dark (the template's own ADI tokens already cover both — see [data-theme="light"] in frontend/ index.html).
Input: ArrowLeft/Right/Up/Down and a/d/w/s (case-insensitive, ignored while any text input or textarea elsewhere on the page has focus), a touch swipe (20px minimum, whichever axis moved further wins ties), and Space/P for pause. Best score is localStorage["adi.gameSnake.best.<mode>"], one key per mode, scoped to the browser — there is nothing server-side to lose it with.
Checking it works
bun test test/game-logic.test.ts31 checks against frontend/modules/.logic.ts directly — no DOM, no server: movement in all four directions, wall-mode and self collision, the tail-vacate exception (and its growth-cancels-it counterpart), wrap-mode edge wrapping, growth and scoring on both the regular and bonus pickup, food/bonus never spawning on an occupied cell, the bonus pickup expiring on its own tick, the length-based speed ramp, and the input queue — duplicate and reversing turns rejected at enqueue time, the two-deep cap, and FIFO order on dequeue.
A second, optional check covers what the logic tests structurally can't: whether the pause/ game-over overlay is actually invisible when it should be. .snake__overlay sets display: flex (to centre its card), and an author stylesheet rule always outranks the UA's own [hidden] { display: none } at equal specificity — so a missing .snake__overlay[hidden] override renders the overlay permanently, over the board, regardless of the hidden attribute. Neither bun:test's DOM nor happy-dom compute CSS cascade, so this needs a real browser:
bun add -d playwright && bunx playwright install chromium # one-time, not needed to play the game
bun test test/overlay-visibility.test.tsIt skips (with a warning, not a failure) if Playwright isn't installed — the dashboard itself has no dependency on it.
Licence
MIT.
Where it comes from
Installing clones this repository at this commit. A publisher who pushes something else afterwards changes nothing about what you already read.
- Repository
- github.com/adi-family/mp-game-snake
- Commit
b4bd2350d6a651f8b6b1f09cac083622a465d7eb- Branch
main- Address
store/game-snake- Contains
- dashboard