P4.4: selection highlight + score/moves HUD (sx, iOS sim)
Tap a gem to select it: BoardView hit-tests the touch to a grid cell and draws a bright rim + translucent fill over it; tapping the same cell clears the selection, tapping another moves it, tapping off-board clears it. Selection only — no swap (that's P5). The HUD renders the live score and remaining moves (out of the move limit) in the Lato font on a translucent card above the grid. The touch→cell geometry is factored into a pure BoardLayout (no GL/stb imports) that BoardView composes and P5 will reuse for swap endpoints. tests/hit_test.sx locks point_to_cell as the exact inverse of cell_frame (every cell center round-trips; off-board taps reject) — headless because BoardLayout pulls no C imports. goldens/p4_hud.png captures the scene after a real idb tap at (201,437)pt: the HUD plus a yellow selection rim on the red gem at cell (col 4, row 3).
This commit is contained in:
1
tests/expected/hit_test.exit
Normal file
1
tests/expected/hit_test.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
5
tests/expected/hit_test.stdout
Normal file
5
tests/expected/hit_test.stdout
Normal file
@@ -0,0 +1,5 @@
|
||||
grid origin (100,0) cell 75
|
||||
ok: 64/64 cell centers round-trip
|
||||
corner maps to (3,5)
|
||||
ok: 0 off-board taps resolved to a cell
|
||||
ok: hit-test mapping is the inverse of the layout
|
||||
68
tests/hit_test.sx
Normal file
68
tests/hit_test.sx
Normal file
@@ -0,0 +1,68 @@
|
||||
// Hit-test golden (P4.4): lock the touch→cell mapping `BoardLayout.point_to_cell`
|
||||
// as the exact inverse of `cell_frame`. The two are written independently — one
|
||||
// multiplies a cell index by cell_size, the other divides a point by cell_size
|
||||
// and truncates — so round-tripping every cell center back to its own cell is a
|
||||
// real check, not a tautology. BoardView and P5's swap input both reuse this
|
||||
// mapping, so a drift here would silently send taps/swaps to the wrong cell.
|
||||
//
|
||||
// Imports BoardLayout (no GL/stb), not BoardView, so it links headless. It also
|
||||
// avoids tests/test.sx, whose modules/process.sx → modules/trace.sx pulls in a
|
||||
// second `Frame` struct that collides with the UI `Frame`. Failure is signalled
|
||||
// via a non-zero exit code (the runner checks exit code AND stdout).
|
||||
#import "modules/std.sx";
|
||||
#import "board.sx";
|
||||
#import "board_layout.sx";
|
||||
|
||||
main :: () -> s32 {
|
||||
// 800×600 with no safe inset → a 600px square grid, cell 75, centered: the
|
||||
// grid origin lands at (100, 0). Integer math keeps the dump deterministic.
|
||||
lay : BoardLayout = ---;
|
||||
lay.compute(Frame.make(0.0, 0.0, 800.0, 600.0), EdgeInsets.zero());
|
||||
|
||||
print("grid origin ({},{}) cell {}\n",
|
||||
cast(s64) lay.origin.x, cast(s64) lay.origin.y, cast(s64) lay.cell_size);
|
||||
|
||||
fails : s64 = 0;
|
||||
|
||||
// Every cell center must map back to its own cell.
|
||||
hits : s64 = 0;
|
||||
for 0..BOARD_ROWS: (row) {
|
||||
for 0..BOARD_COLS: (col) {
|
||||
cf := lay.cell_frame(col, row);
|
||||
center := Point.{ x = cf.mid_x(), y = cf.mid_y() };
|
||||
if h := lay.point_to_cell(center) {
|
||||
if h.col == col and h.row == row { hits += 1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
if hits != BOARD_CELLS { fails += 1; }
|
||||
print("ok: {}/{} cell centers round-trip\n", hits, BOARD_CELLS);
|
||||
|
||||
// A cell's top-left corner belongs to that cell (the leading edge is
|
||||
// inclusive), so corner-of-(3,5) resolves to (3,5).
|
||||
corner := Point.{ x = lay.origin.x + 3.0 * lay.cell_size, y = lay.origin.y + 5.0 * lay.cell_size };
|
||||
corner_col : s64 = -1;
|
||||
corner_row : s64 = -1;
|
||||
if h := lay.point_to_cell(corner) { corner_col = h.col; corner_row = h.row; }
|
||||
if corner_col != 3 or corner_row != 5 { fails += 1; }
|
||||
print("corner maps to ({},{})\n", corner_col, corner_row);
|
||||
|
||||
// Off-board taps reject (null): left of, above, and right of the grid. None
|
||||
// should resolve to a cell, so the on-board count must stay 0.
|
||||
off_left := Point.{ x = lay.origin.x - 5.0, y = lay.origin.y + 10.0 };
|
||||
off_above := Point.{ x = lay.origin.x + 10.0, y = lay.origin.y - 5.0 };
|
||||
off_right := Point.{ x = lay.origin.x + 8.0 * lay.cell_size + 1.0, y = lay.origin.y + 10.0 };
|
||||
on_board : s64 = 0;
|
||||
if h := lay.point_to_cell(off_left) { on_board += 1; print("off_left hit ({},{})\n", h.col, h.row); }
|
||||
if h := lay.point_to_cell(off_above) { on_board += 1; print("off_above hit ({},{})\n", h.col, h.row); }
|
||||
if h := lay.point_to_cell(off_right) { on_board += 1; print("off_right hit ({},{})\n", h.col, h.row); }
|
||||
if on_board != 0 { fails += 1; }
|
||||
print("ok: {} off-board taps resolved to a cell\n", on_board);
|
||||
|
||||
if fails == 0 {
|
||||
print("ok: hit-test mapping is the inverse of the layout\n");
|
||||
return 0;
|
||||
}
|
||||
print("FAIL: {} hit-test checks failed\n", fails);
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user