All apps

Snake

1.0.0

A grid snake with buffered turning, wall/wrap modes, a bonus pickup that times out, and speed that ramps with length.

gamesnakearcade

InstallDownload ADIOpens in your panel, which asks where it goesFree — then this installs in one click

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:

kindnamewhat it is
dashboardgame-snakethe game itself — frontend only, a one-route backend stub for the dashboard contract

Install it:

adi-mono marketplace install <marketplace>/game-snake

It arrives archived, same as every dashboard — start it from the Dashboards page, or:

adi-mono marketplace start <marketplace>/game-snake/dashboards/game-snake

Then 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: wall mode kills the snake against the board edge; wrap mode 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.ts

31 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.ts

It 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
Manifest
raw.githubusercontent.com/adi-family/marketplace/main/apps/marketplace.json