FX2: wire no-moves reshuffle into the UI swipe-commit path
The rendered swipe-commit path (`plan_and_commit`) bypassed the turn-loop's no-moves rule: a deadlocked board (no legal swap) stayed stuck on screen because only `play_turn` checked `!has_legal_swap` and reshuffled, and the UI never calls `play_turn`. Factor the post-settle "no legal swaps -> reshuffle" check into a shared `reshuffle_if_deadlocked` in board.sx and call it from BOTH `play_turn` and `plan_and_commit`, so the animated UI commit obeys the identical model rule. The reshuffle runs after the cascade settles (post-`commit_swap`); the AnimMove's recorded `final` stays the settled pre-reshuffle board, so the cascade animation, per-round audio, and input gating are unchanged — the reshuffled layout renders on the next settled frame. No win/lose/turn-accounting change; a reshuffle spends no move and no score. Regression test tests/swipe_reshuffle.sx drives the exact UI path (swipe_intent -> plan_and_commit) on the deadlocked board from tests/level.sx: before = no legal swaps / in_progress; after = reshuffled (has_legal_swap true, 9 legal swaps, no immediate match), score/moves/budget unchanged. It FAILS pre-fix (board stays stuck, has_legal_swap false) and PASSES post-fix.
This commit is contained in:
19
board.sx
19
board.sx
@@ -882,6 +882,20 @@ reshuffle :: (board: *Board) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
// After a committed move's cascade has settled, recover a deadlocked board so the
|
||||
// player is never stranded: if the level is still in progress yet no legal swap
|
||||
// remains, `reshuffle` the gems in place. A reshuffle is NOT a move and never runs
|
||||
// on a finished (won/lost) level, so win/lose and turn accounting are untouched.
|
||||
// Returns whether a reshuffle ran. BOTH the headless turn loop (`play_turn`) and
|
||||
// the animated UI commit (`plan_and_commit`) call this, so the rendered game obeys
|
||||
// the identical no-moves rule — neither path can leave the board stuck.
|
||||
reshuffle_if_deadlocked :: (board: *Board) -> bool {
|
||||
if level_status(board) == .in_progress and !has_legal_swap(board) {
|
||||
return reshuffle(board);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// Reset to a fresh, reproducible level: `init(seed)` reseeds the board (same
|
||||
// seed → identical starting layout), zeroes `score` and `moves_made`, and
|
||||
// restores the default move budget and score goal, so `level_status` reads
|
||||
@@ -921,9 +935,6 @@ play_turn :: (board: *Board, a: Cell, b: Cell) -> TurnResult {
|
||||
return TurnResult.{ accepted = false, move = frozen, status = status, reshuffled = false };
|
||||
}
|
||||
move := commit_swap(board, a, b);
|
||||
reshuffled := false;
|
||||
if level_status(board) == .in_progress and !has_legal_swap(board) {
|
||||
reshuffled = reshuffle(board);
|
||||
}
|
||||
reshuffled := reshuffle_if_deadlocked(board);
|
||||
TurnResult.{ accepted = true, move = move, status = level_status(board), reshuffled = reshuffled }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user