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)
18 lines
364 B
Plaintext
18 lines
364 B
Plaintext
== legal swap: plan matches model ==
|
|
model: legal true depth 1 score 30 moves 1
|
|
plan: legal true rounds 1 score 30 moves 1
|
|
final==model true
|
|
contiguous true
|
|
final board:
|
|
RRPBORRG
|
|
PGPPOGRO
|
|
YYBOPRYB
|
|
GBYBYRGP
|
|
OGBYRGOY
|
|
BYRRPRBG
|
|
YOYYROBB
|
|
OROBPPRB
|
|
== illegal swap: untouched ==
|
|
legal false rounds 0 score 0 moves 0
|
|
ok: animation layer leaves the model result unchanged
|