6.9 KiB
SNAKE — Xiaomi Watch S4
Textured Snake on a 368px circular arena, centered on the round 466px
screen. 32px tiles (60% larger than 0.7) occupy 89 legal cells of an 11×11
bounding grid; every tile corner stays inside the actual circle. The square
board frame is gone. Directional sprite artwork and user icon are preserved. Package identity: com.ilias.snake.
Version 0.9 (versionCode 9): crown control experiment, unit-tested, needs an on-watch check. See docs/CROWN.md for the firmware reasoning.
Crown control (new in 0.9)
- The watch firmware exposes no crown event to quickapps; it routes crown
rotation into the scrollable container as scroll deltas. The page is now a
full-screen vertical scroll with the dial centered;
onscrollfires for both crown rotation and finger scroll. - Scroll deltas accumulate; every 40 px of scroll = one 90° turn,
clockwise = clockwise turn. Reversals are structurally impossible because
turns are relative 90° steps (
rotateDir-style), never absolute directions. - The scroller is snapped back to center after every event, so the board never visibly moves, even though the scroll travel exists.
- Swipes still work exactly as before; both inputs share the two-slot queue.
Play
- Swipe to steer. A two-turn queue accepts rapid corners, consumes one turn per tick, and rejects duplicate directions and reversals against the last queued direction. Excess inputs are ignored, not allowed to replace a turn.
- Tap the board, snake or apple to pause. Tap RESUME (or an exposed part of the board) to resume. Pausing clears queued turns.
- In the pause or game-over panel, select CLASSIC (the circular boundary ends the run) or WRAP (cross the circular boundary to appear at the opposite legal end of the same row or column). Changing mode starts a fresh, paused game; tap RESUME to begin. Selecting the current mode does not reset the run. The current mode is shown below the board.
- Both modes end on self-collision. Moving into the vacating tail is legal.
- PLAY AGAIN is a large 216×56px retry target. Retry keeps the selected mode, resets score/speed/queued turns, and starts playing. Filling the board displays YOU WIN at 89 occupied cells (86 points) and hides the apple safely.
- Leaving the page pauses it and stops the timer. Returning requires an explicit resume; game-over never resumes automatically. The system back action remains untouched. No crown, rotary, side-button or back interception APIs are used.
- Crown to steer (0.9): rotate the crown; every 40px of scroll is one
90° turn, clockwise = clockwise. Crown and swipe feed the same queue; the
board stays visually fixed while you turn. If your firmware gives the crown
coarser steps than 40px, adjust
CROWN_STEP_PXat the top ofindex.ux.
Score, speed and persistence
Each apple is one point. The always-visible header shows score and best. One
best score is shared across Classic and Wrap, saved through documented
@system.storage
under snake.best. Mode is session-only; a newly created page defaults to Classic.
The movement interval starts at 300ms, decreases by 10ms per apple, and never goes below 120ms. Only one movement timer runs; none runs while hidden, paused, finished or destroyed. Old timer callbacks are invalidated.
Storage reads merge with the in-memory maximum; writes wait for the initial read and are serialized/coalesced so slow callbacks cannot overwrite a newer record. The coordinator survives page recreation within the module lifetime. Old 0.7 records through 253 remain valid and are never reduced to the new 86-point maximum. Invalid saved values are ignored. Errors/exceptions do not interrupt gameplay: failed reads/writes retry on later apples or page-show activity, without a retry loop. A failed/uncompleted write or immediate app termination may lose the unsaved record. Storage persistence still needs verification on the watch.
Lightweight rendering
Directional PNG tiles use only left/top positioning, no transforms. Existing
20px sprite art is nearest-neighbor enlarged to native 32px assets (crisp pixel
art, not new high-resolution detail). The board is a newly generated circular
PNG with transparent corners and a subtle legal-cell checker pattern. Wrapped
texture connections handle each varying row/column length. All rows/columns
have at least three cells, avoiding ambiguous one-/two-cell seams.
Food scans only 89 legal cells, including a nearly full board. HUD/footer
rectangles never overlap legal play tiles; text/overlay fit the watch chord.
tests/preview-0.8-static.png is a static composition of actual geometry/art,
with a legal-cell overlay—not a runtime screenshot.
Build and test
npm ci
npm test
npm run release
npm test runs the original core, piece and page tests, 14 upgrade
regressions, the numeric layout suite, and deterministic randomized property tests. Upgrade coverage includes rapid
swipes, lifecycle/timer races, storage failures and async ordering, mode changes,
all four Wrap edges, seam textures, full-board victory and bounded food spawning.
The page suite also drives the crown scroll path: one notch = one queued turn,
scroller re-centered, queue capped at two, no reversal, turn consumed on tick.
New behaviors were exercised through failing-then-passing test cycles.
Release output: dist/com.ilias.snake.release.0.9.rpk. The locked
aiot-toolkit 2.0.5 uses the existing sign/private.pem and sign/certificate.pem;
do not regenerate or commit those keys. Release uses plain JS (not JSC/protobuf),
matching the previously working toolchain. The dependency lock is unchanged;
npm ci currently reports 14 development-toolchain audit findings (3 low,
11 high). No forced toolkit upgrade was applied.
Files
src/pages/index/index.ux: round-screen UI, swipe/crown queue, game/lifecycle state.src/common/snake.js: movement, collision, Wrap piece classification, food.src/common/best.js: asynchronous storage coordinator.src/common/tex/: native 32px sprite artwork and circular board.docs/CROWN.md: firmware-derived rationale for the crown scroll path.tests/{snake,pieces,page,upgrade,layout,properties,round,art,round-history}.cjs: Node regression suites, including history-based circular seam orientation checks.
Install the signed RPK through AstroBox. Source lives at https://gitea.oimcloud.myaddr.tools/Omarchy-Big-PC/vela-snake-m (write access to the upstream codex-bot repo was denied, so 0.9 lives in the fork).
Earlier versions
- 0.2/0.3: board played on watch; designWidth 480→466 fixed undersizing.
- 0.6: directional textured tiles and preserved user-supplied icon.
- 0.7: queued swipes, live/persisted records, capped acceleration, pause/resume, lifecycle safety, retry/win panels and selectable Classic/Wrap.
- 0.8: circular arena (no square frame), 32px tiles, 89 legal cells.