Commit Graph

5 Commits

Author SHA1 Message Date
swipelab
2713a67b2b P2.1: clear matched cells (pure sx)
Add the first resolution-pipeline step to the headless board model.

- Introduce an `empty` hole sentinel on the Gem enum (ordinal 6, outside
  GEM_COUNT so the RNG/pick_gem never draw it). board_dump renders holes as
  EMPTY_CHAR ('.') via a single branch in gem_char, leaving boards without
  holes byte-identical to before (existing goldens unchanged).
- clear_cells(board, mask): set every matched cell to `.empty`, leave all
  others untouched, return the count cleared.
- clear_matches(board): detect+clear in one call; returns 0 (board unchanged)
  when there are no matches.

No gravity or refill yet (P2.2 / P2.3).

tests/clear.sx applies detect->clear to hand-crafted boards (single
horizontal/vertical runs, disjoint runs, an overlapping L/T whose shared cell
clears once, and a no-match checkerboard), snapshots before/after, and asserts
matched cells became holes, non-matched cells are unchanged, and the cleared
count is exact. Locked as tests/expected/clear.{stdout,exit}.
2026-06-04 19:57:08 +03:00
swipelab
4264f5f36f P1.3: adjacent-swap legality (pure sx)
Add swap + legality to the board model:
- swap(board, a, b): in-place, self-inverse cell exchange (trial then revert).
- adjacent(a, b): orthogonal-adjacency predicate (diagonal/gap = false).
- swap_legal(board, a, b): legal iff adjacent AND, after the trial swap, either
  swapped cell participates in a 3+ match (reuses find_matches); leaves the
  board unchanged. Non-adjacent/diagonal rejected before any match check.
- Cell/Swap structs + legal_swaps(board): all currently-legal swaps in a stable
  row-major, right-before-down order; dump_swaps for deterministic snapshotting.

tests/swap_legality.sx asserts the predicate over hand-crafted boards (legal
3-run, no-match, non-adjacent, diagonal, only-the-other-gem-matches) and the
non-mutating revert; locks legal_swaps over the seeded board as a golden.
2026-06-04 19:44:54 +03:00
swipelab
b0c081e397 P1.2: match detection (3+ horizontal/vertical runs)
Add a pure-sx match detector to the board model: `find_matches` walks each
row and column once in maximal same-type spans and marks every cell in a run
of length >= 3 into a `MatchMask` (a per-cell membership set mirroring
Board.cells). Overlapping shapes (L / T where a horizontal and vertical run
share a cell) collapse to the union automatically. `dump_matches` renders the
set deterministically: matched cells show their gem char, others '.'.

Detection only — no clear/collapse/refill (that is P2.1).

tests/match_detect.sx exercises hand-crafted boards (built explicitly on a
run-free checkerboard, no seeded init): a horizontal 3-run, a vertical 3-run,
multiple disjoint runs, length-4 and length-5 runs, intersecting L and T
shapes (shared cell counted once), and a no-match board. Output is locked as
tests/expected/match_detect.stdout (+ .exit) and asserts matched-cell counts.
2026-06-04 19:27:15 +03:00
swipelab
45e7eb803e P1.1: pure-sx Gem & Board model with seeded, match-free init
Add board.sx, the headless Phase-1 match-3 core:
- Gem enum (6 types, ordinal 0..5) + single-char dump alphabet.
- Rng: a 32-bit LCG carried in s64, masked to 32 bits each step, so the
  stream is host-width independent and valid for any seed.
- Board (8x8, row-major) with idx/at/set accessors and a seeded init that
  fills row-major, excluding any gem that would complete a 3-in-a-row with
  the two cells to the left or above — so the result has zero pre-existing
  matches. Single RNG draw per cell, always terminates.
- board_dump: deterministic one-row-per-line textual snapshot.

tests/board_init.sx seeds with a fixed seed, dumps the board, and asserts
zero horizontal/vertical 3-in-a-row runs via an independent scan. Output and
exit code are locked as goldens. App ios-sim build is unaffected (main.sx
does not import the model yet).
2026-06-04 19:12:55 +03:00
swipelab
6d9aee67ba P0.4: wire the logic verification gate (sx assert helper + snapshot runner)
Add a real logic-test gate so future pure-sx game logic fails the build on a
bad assertion:

- tests/test.sx: `expect(cond, msg)` assert helper — prints a greppable
  `FAIL <file>:<line>: <msg>` and exits non-zero via process.exit on failure.
- tools/run_tests.sh: snapshot runner mirroring sx/tests/run_examples.sh; runs
  each tests/<name>.sx and diffs stdout + exit code against tests/expected/.
  Exits 0 iff all tests pass.
- tests/arith.sx (+ expected snapshots): seed passing sanity test.
- README.md: document both halves of the gate — logic runner and the
  reproducible ios-sim build/launch sequence (with device discovery).
2026-06-04 18:57:21 +03:00