Add a purely-visual animation timeline so the board no longer snaps on a move. board_anim.sx records, on a value-copy of the pre-move board, the swap and each cascade round's matched cells + per-column fall provenance, then BoardView plays it over delta_time: the two swapped gems SLIDE between cells (and ping out-and-back on an illegal swap), matched gems SCALE OUT, and survivors FALL into place while refills drop in from above the grid. The model stays authoritative: plan_and_commit still calls commit_swap on the real board exactly as before, and the recording replays the identical primitives from the identical cells + RNG state, so the timeline ends ON the model's settled board. tests/anim_plan.sx is the determinism guard — it asserts the committed board, score, moves, and the timeline's final state all equal an independent commit_swap of the same move, that the rounds are contiguous, and that an illegal swap records nothing and leaves the board untouched. All pre-existing logic/cascade goldens stay green. Evidence (sx-test-metal, iOS 26.0, time-sampled with temporarily-lengthened durations; committed durations are the short production values): goldens/p6_anim_swap.png gems sliding between (5,4)/(6,4) goldens/p6_anim_clear.png matched reds scaling out in row 4 goldens/p6_anim_fall.png gems mid-fall with gaps + refill dropping in goldens/p6_anim_after.png settled board == model (SCORE 30, MOVES 29/30)
m3te
A match-3 game written entirely in the sx language, targeting iOS first.
- Game logic, rendering, input, and UI are all authored in sx.
- Art (palettes, sprite sheets) is produced as real assets.
- Verification gate: sx logic tests pass AND the iOS app builds & launches in the Simulator.
Development is driven by the multi-agent flow (Product Owner → Worker → Reviewer → Observer).
Verification gate
The gate has two halves. Both must pass. The sx compiler used below lives at
/Users/agra/projects/sx/zig-out/bin/sx (override the runner's binary with the
SX env var). Run everything from the repo root.
1. Logic tests
Pure-sx logic tests run under sx and have their stdout + exit code diffed
against committed snapshots in tests/expected/. A failed assertion exits the
process non-zero, so it fails the runner (and the gate).
bash tools/run_tests.sh
- A test is any
tests/<name>.sxthat has atests/expected/<name>.exitmarker;tests/test.sx(theexpectassert helper) has no marker, so it is not itself run. - Regenerate snapshots after an intentional change:
bash tools/run_tests.sh --update.
2. iOS Simulator build + launch
Build the app for the simulator, then install/launch it on an available device and screenshot the rendered scene (blue background + a centered orange quad).
# Build the .app bundle (sx-out/ios/M3te.app):
/Users/agra/projects/sx/zig-out/bin/sx build --target ios-sim main.sx
# Discover an available simulator — do NOT hardcode a udid:
xcrun simctl list devices available
# e.g. capture the first available device's UDID into $udid:
udid=$(xcrun simctl list devices available | grep -Eo '[0-9A-Fa-f-]{36}' | head -1)
# Boot it (skip if already "Booted") and bring the Simulator window up:
xcrun simctl boot "$udid" || true
open -a Simulator
# Install, launch (bundle id co.swipelab.m3te), and screenshot:
xcrun simctl install booted sx-out/ios/M3te.app
xcrun simctl launch booted co.swipelab.m3te
xcrun simctl io booted screenshot /tmp/m3te.png
The screenshot should match goldens/p0_quad.png (a centered orange quad over a
blue clear), modulo the status-bar clock — pixel-exact equality is not required.
A tap on the quad flips its color (orange ↔ green); see
goldens/p0_input_before.png / goldens/p0_input_after.png.