P5.2: swipe commits legal swap / reverts illegal (sx, iOS sim)
Wire touch input into the model in BoardView.handle_event. A press records the drag start (new DragInput, heap-allocated so it survives the per-frame BoardView rebuild between mouse_down and mouse_up); the release resolves the gesture against the same layout it was drawn with. A swipe — start→end mapped by swipe_intent to an adjacent-swap intent — is fed straight into commit_swap: a legal swap applies, cascades (clear→collapse→refill), accrues score and spends a move; an illegal one reverts, no move. A sub-threshold / off-board drag carries no intent and falls back to the tap behaviour (toggle/clear selection). The next frame re-renders board + HUD from the model. Reuses swipe.sx + board_layout.sx + commit_swap unchanged — this is wiring, not new legality/cascade logic. tests/swipe_commit.sx (new golden) drives the full path on the seeded board (SEED 1337): a rightward swipe (0,0)->(1,0) is illegal (two reds) and reverts byte-for-byte with no score/move; (5,4)->(6,4) is legal, completes R,R,R on row 4, awards 30, spends one move. Sim evidence (iPhone 17, iOS 26.0): goldens/p5_swap_before.png (SCORE 0, MOVES 30/30) and goldens/p5_swap_after.png (SCORE 30, MOVES 29/30) bracket a real idb-injected swipe at (276,475)->(327,475) pt = cell (5,4)->(6,4); the three reds clear and the board matches the model's resolved state.
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
#import "modules/ui/font.sx";
|
||||
#import "board.sx";
|
||||
#import "board_layout.sx";
|
||||
#import "swipe.sx";
|
||||
|
||||
// Fraction of a cell each gem occupies; the remainder is margin so a gem sits
|
||||
// inside its cell tile rather than touching the tile's edges.
|
||||
@@ -112,7 +113,8 @@ load_texture :: (path: [:0]u8, gpu: ?GPU) -> u32 {
|
||||
// Which board cell the player has currently selected, if any. Lives behind a
|
||||
// pointer (heap-allocated in main) because BoardView is a value rebuilt every
|
||||
// frame from `build_ui`, so the view itself cannot carry state across frames.
|
||||
// Selection only — P5 turns a selected→adjacent tap into a swap.
|
||||
// A tap toggles this highlight; a swipe commits a swap (see DragInput) and
|
||||
// clears it.
|
||||
BoardSelection :: struct {
|
||||
active: bool;
|
||||
cell: Cell;
|
||||
@@ -138,10 +140,35 @@ BoardSelection :: struct {
|
||||
}
|
||||
}
|
||||
|
||||
// Tracks an in-progress touch drag between its press and release so a swipe can
|
||||
// be resolved on lift: the press records the start point, and release maps
|
||||
// start→end through `swipe_intent` to an adjacent-swap intent. Heap-allocated
|
||||
// (like BoardSelection) so it survives BoardView's per-frame rebuild between the
|
||||
// down (touchesBegan → mouse_down) and up (touchesEnded → mouse_up) events.
|
||||
DragInput :: struct {
|
||||
active: bool;
|
||||
start: Point;
|
||||
|
||||
init :: (self: *DragInput) {
|
||||
self.active = false;
|
||||
self.start = Point.{ x = 0.0, y = 0.0 };
|
||||
}
|
||||
|
||||
begin :: (self: *DragInput, p: Point) {
|
||||
self.active = true;
|
||||
self.start = p;
|
||||
}
|
||||
|
||||
clear :: (self: *DragInput) {
|
||||
self.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
BoardView :: struct {
|
||||
board: *Board;
|
||||
assets: *BoardAssets;
|
||||
sel: *BoardSelection;
|
||||
drag: *DragInput;
|
||||
safe: EdgeInsets;
|
||||
|
||||
// Where the grid sits + the touch↔cell mapping. Recomputed each render /
|
||||
@@ -209,14 +236,33 @@ impl View for BoardView {
|
||||
render_hud(ctx, self.board, avail);
|
||||
}
|
||||
|
||||
// Touch input. A press records the drag start; the release resolves the
|
||||
// gesture against the SAME layout it was drawn with. A swipe (start→end maps
|
||||
// to an adjacent-swap intent) is fed straight into `commit_swap`: a legal
|
||||
// swap applies, cascades, scores and spends a move, an illegal one reverts —
|
||||
// either way the next frame re-renders the board + HUD from the model. A
|
||||
// sub-threshold / off-board drag carries no intent and falls back to the tap
|
||||
// behaviour: toggle the selection on the pressed cell, or clear it off-board.
|
||||
handle_event :: (self: *BoardView, event: *Event, frame: Frame) -> bool {
|
||||
self.compute_layout(frame);
|
||||
if event.* == {
|
||||
case .mouse_down: (d) {
|
||||
if hit := self.layout.point_to_cell(d.position) {
|
||||
self.sel.toggle(hit);
|
||||
} else {
|
||||
self.drag.begin(d.position);
|
||||
return true;
|
||||
}
|
||||
case .mouse_up: (d) {
|
||||
if !self.drag.active { return false; }
|
||||
start := self.drag.start;
|
||||
self.drag.clear();
|
||||
if intent := swipe_intent(@self.layout, start, d.position) {
|
||||
commit_swap(self.board, intent.a, intent.b);
|
||||
self.sel.clear();
|
||||
} else {
|
||||
if hit := self.layout.point_to_cell(start) {
|
||||
self.sel.toggle(hit);
|
||||
} else {
|
||||
self.sel.clear();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user