code wiki / _hdl_build / nx_wasm_physlab_gate.nx
nx_wasm_physlab_gate.nx source
↩ module page · 141 lines · 6249 B
1// nx_wasm_physlab_gate.nx -- gate for PHYSLAB (the visible consumer of phys3d+softjiggle; reuse-census W2).
2// T1 the crate stack settles and SLEEPS in the composed world; spheres come to rest
3// T2 the chain follows a moving mouse anchor with jiggle lag (never rigid-locked to it)
4// T3 the chain dragged across the pillar NEVER penetrates the capsule
5// T4 determinism: two labs, same scripted mouse -> byte-identical physics + jiggle state
6// T5 render: frame draws (nonblack majority) + PNG knowledge/nx_physlab.png (the eyeball)
7// T6 click-spawn adds a crate; it lands and eventually sleeps
8// license_tier: ORIGINAL expect_exit: 0
9import "nx_syscalls.nx"
10import "nx_png.nx"
11import "nx_wasm_physlab.nx"
12
13func hw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
14func pn(v: i64) -> i64 {
15 let b: *u8 = sys_mmap(32) as *u8
16 var x: i64 = v
17 var neg: i64 = 0
18 if x < 0 { neg = 1; x = 0 - x }
19 var i: i64 = 31
20 if x == 0 { b[i] = 48 as u8; i = i - 1 }
21 while x > 0 { b[i] = (48 + x % 10) as u8; x = x / 10; i = i - 1 }
22 if neg == 1 { b[i] = 45 as u8; i = i - 1 }
23 sys_write(1, (b as i64 + i + 1) as *u8, 31 - i)
24 return 0
25}
26func gabs(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
27func isq(v: i64) -> i64 { if v <= 0 { return 0 } var x: i64 = v; var y: i64 = (x + 1) / 2; while y < x { x = y; y = (x + v / x) / 2 } return x }
28
29func main() -> i64 {
30 var fails: i64 = 0
31
32 // T1 settle + sleep (mouse parked far away, no clicks)
33 let L: i64 = sys_mmap(mem_bytes()) as i64
34 start_at(L)
35 var t: i64 = 0
36 while t < 900 { tick_at(L, 40, 40, 0); t = t + 1 }
37 let bodies: i64 = body_count_at(L)
38 let asleep: i64 = asleep_count_at(L)
39 var t1: i64 = 1
40 if bodies != 8 { t1 = 0 } // ground + 5 crates + 2 spheres
41 if asleep != 7 { t1 = 0 } // all 7 dynamics asleep
42 if t1 == 1 { hw("T1 PASS composed world settles: 7/7 dynamic bodies ASLEEP after 900 ticks (crates stacked, spheres rested)\n" as *u8) }
43 else { hw("T1 FAIL bodies=" as *u8); pn(bodies); hw(" asleep=" as *u8); pn(asleep); hw("\n" as *u8); fails = fails + 1 }
44
45 // T2 chain lag: sweep the mouse; the chain tip must trail the anchor (jiggle, not rigid)
46 var lagticks: i64 = 0
47 t = 0
48 while t < 120 {
49 tick_at(L, 60 + t * 2, 60, 0)
50 let tip: *i64 = sj_node(L + 721024, 4)
51 let st2: *i64 = (L + 739584) as *i64
52 if gabs(tip[0] - st2[4]) > 200 { lagticks = lagticks + 1 }
53 t = t + 1
54 }
55 if lagticks > 60 { hw("T2 PASS chain trails the moving anchor (lag on " as *u8); pn(lagticks); hw("/120 sweep ticks = secondary motion, not rigid)\n" as *u8) }
56 else { hw("T2 FAIL lagticks=" as *u8); pn(lagticks); hw("\n" as *u8); fails = fails + 1 }
57
58 // T3 drag the chain across the pillar: nodes never inside the capsule (r=300 at x=1800, y 0..1500)
59 var minr: i64 = 99999
60 t = 0
61 while t < 260 {
62 var mx: i64 = 260 - t // sweep the mouse right-to-left across the pillar
63 tick_at(L, mx, 130, 0)
64 var c: i64 = 0
65 while c < 5 {
66 let n: *i64 = sj_node(L + 721024, c)
67 if n[1] >= 0 { if n[1] <= 1500 {
68 let dx: i64 = n[0] - 1800
69 let dz: i64 = n[2]
70 let d: i64 = isq(dx * dx + dz * dz)
71 if d < minr { minr = d }
72 } }
73 c = c + 1
74 }
75 t = t + 1
76 }
77 if minr >= 296 { hw("T3 PASS chain dragged across the pillar: min node distance " as *u8); pn(minr); hw("/300 -- capsule NEVER penetrated\n" as *u8) }
78 else { hw("T3 FAIL minr=" as *u8); pn(minr); hw("\n" as *u8); fails = fails + 1 }
79
80 // T4 determinism: two fresh labs, same script
81 let A: i64 = sys_mmap(mem_bytes()) as i64
82 let B: i64 = sys_mmap(mem_bytes()) as i64
83 start_at(A)
84 start_at(B)
85 t = 0
86 while t < 400 {
87 var mx2: i64 = 80 + (t * 3) % 160
88 var bt: i64 = 0
89 if (t % 97) == 50 { bt = 1 }
90 tick_at(A, mx2, 90, bt)
91 tick_at(B, mx2, 90, bt)
92 t = t + 1
93 }
94 var diff: i64 = 0
95 var k: i64 = 0
96 while k < 8256 / 8 {
97 let pa: *i64 = (A + 614400) as *i64
98 let pb: *i64 = (B + 614400) as *i64
99 if pa[k] != pb[k] { diff = diff + 1 }
100 k = k + 1
101 }
102 k = 0
103 while k < 18496 / 8 {
104 let sa: *i64 = (A + 721024) as *i64
105 let sb: *i64 = (B + 721024) as *i64
106 if sa[k] != sb[k] { diff = diff + 1 }
107 k = k + 1
108 }
109 if diff == 0 { hw("T4 PASS determinism: 400 scripted ticks incl clicks -> physics+jiggle state IDENTICAL\n" as *u8) }
110 else { hw("T4 FAIL diff-slots=" as *u8); pn(diff); hw("\n" as *u8); fails = fails + 1 }
111
112 // T5 render + PNG
113 render_at(A)
114 let fb: *i64 = (A + fb_off()) as *i64
115 var nonblack: i64 = 0
116 var pi: i64 = 0
117 while pi < ww() * hh() {
118 if fb[pi] != 0 { nonblack = nonblack + 1 }
119 pi = pi + 1
120 }
121 write_png(fb, ww(), hh(), "knowledge/nx_physlab.png" as *u8)
122 if nonblack * 10 >= ww() * hh() * 9 { hw("T5 PASS render draws the lab (nonblack=" as *u8); pn(nonblack); hw(") + PNG knowledge/nx_physlab.png\n" as *u8) }
123 else { hw("T5 FAIL nonblack=" as *u8); pn(nonblack); hw("\n" as *u8); fails = fails + 1 }
124
125 // T6 click-spawn on a fresh lab: body appears, then sleeps
126 let C: i64 = sys_mmap(mem_bytes()) as i64
127 start_at(C)
128 tick_at(C, 200, 80, 0)
129 tick_at(C, 200, 80, 1) // click edge
130 var t6: i64 = 1
131 if body_count_at(C) != 9 { t6 = 0 }
132 t = 0
133 while t < 900 { tick_at(C, 40, 40, 0); t = t + 1 }
134 if asleep_count_at(C) != 8 { t6 = 0 }
135 if t6 == 1 { hw("T6 PASS click spawns a crate (9 bodies) and the world re-settles ALL ASLEEP (8/8)\n" as *u8) }
136 else { hw("T6 FAIL bodies=" as *u8); pn(body_count_at(C)); hw(" asleep=" as *u8); pn(asleep_count_at(C)); hw("\n" as *u8); fails = fails + 1 }
137
138 if fails == 0 { hw("VERDICT GREEN: physlab 6/6 -- phys3d+softjiggle WIRED into a visible surface (reuse W2), deterministic, capsule-safe (PNG knowledge/nx_physlab.png)\n" as *u8) }
139 else { hw("VERDICT RED fails=" as *u8); pn(fails); hw("\n" as *u8) }
140 return fails
141}