P16.2: organic illegal swap — springy bounce-back (+ M3TE_BADSWAP hook)
render_swap's rejected-swap branch now drives the two gems with a P15.1 spring-based bounce-back (bad_swap_bounce): a quick lunge toward the neighbour, then a damped spring home that overshoots rest by a bounded amount and settles to exactly 0. f(0)=f(1)=0, so the move stays purely visual — board byte-identical to pre-swap, no score/move spent. - board_anim.sx: add bad_swap_bounce envelope (lunge via ease_out_cubic, settle via 1 - spring(u)); BADSWAP_LUNGE_T/AMP constants. - board_view.sx: replace the linear ping-out illegal branch with the bounce. - main.sx: add illegal_swaps (complement of legal_swaps, same row-major order) + the startup-only M3TE_BADSWAP=n capture hook; mirrors M3TE_FX. - tests/easing.sx: append bounce-envelope assertions (endpoints, single lunge peak + location, damped settle); regenerate expected snapshot. - README.md: document the M3TE_BADSWAP recipe + goldens/p16_badswap.png. Gate green: ios-sim build links, 22 logic snapshots pass (anim_plan model invariants unchanged; SWAP_ANIM_DUR untouched so cascade-cue snapshots do not churn).
This commit is contained in:
@@ -113,6 +113,34 @@ main :: () -> s32 {
|
||||
if !sq_two_sided { fails += 1; }
|
||||
if !sq_bounded { fails += 1; }
|
||||
|
||||
// 5. Illegal-swap bounce-back (P16.2): the springy lunge-and-settle render_swap
|
||||
// plays for a REJECTED swap. Lock its envelope end to end — rests at BOTH
|
||||
// ends (f(0)=f(1)=0, so the move stays purely visual), a SINGLE lunge peak of
|
||||
// exactly BADSWAP_LUNGE_AMP at BADSWAP_LUNGE_T, then a damped settle that
|
||||
// overshoots past rest by a BOUNDED amount and leaves NO residual at t=1.
|
||||
print("== illegal-swap bounce ==\n");
|
||||
bb_ends := bad_swap_bounce(0.0) == 0.0 and bad_swap_bounce(1.0) == 0.0;
|
||||
bb_mx : f32 = 0.0; bb_mx_t : f32 = 0.0; bb_mn : f32 = 0.0;
|
||||
for 0..101: (i) {
|
||||
t := cast(f32) i / 100.0;
|
||||
v := bad_swap_bounce(t);
|
||||
if v > bb_mx { bb_mx = v; bb_mx_t = t; }
|
||||
if v < bb_mn { bb_mn = v; }
|
||||
}
|
||||
bb_peak_amp := approx(bb_mx, BADSWAP_LUNGE_AMP);
|
||||
bb_peak_loc := fabs(bb_mx_t - BADSWAP_LUNGE_T) < 0.011;
|
||||
bb_overshoots := bb_mn < -0.01; // springs PAST rest after the peak
|
||||
bb_overshoot_bounded := bb_mn > -0.12; // but the recoil stays tasteful
|
||||
bb_settles := approx(bad_swap_bounce(1.0), 0.0); // no residual displacement
|
||||
print("bounce_ends {} peak_amp {} peak_loc {} overshoots {} overshoot_bounded {} settles {}\n",
|
||||
bb_ends, bb_peak_amp, bb_peak_loc, bb_overshoots, bb_overshoot_bounded, bb_settles);
|
||||
if !bb_ends { fails += 1; }
|
||||
if !bb_peak_amp { fails += 1; }
|
||||
if !bb_peak_loc { fails += 1; }
|
||||
if !bb_overshoots { fails += 1; }
|
||||
if !bb_overshoot_bounded { fails += 1; }
|
||||
if !bb_settles { fails += 1; }
|
||||
|
||||
if fails == 0 {
|
||||
print("ok: easing toolkit endpoints locked + amplitudes bounded\n");
|
||||
return 0;
|
||||
|
||||
@@ -6,4 +6,6 @@ ease_in true ease_in_out true ease_out_cubic true ease_in_quad true
|
||||
back_overshoots true back_bounded true spring_overshoots true spring_bounded true spring_wobbles true
|
||||
== squash envelope bounded ==
|
||||
squash_moves true squash_two_sided true squash_bounded true
|
||||
== illegal-swap bounce ==
|
||||
bounce_ends true peak_amp true peak_loc true overshoots true overshoot_bounded true settles true
|
||||
ok: easing toolkit endpoints locked + amplitudes bounded
|
||||
|
||||
Reference in New Issue
Block a user