// P10.10 — Per-round cascade-cue timing snapshot: prove the frame loop plays ONE // ascending combo cue PER cascade round, edge-triggered as each round's clear // begins — combo1, combo2, … clamped at combo5, an audible ascending run — NOT a // single combo cue keyed to the final cascade depth. The pure timing helper // `cascade_rounds_started` (board_anim.sx) reports how many rounds have begun // clearing by a given `elapsed` on the SAME swap→(clear,fall)* timeline the view // animates; the round→cue mapping reuses `cascade_cue_index`/`cascade_cue_name` // (audio.sx), the exact functions `sfx_cascade` plays. Simulating the frame loop // over a 5-round chain yields the locked combo1..combo5 run. Links headless like // tests/anim_plan.sx (board_anim pulls no GL) + tests/cascade_cue.sx (audio alone // links). Failure is a non-zero exit code. #import "modules/std.sx"; #import "board_anim.sx"; #import "audio.sx"; main :: () -> i32 { print("== per-round cascade cue timing ==\n"); // `cascade_rounds_started` = how many cascade rounds have BEGUN clearing by // `elapsed`, on the swap(0.16s)→[clear(0.14s),fall(0.22s)] per-round timeline. // 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 : 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)); print("e=0.50 -> {}\n", cascade_rounds_started(0.50, rounds)); print("e=0.55 -> {}\n", cascade_rounds_started(0.55, rounds)); print("e=0.90 -> {}\n", cascade_rounds_started(0.90, rounds)); print("e=1.30 -> {}\n", cascade_rounds_started(1.30, rounds)); print("e=1.70 -> {}\n", cascade_rounds_started(1.70, rounds)); print("e=5.00 -> {}\n", cascade_rounds_started(5.00, rounds)); // Edge-triggered playback: stepping the clock, each rising edge of the // started-count plays the NEXT round's cue — combo1..combo5, once each, // 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 : i64 = 0; elapsed : f32 = 0.0; while fired < rounds { started := cascade_rounds_started(elapsed, rounds); while fired < started { fired += 1; print("round {} -> {}\n", fired, cascade_cue_name(cascade_cue_index(fired))); } elapsed += 0.02; } // Non-cascade guards: a 1-round (single match) and 0-round (illegal) move. The // frame loop gates the run on rounds>=2, so neither plays a combo run; the // helper still reports the lone/zero round so the gate lives in the caller. print("-- non-cascade --\n"); print("1-round@end {}\n", cascade_rounds_started(5.0, 1)); print("0-round@end {}\n", cascade_rounds_started(5.0, 0)); // Deep chain: the cue tail clamps at combo5 for round >= 5 (cascade_cue_index). print("-- deep-chain cue clamp --\n"); for 1..8 (r) { print("round {} -> {}\n", r, cascade_cue_name(cascade_cue_index(r))); } print("ok: one ascending combo cue per cascade round, clamped at combo5\n"); return 0; }