lang migration: rename signed integer types sN -> iN

Mechanical sweep of all .sx sources, plan docs, and tests/expected
snapshots for the sx language rename (s8/s16/s32/s64 -> i8/i16/i32/i64).
Verified: tools/run_tests.sh 23/23.

Note: the ios-sim build has 2 pre-existing 'restart' dot-call errors
from the sx opt-in UFCS change (sx a47ea14) — independent of this
rename (present pre-sweep); migrated in the follow-up commit.
This commit is contained in:
swipelab
2026-06-12 09:36:51 +03:00
parent 1ab74c7d08
commit 6f7d2f4db2
36 changed files with 344 additions and 265 deletions

View File

@@ -30,8 +30,8 @@ boards_equal :: (x: *Board, y: *Board) -> bool {
true
}
main :: () -> s32 {
fails : s64 = 0;
main :: () -> i32 {
fails : i64 = 0;
// ── Legal swap: plan == model, timeline ends on the model ───────────────
// (5,4)->(6,4): brings R into (5,4), completing R,R,R across cols 3-5 of row

View File

@@ -4,7 +4,7 @@
#import "modules/std.sx";
t :: #import "test.sx";
main :: () -> s32 {
main :: () -> i32 {
t.expect(2 + 2 == 4, "two plus two is four");
t.expect(7 % 3 == 1, "seven mod three is one");
t.expect(10 - 4 == 6, "ten minus four is six");

View File

@@ -19,11 +19,11 @@
irect :: (f: Frame) -> string {
format("({},{},{},{})",
cast(s64) f.origin.x, cast(s64) f.origin.y,
cast(s64) f.size.width, cast(s64) f.size.height)
cast(i64) f.origin.x, cast(i64) f.origin.y,
cast(i64) f.size.width, cast(i64) f.size.height)
}
main :: () -> s32 {
main :: () -> i32 {
// 800×600, no safe inset → 600px square grid, cell 75, origin (100,0): the
// same layout tests/hit_test.sx pins, so the numbers are checkable by hand.
lay : BoardLayout = ---;
@@ -36,18 +36,18 @@ main :: () -> s32 {
print("title {}\n", irect(bl.title));
print("button {}\n", irect(bl.button));
fails : s64 = 0;
fails : i64 = 0;
// The button is horizontally centered on the grid (centred banner).
bcx := bl.button.mid_x();
if cast(s64) bcx != cast(s64) grid.mid_x() { fails += 1; }
print("button mid_x {} grid mid_x {}\n", cast(s64) bcx, cast(s64) grid.mid_x());
if cast(i64) bcx != cast(i64) grid.mid_x() { fails += 1; }
print("button mid_x {} grid mid_x {}\n", cast(i64) bcx, cast(i64) grid.mid_x());
// The whole button sits inside the panel — its four corners are contained,
// so it can never spill outside the drawn card.
bx0 := bl.button.origin.x; by0 := bl.button.origin.y;
bx1 := bl.button.max_x(); by1 := bl.button.max_y();
corners_in : s64 = 0;
corners_in : i64 = 0;
if bl.panel.contains(Point.{ x = bx0, y = by0 }) { corners_in += 1; }
if bl.panel.contains(Point.{ x = bx1, y = by0 }) { corners_in += 1; }
if bl.panel.contains(Point.{ x = bx0, y = by1 }) { corners_in += 1; }
@@ -67,7 +67,7 @@ main :: () -> s32 {
// in the button, so each leaves the level frozen.
corner_cell := Point.{ x = grid.origin.x + lay.cell_size * 0.5, y = grid.origin.y + lay.cell_size * 0.5 };
outside := Point.{ x = bl.panel.origin.x - 5.0, y = bl.panel.mid_y() };
off_hits : s64 = 0;
off_hits : i64 = 0;
if bl.button.contains(corner_cell) { off_hits += 1; }
if bl.button.contains(outside) { off_hits += 1; }
if off_hits != 0 { fails += 1; }

View File

@@ -10,8 +10,8 @@ SEED :: 1337;
// Count every horizontal or vertical window of three consecutive same-type
// gems. A correctly initialized board has zero. This walks the finished board
// independently of the placement logic, so it's a real check, not a tautology.
count_three_runs :: (b: *Board) -> s32 {
runs : s32 = 0;
count_three_runs :: (b: *Board) -> i32 {
runs : i32 = 0;
for 0..BOARD_ROWS (row) {
for 0..(BOARD_COLS - 2) (col) {
g := b.at(col, row);
@@ -27,7 +27,7 @@ count_three_runs :: (b: *Board) -> s32 {
runs
}
main :: () -> s32 {
main :: () -> i32 {
board : Board = ---;
board.init(SEED);

View File

@@ -84,7 +84,7 @@ checker_board :: () -> Board {
b
}
main :: () -> s32 {
main :: () -> i32 {
print("== cascade (resolution loop) ==\n");
// Drive the loop one round at a time so each post-round board is visible in
@@ -94,7 +94,7 @@ main :: () -> s32 {
out(board_dump(@b));
depth := 0;
counts := List(s64).{};
counts := List(i64).{};
while true {
n := resolve_step(@b);
if n == 0 { break; }

View File

@@ -8,13 +8,13 @@
#import "modules/std.sx";
#import "audio.sx";
main :: () -> s32 {
main :: () -> i32 {
print("== cascade cue selection (depth -> combo cue) ==\n");
// Walk a representative depth range (0..9) so both clamps and the monotonic
// middle are visible: depths 0,1 pin to the first cue; depths >= 5 pin to
// the last; 2,3,4 step up one cue at a time.
prev : s64 = -1;
prev : i64 = -1;
for 0..10 (depth) {
idx := cascade_cue_index(depth);
print("depth {} -> idx {} ({})\n", depth, idx, cascade_cue_name(idx));

View File

@@ -13,7 +13,7 @@
#import "board_anim.sx";
#import "audio.sx";
main :: () -> s32 {
main :: () -> i32 {
print("== per-round cascade cue timing ==\n");
// `cascade_rounds_started` = how many cascade rounds have BEGUN clearing by
@@ -21,7 +21,7 @@ main :: () -> s32 {
// Round k (0-based) starts clearing at 0.16 + k*0.36; sampled safely INSIDE
// each round window so the integer step is unambiguous. Locked for 5 rounds.
print("-- started-count across a 5-round chain --\n");
rounds : s64 = 5;
rounds : i64 = 5;
print("e=0.00 -> {}\n", cascade_rounds_started(0.00, rounds));
print("e=0.10 -> {}\n", cascade_rounds_started(0.10, rounds));
print("e=0.20 -> {}\n", cascade_rounds_started(0.20, rounds));
@@ -37,7 +37,7 @@ main :: () -> s32 {
// ascending. This IS the loop main's frame loop runs; the emitted run is the
// locked acceptance ordering.
print("-- ascending per-round run --\n");
fired : s64 = 0;
fired : i64 = 0;
elapsed : f32 = 0.0;
while fired < rounds {
started := cascade_rounds_started(elapsed, rounds);

View File

@@ -38,7 +38,7 @@ load_board :: (rows: []string) -> Board {
// Detect→clear one scene, snapshot before/after, and assert the three clear
// invariants against the matched-cell set: every flagged cell is now a hole,
// every unflagged cell is unchanged, and the returned count is exact.
scene :: (name: string, rows: []string, want_cleared: s64) {
scene :: (name: string, rows: []string, want_cleared: i64) {
b := load_board(rows);
orig := load_board(rows); // pristine copy for the unchanged check
@@ -65,7 +65,7 @@ scene :: (name: string, rows: []string, want_cleared: s64) {
t.expect(cleared == want_cleared, concat(name, ": cleared count exact"));
}
main :: () -> s32 {
main :: () -> i32 {
print("== clear (detect -> clear) ==\n");
// Single horizontal 3-run (row 3, cols 2-4) → three holes there only.

View File

@@ -79,7 +79,7 @@ scene :: (name: string, rows: []string, want_moved: bool) {
t.expect(moved == want_moved, concat(name, ": moved flag exact"));
}
main :: () -> s32 {
main :: () -> i32 {
print("== collapse (gravity) ==\n");
// Eight independent columns, one case each (top-to-bottom):

View File

@@ -31,7 +31,7 @@ char_to_gem :: (c: u8) -> Gem {
// Load an 8x8 board from `rows` (top row first, each exactly BOARD_COLS chars),
// seeded RNG, running score zeroed so `board.score` ends equal to the payout.
load_board :: (rows: []string, seed: s64) -> Board {
load_board :: (rows: []string, seed: i64) -> Board {
b : Board = ---;
for 0..BOARD_ROWS (row) {
line := rows[row];
@@ -51,14 +51,14 @@ load_board :: (rows: []string, seed: s64) -> Board {
// identical board awards `want_mult` into `Board.score` and reports it as
// `Cascade.awarded` at the same depth. A depth-1 settle must equal the flat sum
// (no bonus); a deeper chain must strictly exceed it.
scene :: (name: string, rows: []string, seed: s64, want_flat: s64, want_mult: s64) {
scene :: (name: string, rows: []string, seed: i64, want_flat: i64, want_mult: i64) {
print("== {} ==\n", name);
b := load_board(rows, seed);
out(board_dump(@b));
flat : s64 = 0;
mult : s64 = 0;
depth : s64 = 0;
flat : i64 = 0;
mult : i64 = 0;
depth : i64 = 0;
while true {
base := score_round(@b);
n := resolve_step(@b);
@@ -90,7 +90,7 @@ scene :: (name: string, rows: []string, seed: s64, want_flat: s64, want_mult: s6
t.expect(b2.score == want_mult, concat(name, ": resolve accumulates into board.score"));
}
main :: () -> s32 {
main :: () -> i32 {
print("== combo (cascade multiplier) ==\n");
// Single-round clear (seed 0): one RRR clears and the refill makes no new

View File

@@ -19,8 +19,8 @@
fabs :: (x: f32) -> f32 { if x < 0.0 then 0.0 - x else x }
approx :: (a: f32, b: f32) -> bool { fabs(a - b) < 0.0001 }
main :: () -> s32 {
fails : s64 = 0;
main :: () -> i32 {
fails : i64 = 0;
// 1. Endpoints are locked: every curve starts/ends exactly on its rest value
// (the in/out curves at 1, the spring at 1, the squash envelope at 0).

View File

@@ -13,14 +13,14 @@
#import "modules/std.sx";
#import "board_fx.sx";
main :: () -> s32 {
main :: () -> i32 {
print("== combo emphasis selection (depth -> fx level / popup font) ==\n");
// The cascade-cue index per depth 0..9, copied from cascade_cue.stdout. The
// FX level must equal this entry for entry — the audio/visual lockstep.
expect_level : [10]s64 = .{ 0, 0, 1, 2, 3, 4, 4, 4, 4, 4 };
expect_level : [10]i64 = .{ 0, 0, 1, 2, 3, 4, 4, 4, 4, 4 };
prev : s64 = -1;
prev : i64 = -1;
for 0..10 (depth) {
lvl := fx_combo_level(depth);
font := fx_popup_font(depth);

View File

@@ -18,8 +18,8 @@
fabs :: (x: f32) -> f32 { if x < 0.0 then 0.0 - x else x }
approx :: (a: f32, b: f32) -> bool { fabs(a - b) < 0.0001 }
main :: () -> s32 {
fails : s64 = 0;
main :: () -> i32 {
fails : i64 = 0;
// 1. t==0 idle pose is EXACTLY rest for every cell (the determinism invariant).
print("== idle t=0 is rest for all cells ==\n");

View File

@@ -14,19 +14,19 @@
#import "board.sx";
#import "board_layout.sx";
main :: () -> s32 {
main :: () -> i32 {
// 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);
cast(i64) lay.origin.x, cast(i64) lay.origin.y, cast(i64) lay.cell_size);
fails : s64 = 0;
fails : i64 = 0;
// Every cell center must map back to its own cell.
hits : s64 = 0;
hits : i64 = 0;
for 0..BOARD_ROWS (row) {
for 0..BOARD_COLS (col) {
cf := lay.cell_frame(col, row);
@@ -42,8 +42,8 @@ main :: () -> s32 {
// 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;
corner_col : i64 = -1;
corner_row : i64 = -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);
@@ -53,7 +53,7 @@ main :: () -> s32 {
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;
on_board : i64 = 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); }

View File

@@ -34,7 +34,7 @@ char_to_gem :: (c: u8) -> Gem {
// Load an 8x8 board from `rows` (top row first, each exactly BOARD_COLS chars),
// seeded RNG, running score zeroed, the turn counters reset to a fresh game, and
// the per-level goal set.
load_board :: (rows: []string, seed: s64, move_limit: s64, target_score: s64) -> Board {
load_board :: (rows: []string, seed: i64, move_limit: i64, target_score: i64) -> Board {
b : Board = ---;
for 0..BOARD_ROWS (row) {
line := rows[row];
@@ -55,7 +55,7 @@ boards_equal :: (a: *Board, b: *Board) -> bool {
true
}
main :: () -> s32 {
main :: () -> i32 {
print("== level (turn / goal state machine) ==\n");
// ── Start: a fresh seeded board reads in_progress with the default goal ──

View File

@@ -20,7 +20,7 @@ char_to_gem :: (c: u8) -> Gem {
// Build a scene: load an 8x8 board from `rows` (top row first, each exactly
// BOARD_COLS gem characters), detect matches, print board + matched dump, and
// assert the matched-cell count.
scene :: (name: string, rows: []string, want_count: s64) {
scene :: (name: string, rows: []string, want_count: i64) {
b : Board = ---;
for 0..BOARD_ROWS (row) {
line := rows[row];
@@ -38,7 +38,7 @@ scene :: (name: string, rows: []string, want_count: s64) {
t.expect(m.count() == want_count, name);
}
main :: () -> s32 {
main :: () -> i32 {
// Single horizontal 3-run (row 3, cols 2-4).
scene("horizontal-3", .[
"OGOGOGOG",

View File

@@ -41,8 +41,8 @@ load_board :: (rows: []string) -> Board {
b
}
count_empties :: (b: *Board) -> s64 {
n : s64 = 0;
count_empties :: (b: *Board) -> i64 {
n : i64 = 0;
for 0..BOARD_CELLS (i) { if b.cells[i] == .empty { n += 1; } }
n
}
@@ -72,7 +72,7 @@ fresh_board :: () -> Board {
b
}
main :: () -> s32 {
main :: () -> i32 {
print("== refill (seeded) ==\n");
// Pipeline, snapshotting each stage.
@@ -123,7 +123,7 @@ main :: () -> s32 {
// filled, then refill again. The board's RNG has advanced past the first
// fill, so the second fill draws new gems — proof it does NOT reseed per call.
holes_n := 0;
hole_idx : [BOARD_CELLS]s64 = ---;
hole_idx : [BOARD_CELLS]i64 = ---;
fill1 : [BOARD_CELLS]Gem = ---;
for 0..BOARD_CELLS (i) {
if pre.cells[i] == .empty {

View File

@@ -42,7 +42,7 @@ load_board :: (rows: []string) -> Board {
// Score one scene: snapshot board + enumerated runs + points, then assert
// `score_round` is exact and `add_round_score` accumulates it into `board.score`.
scene :: (name: string, rows: []string, want_points: s64) {
scene :: (name: string, rows: []string, want_points: i64) {
b := load_board(rows);
runs := find_runs(@b);
@@ -58,7 +58,7 @@ scene :: (name: string, rows: []string, want_points: s64) {
concat(name, ": add_round_score accumulates into board.score"));
}
main :: () -> s32 {
main :: () -> i32 {
print("== score (base match scoring) ==\n");
// Single length-3 horizontal run (row 3, cols 2-4) -> SCORE_RUN_3 = 30.

View File

@@ -44,11 +44,11 @@ boards_equal :: (x: *Board, y: *Board) -> bool {
true
}
cell :: (col: s64, row: s64) -> Cell {
cell :: (col: i64, row: i64) -> Cell {
Cell.{ col = col, row = row }
}
main :: () -> s32 {
main :: () -> i32 {
print("== swap & legality ==\n");
// Board whose ONLY swap-formable match is the adjacent (2,3)<->(3,3)

View File

@@ -20,7 +20,7 @@
SEED :: 1337;
cell_center :: (lay: *BoardLayout, col: s64, row: s64) -> Point {
cell_center :: (lay: *BoardLayout, col: i64, row: i64) -> Point {
cf := lay.cell_frame(col, row);
Point.{ x = cf.mid_x(), y = cf.mid_y() }
}
@@ -32,14 +32,14 @@ boards_equal :: (x: *Board, y: *Board) -> bool {
true
}
main :: () -> s32 {
main :: () -> i32 {
// 800×600, no safe inset → 600px square grid, cell 75, origin (100, 0). A
// 60px drag clears the cell*0.5 = 37.5px swipe threshold on the dominant axis.
lay : BoardLayout = ---;
lay.compute(Frame.make(0.0, 0.0, 800.0, 600.0), EdgeInsets.zero());
D : f32 = 60.0;
fails : s64 = 0;
fails : i64 = 0;
// ── ILLEGAL swipe reverts ──────────────────────────────────────────────
// (0,0) and (1,0) are both red on the seed board, so swapping them forms no

View File

@@ -14,14 +14,14 @@
#import "board_layout.sx";
#import "swipe.sx";
cell_center :: (lay: *BoardLayout, col: s64, row: s64) -> Point {
cell_center :: (lay: *BoardLayout, col: i64, row: i64) -> Point {
cf := lay.cell_frame(col, row);
Point.{ x = cf.mid_x(), y = cf.mid_y() }
}
// Print the resolved intent (locked in the golden) and report whether it matches
// the expected adjacent pair (A, B). Drives the exit code alongside the dump.
expect_swap :: (label: string, got: ?Swap, ac: s64, ar: s64, bc: s64, br: s64) -> bool {
expect_swap :: (label: string, got: ?Swap, ac: i64, ar: i64, bc: i64, br: i64) -> bool {
if s := got {
print("{}: ({},{})->({},{})\n", label, s.a.col, s.a.row, s.b.col, s.b.row);
return s.a.col == ac and s.a.row == ar and s.b.col == bc and s.b.row == br;
@@ -39,14 +39,14 @@ expect_none :: (label: string, got: ?Swap) -> bool {
true
}
main :: () -> s32 {
main :: () -> i32 {
lay : BoardLayout = ---;
lay.compute(Frame.make(0.0, 0.0, 800.0, 600.0), EdgeInsets.zero());
print("grid origin ({},{}) cell {} threshold {}\n",
cast(s64) lay.origin.x, cast(s64) lay.origin.y, cast(s64) lay.cell_size,
cast(s64) (lay.cell_size * SWIPE_THRESHOLD_FRACTION));
cast(i64) lay.origin.x, cast(i64) lay.origin.y, cast(i64) lay.cell_size,
cast(i64) (lay.cell_size * SWIPE_THRESHOLD_FRACTION));
fails : s64 = 0;
fails : i64 = 0;
// A known interior cell; every cardinal swipe from it stays on the board.
start := cell_center(@lay, 3, 5);

View File

@@ -36,7 +36,7 @@ char_to_gem :: (c: u8) -> Gem {
// Load an 8x8 board from `rows` (top row first), seeded RNG, score zeroed, turn
// counters reset to a fresh game, and the per-level goal set.
load_board :: (rows: []string, seed: s64, move_limit: s64, target_score: s64) -> Board {
load_board :: (rows: []string, seed: i64, move_limit: i64, target_score: i64) -> Board {
b : Board = ---;
for 0..BOARD_ROWS (row) {
line := rows[row];
@@ -52,13 +52,13 @@ load_board :: (rows: []string, seed: s64, move_limit: s64, target_score: s64) ->
b
}
cell_center :: (lay: *BoardLayout, col: s64, row: s64) -> Point {
cell_center :: (lay: *BoardLayout, col: i64, row: i64) -> Point {
cf := lay.cell_frame(col, row);
Point.{ x = cf.mid_x(), y = cf.mid_y() }
}
main :: () -> s32 {
fails : s64 = 0;
main :: () -> i32 {
fails : i64 = 0;
// 800×600, no safe inset → 600px square grid, cell 75, origin (100, 0). A 60px
// drag clears the cell*0.5 = 37.5px swipe threshold on the dominant axis — the

View File

@@ -37,7 +37,7 @@ char_to_gem :: (c: u8) -> Gem {
// Load an 8x8 board from `rows` (top row first, each exactly BOARD_COLS chars),
// seeded RNG, running score zeroed, and the turn counters reset to a fresh game
// (no moves made, the given move budget).
load_board :: (rows: []string, seed: s64, move_limit: s64) -> Board {
load_board :: (rows: []string, seed: i64, move_limit: i64) -> Board {
b : Board = ---;
for 0..BOARD_ROWS (row) {
line := rows[row];
@@ -60,7 +60,7 @@ boards_equal :: (a: *Board, b: *Board) -> bool {
// One flag scene: snapshot the board, then count its single round's special
// runs and assert the tallies (and the boolean flags derived from them) are
// exactly the documented values. No RNG, no clear — pure detection.
flag_scene :: (name: string, rows: []string, want_len4: s64, want_len5_plus: s64) {
flag_scene :: (name: string, rows: []string, want_len4: i64, want_len5_plus: i64) {
print("== {} ==\n", name);
b := load_board(rows, 0, LIMIT);
out(board_dump(@b));
@@ -71,7 +71,7 @@ flag_scene :: (name: string, rows: []string, want_len4: s64, want_len5_plus: s64
t.expect(sp.len5_plus == want_len5_plus, concat(name, ": len5_plus count exact"));
}
main :: () -> s32 {
main :: () -> i32 {
print("== turn (accounting + special-match flagging) ==\n");
// ── Special-match flagging (single round, no RNG) ──────────────────────