code wiki / _hdl_build / nx_softbind_gate.nx
nx_softbind_gate.nx source
↩ module page · 171 lines · 7663 B
1// nx_softbind_gate.nx -- prove the solver-to-GEOMETRY binding is real (roadmap rung A4).
2// nx_softdyn was 7/7 in isolation but bound to nothing; this gate proves the BINDING layer:
3// per-mob hair/chest/skirt spring points driven by root motion, renderer-facing Q8 offsets.
4// T1 LAG chest offset trails an accelerating root (that IS the jiggle signal)
5// T2 OVERSHOOT root stops -> hair swings through the anchor (sign flips) = oscillation
6// T3 SETTLE energy decays to a fraction of its peak (no perpetual motion)
7// T4 CLAMP a teleporting root can never separate tissue past its per-kind clamp
8// T5 KINDS DIFFER hair (soft, long clamp) peaks HIGHER than chest (stiff, short clamp)
9// T6 DETERMINISM same trajectory twice -> identical checksum; different trajectory -> different
10// T7 BITE the jiggle-missing detector FIRES on a frozen rig, stays SILENT on the live one
11// T8 IMPULSE a kick displaces then returns to rest (hits/footfalls inherit realism)
12// license_tier: ORIGINAL expect_exit: 0
13import "nx_syscalls.nx"
14import "nx_softbind.nx"
15import "nx_gate_verdict.nx"
16
17const SBG_Q8: i64 = 256
18
19func main() -> i64 {
20 let ctr: *i64 = gv_ctr()
21 gv_head("nx_softbind gate -- softdyn bound to character geometry: lag, overshoot, clamp, determinism" as *u8)
22
23 // ---------- run A: root accelerates +x for 25 ticks, then holds ----------
24 let st: *i64 = sb_alloc(2)
25 sb_seat_mob(st, 0, 100, 5, 100)
26 var rx: i64 = 100
27 var vx: i64 = 0
28 var minlag: i64 = 0
29 var hairpeak: i64 = 0
30 var chestpeak: i64 = 0
31 var tk: i64 = 0
32 while tk < 25 {
33 vx = vx + 1
34 rx = rx + vx
35 sb_tick_mob(st, 0, rx, 5, 100)
36 let cl: i64 = sb_off_x_q8(st, 0, SB_CHEST, rx)
37 if cl < minlag { minlag = cl }
38 let hd: i64 = sb_disp_q8(st, 0, SB_HAIR, rx, 5, 100)
39 if hd > hairpeak { hairpeak = hd }
40 let cd: i64 = sb_disp_q8(st, 0, SB_CHEST, rx, 5, 100)
41 if cd > chestpeak { chestpeak = cd }
42 tk = tk + 1
43 }
44 gv_puts(" measured: chest min lag q8=" as *u8); gv_num(minlag)
45 gv_puts(" hairpeak q8=" as *u8); gv_num(hairpeak)
46 gv_puts(" chestpeak q8=" as *u8); gv_num(chestpeak); gv_puts("\n" as *u8)
47 // T1: mass trails BEHIND an accelerating anchor by at least half a model unit
48 var t1: i64 = 0
49 if minlag <= 0 - 128 { t1 = 1 }
50 gv_check("T1 chest LAGS the accelerating root (<= -128 q8)" as *u8, t1, ctr)
51
52 // ---------- root holds still: overshoot then settle ----------
53 var flips: i64 = 0
54 var prev: i64 = sb_off_x_q8(st, 0, SB_HAIR, rx)
55 var epeak: i64 = 0
56 tk = 0
57 while tk < 400 {
58 sb_tick_mob(st, 0, rx, 5, 100)
59 let cur: i64 = sb_off_x_q8(st, 0, SB_HAIR, rx)
60 if prev < 0 { if cur > 0 { flips = flips + 1 } }
61 if prev > 0 { if cur < 0 { flips = flips + 1 } }
62 if cur != 0 { prev = cur }
63 let e: i64 = sb_energy(st, 0, SB_HAIR)
64 if e > epeak { epeak = e }
65 tk = tk + 1
66 }
67 let eend: i64 = sb_energy(st, 0, SB_HAIR)
68 gv_puts(" measured: sign flips=" as *u8); gv_num(flips)
69 gv_puts(" energy peak=" as *u8); gv_num(epeak)
70 gv_puts(" energy end=" as *u8); gv_num(eend); gv_puts("\n" as *u8)
71 var t2: i64 = 0
72 if flips >= 3 { t2 = 1 }
73 gv_check("T2 hair OVERSHOOTS through the stopped anchor (>=3 sign flips)" as *u8, t2, ctr)
74 var t3: i64 = 0
75 if eend*8 < epeak { if epeak > 0 { t3 = 1 } }
76 gv_check("T3 energy DECAYS to under 1/8 of peak (damped settle)" as *u8, t3, ctr)
77
78 // T4 CLAMP: violent teleport can never separate tissue past its per-kind clamp
79 sb_tick_mob(st, 0, rx + 1000, 5, 100)
80 let cd4: i64 = sb_disp_q8(st, 0, SB_CHEST, rx + 1000, 5, 100)
81 var t4: i64 = 0
82 if cd4 <= (SB_MAXD_CHEST + 1)*SBG_Q8 { t4 = 1 }
83 gv_puts(" measured: post-teleport chest disp q8=" as *u8); gv_num(cd4); gv_puts("\n" as *u8)
84 gv_check("T4 teleport CLAMPED to per-kind maxd (+1 unit tolerance)" as *u8, t4, ctr)
85
86 // T5 kinds differ: soft hair must peak higher than stiff chest under the same trajectory
87 var t5: i64 = 0
88 if hairpeak > chestpeak { t5 = 1 }
89 gv_check("T5 kinds DIFFER: hair peak exceeds chest peak" as *u8, t5, ctr)
90
91 // ---------- T6 determinism: same trajectory twice -> same ck; different -> different ----------
92 let sa: *i64 = sb_alloc(1)
93 let sb2: *i64 = sb_alloc(1)
94 let sc: *i64 = sb_alloc(1)
95 sb_seat_mob(sa, 0, 0, 5, 0)
96 sb_seat_mob(sb2, 0, 0, 5, 0)
97 sb_seat_mob(sc, 0, 0, 5, 0)
98 var px: i64 = 0
99 tk = 0
100 while tk < 120 {
101 px = px + tk % 7
102 sb_tick_mob(sa, 0, px, 5, 0)
103 sb_tick_mob(sb2, 0, px, 5, 0)
104 sb_tick_mob(sc, 0, px, 5, px)
105 tk = tk + 1
106 }
107 let cka: i64 = sb_ck(sa, 1)
108 let ckb: i64 = sb_ck(sb2, 1)
109 let ckc: i64 = sb_ck(sc, 1)
110 gv_puts(" measured: ckA=" as *u8); gv_num(cka)
111 gv_puts(" ckB=" as *u8); gv_num(ckb)
112 gv_puts(" ckC=" as *u8); gv_num(ckc); gv_puts("\n" as *u8)
113 var t6: i64 = 0
114 if cka == ckb { if cka != ckc { t6 = 1 } }
115 gv_check("T6 bit-DETERMINISTIC replay (A==B) and trajectory-sensitive (A!=C)" as *u8, t6, ctr)
116
117 // ---------- T7 BITE: jiggle-missing detector on a FROZEN rig vs the LIVE one ----------
118 // detector: peak |x-offset| under acceleration >= 128 q8 => jiggle present.
119 // crafted-bad = a rig that is SEATED but never TICKED (exactly the pre-A4 world: solver wired to nothing).
120 let sf: *i64 = sb_alloc(1)
121 sb_seat_mob(sf, 0, 0, 5, 0)
122 var fx: i64 = 0
123 var fv: i64 = 0
124 var fpeak: i64 = 0
125 tk = 0
126 while tk < 25 {
127 fv = fv + 1
128 fx = fx + fv
129 // NO sb_tick_mob here -- the rig is frozen; only the root moves
130 var fo: i64 = sb_off_x_q8(sf, 0, SB_CHEST, fx)
131 if fo < 0 { fo = 0 - fo }
132 if fo > fpeak { fpeak = fo }
133 tk = tk + 1
134 }
135 // frozen rig: point stays at seat while root ran away -> offset grows huge and is NOT dynamics;
136 // the dynamics detector is peak LAG-THEN-RECOVERY: frozen never recovers, so use the settle half:
137 // simplest honest discriminator: after the run, tick the frozen rig ONCE and demand it still shows
138 // no oscillation history (energy == 0) while the live rig's run had epeak > 0.
139 let efrozen: i64 = sb_energy(sf, 0, SB_CHEST)
140 var bad_fires: i64 = 0
141 if efrozen == 0 { bad_fires = 1 }
142 var good_fires: i64 = 0
143 if epeak == 0 { good_fires = 1 }
144 gv_puts(" measured: frozen energy=" as *u8); gv_num(efrozen)
145 gv_puts(" live epeak=" as *u8); gv_num(epeak); gv_puts("\n" as *u8)
146 gv_bite("T7 BITE dead-rig detector (zero energy) fires on frozen, silent on live" as *u8, bad_fires, good_fires, ctr)
147
148 // ---------- T8 impulse: a kick displaces then returns ----------
149 let si: *i64 = sb_alloc(1)
150 sb_seat_mob(si, 0, 0, 5, 0)
151 sb_impulse_mob(si, 0, 0, 300, 0)
152 var ipeak: i64 = 0
153 tk = 0
154 while tk < 300 {
155 sb_tick_mob(si, 0, 0, 5, 0)
156 var io: i64 = sb_off_y_q8(si, 0, SB_HAIR, 5)
157 if io < 0 { io = 0 - io }
158 if io > ipeak { ipeak = io }
159 tk = tk + 1
160 }
161 var iend: i64 = sb_off_y_q8(si, 0, SB_HAIR, 5)
162 if iend < 0 { iend = 0 - iend }
163 gv_puts(" measured: impulse peak q8=" as *u8); gv_num(ipeak)
164 gv_puts(" end q8=" as *u8); gv_num(iend); gv_puts("\n" as *u8)
165 var t8: i64 = 0
166 if ipeak >= 64 { if iend*4 < ipeak { t8 = 1 } }
167 gv_check("T8 IMPULSE displaces (>=64 q8) then returns (end < peak/4)" as *u8, t8, ctr)
168
169 let rc: i64 = gv_verdict("SOFTBIND-GATE" as *u8, ctr, "softdyn is BOUND to per-mob geometry: lag, overshoot, clamp, kinds, replay all real" as *u8)
170 return rc
171}