[verified] Snake 0.7: queued controls, records, pause and wrap mode

This commit is contained in:
2026-09-12 12:23:09 +01:00
parent eacad97240
commit 5a7081b933
10 changed files with 548 additions and 109 deletions
+79 -35
View File
@@ -1,51 +1,95 @@
# SNAKE — classic snake for Xiaomi Watch S4
# SNAKE — Xiaomi Watch S4
Classic snake on a 16×16 grid rendered on the round 466px screen (320px board,
20px cells). Swipe to steer; the crown is NOT used (confirmed unsupported by the
Vela quick-app API on this device). Tap the overlay to restart after a crash.
Textured Snake on a 16×16 grid: 20px tiles, 320px board, centered on a round
466px screen. Existing PNG board, directional snake pieces, apple and icon are
preserved. Package identity: `com.ilias.snake`.
**Status: RENDERS AND PLAYS ON WATCH** (user-confirmed since 0.2/0.3).
**Version 0.7 (versionCode 7): locally tested and release-built; the new features
still need an on-watch check.** Earlier versions rendered and played on the
user's Watch S4 41mm. Node tests and numeric layout checks are not device tests.
## Controls
## Play
- **Swipe** in any direction to turn (180° reversals are blocked in the logic)
- **Tap** the game-over overlay to restart
- Crown does nothing — no rotary API exists in Vela quick apps (triple-confirmed
against docs + the largest open-source S4 app)
- **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** (walls end the run) or
**WRAP** (cross an edge to appear on the opposite edge). 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** 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.
## Project layout
## Score, speed and persistence
```text
src/
├── app.ux # app lifecycle
├── manifest.json # com.ilias.snake, watch, designWidth 466
├── common/
│ ├── snake.js # pure game logic: step/turn/spawnFood — unit-tested
│ └── icon.png # 192×192 circular app icon
└── pages/index/index.ux # board rendering (for-directive), swipe input, game loop
tests/
├── snake.cjs # logic: self-collision, food, 180° block, wrap rules
└── page.cjs # wiring: template binds, handlers, state machine
```
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`](https://iot.mi.com/vela/quickapp/en/features/data/storage.html)
under `snake.best`. Mode is session-only; a newly created page defaults to Classic.
The game ticks on a 300ms `setInterval`, cleared on hide/destroy.
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.
## Build & test
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.
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 or new
art assets. Wrapped neighbors are normalized before classifying seam textures.
Food placement takes at most 256 cell checks, including a nearly full board.
The HUD, footer, board and overlay fit the circular chord numerically with
at least an 8px total horizontal safety allowance.
## Build and test
```bash
npm ci
npm run release # signed .rpk → dist/
node tests/snake.cjs # logic tests
node tests/page.cjs # page wiring tests
npm test
npm run release
```
## Install
`npm test` runs the original core, piece and page tests, 13 incremental 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.
New behaviors were exercised through failing-then-passing test cycles.
Side-load `dist/com.ilias.snake.release.*.rpk` via AstroBox.
Releases: https://gitea.oimcloud.myaddr.tools/codex-bot/vela-snake
Release output: `dist/com.ilias.snake.release.0.7.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.
## Version notes
## Files
- **0.2** — board renders on watch (user confirmed); ring undersized
- **0.3** — designWidth 480→466 fixed the undersized bezel
- Crown-based input was tried and disproven — swipe is the only reliable input
- `src/pages/index/index.ux`: round-screen UI, swipe queue, game/lifecycle state.
- `src/common/snake.js`: movement, collision, Wrap piece classification, food.
- `src/common/best.js`: asynchronous storage coordinator.
- `src/common/tex/`: preserved textured artwork.
- `tests/{snake,pieces,page,upgrade,layout,properties}.cjs`: Node regression suites.
Install the signed RPK through AstroBox. Source/releases live at
https://gitea.oimcloud.myaddr.tools/codex-bot/vela-snake — publication is a
separate step after review; a local build is not evidence of a published release.
## 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.