code wiki / _hdl_build / nx_ballistics_gate.nx
nx_ballistics_gate.nx source
↩ module page · 120 lines · 8636 B
1// nx_ballistics_gate.nx -- NATIVE verify of the browser ballistics sim (base = mmap; SAME code runs in sovereign
2// WASM). Integrates projectiles with the sim's own acceleration and validates the real ballistic laws: (a) range
3// R = v^2 sin(2theta)/g is MAXIMUM at 45deg, (b) complementary angles (30/60, 15/75) give EQUAL range, (c) the
4// range curve follows sin(2theta), (d) vacuum flight is parabolic-SYMMETRIC (apex at half the flight time), (e) air
5// DRAG reduces the range. Liar-kill: a horizontal launch (theta=0) lands immediately (range ~ 0) -- vertical launch
6// is essential, the sim isn't returning a constant. license_tier: ORIGINAL expect_exit: 0
7import "nx_syscalls.nx"
8import "nx_ballistics.nx"
9import "nx_png.nx"
10
11func g_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
12func g_n(v: i64) -> i64 { var m: i64=v; if m<0{g_w("-");m=0-m} let t:*u8=sys_mmap(24); var k:i64=0; if m==0{t[0]=48 as u8;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i:i64=0; let o:*u8=sys_mmap(24); while i<k{o[i]=t[k-1-i];i=i+1}; sys_write(1,o,k); return 0 }
13func g_row(id: *u8, ok: i64, pass: *i64) -> i64 { g_w(" "); g_w(id); g_w(": "); if ok==1 { g_w("OK\n"); pass[0]=pass[0]+1 } else { g_w("FAIL\n") } return 0 }
14func iabs(x: i64) -> i64 { if x<0 { return 0-x } return x }
15
16func vx0_of(thed: i64) -> i64 { return V0*cos_fixed(thed)/SC }
17func vy0_of(thed: i64) -> i64 { return V0*sin_fixed(thed)/SC }
18
19// integrate one projectile (matching the sim's update order) and return its range (x at landing)
20func range_at(thed: i64, drag: i64) -> i64 {
21 var x: i64=0; var y: i64=0; var vx: i64=vx0_of(thed); var vy: i64=vy0_of(thed); var s: i64=0
22 while s<5000000 {
23 vx = vx + acc_x(vx,vy,drag); vy = vy + acc_y(vx,vy,drag)
24 x = x + vx; y = y + vy
25 if y<=0 { return x }
26 s=s+1
27 }
28 return x
29}
30// apex step and landing step for a vacuum projectile
31func flight(thed: i64, ap: *i64, land: *i64) -> i64 {
32 var x: i64=0; var y: i64=0; var vx: i64=vx0_of(thed); var vy: i64=vy0_of(thed); var s: i64=0; var apex: i64=0
33 while s<5000000 {
34 vx = vx + acc_x(vx,vy,0); vy = vy + acc_y(vx,vy,0); x = x + vx; y = y + vy; s=s+1
35 if apex==0 { if vy<=0 { apex=s } }
36 if y<=0 { ap[0]=apex; land[0]=s; return 0 }
37 }
38 return 0
39}
40
41func main() -> i64 {
42 let pass: *i64 = sys_mmap(8) as *i64; pass[0]=0
43 g_w("=== NX-BALLISTICS native verify (projectile firing-table physics; SAME code runs in sovereign wasm) ===\n")
44 let base: i64 = sys_mmap(2*1048576) as i64
45
46 let R15: i64=range_at(261799,0); let R30: i64=range_at(523599,0); let R45: i64=range_at(785398,0)
47 let R60: i64=range_at(1047198,0); let R75: i64=range_at(1308997,0)
48 let R0: i64=range_at(0,0) // horizontal -> ~0
49 let Rd45: i64=range_at(785398,1) // 45deg WITH drag
50 let ap: *i64=sys_mmap(8) as *i64; let ld: *i64=sys_mmap(8) as *i64
51 flight(785398, ap, ld)
52
53 g_w(" ranges(vac): R15="); g_n(R15); g_w(" R30="); g_n(R30); g_w(" R45="); g_n(R45); g_w(" R60="); g_n(R60); g_w(" R75="); g_n(R75); g_w("\n")
54 g_w(" R(0deg)="); g_n(R0); g_w(" R45(drag)="); g_n(Rd45); g_w(" flight: apex_step="); g_n(ap[0]); g_w(" land_step="); g_n(ld[0]); g_w("\n")
55 g_w(" ratios x1000: R15/R45="); g_n(R15*1000/R45); g_w(" (sin30=500) R30/R45="); g_n(R30*1000/R45); g_w(" (sin60=866)\n")
56
57 var peak: i64=1; if R45<R15 { peak=0 } if R45<R30 { peak=0 } if R45<R60 { peak=0 } if R45<R75 { peak=0 }
58 var comp: i64=0; if iabs(R30-R60)*1000/R45 < 20 { if iabs(R15-R75)*1000/R45 < 20 { comp=1 } }
59 var shape: i64=0; if iabs(R15*1000/R45 - 500) < 40 { if iabs(R30*1000/R45 - 866) < 40 { shape=1 } }
60 var sym: i64=0; if iabs(ld[0] - 2*ap[0])*1000/ld[0] < 20 { sym=1 }
61
62 // render a frame
63 init_impl(base); var t: i64=0; while t<120 { tick_impl(base, 0); t=t+1 }
64 render_impl(base)
65 write_png((base + O_FB) as *i64, W, H, "knowledge/nx_ballistics.png" as *u8)
66 let m: *u8=(base+O_FB) as *u8; var nb: i64=0; var i: i64=0
67 while i<W*H { let b: i64=i*8; if (m[b] as i64)!=12 { nb=nb+1 } else { if (m[b+1] as i64)!=16 { nb=nb+1 } else { if (m[b+2] as i64)!=26 { nb=nb+1 } } } i=i+1 }
68
69 var ran: i64=0; if R45>0 { if R15>0 { ran=1 } }
70 g_row("RUNS: ranges integrated for every launch angle (vacuum + drag)" as *u8, ran, pass)
71 g_row("MAX RANGE AT 45deg: R(45) is the maximum over 15/30/45/60/75 -- the classic optimal launch angle" as *u8, peak, pass)
72 g_row("COMPLEMENTARY ANGLES: R(30)=R(60) and R(15)=R(75) within 2% -- the real complementary-angle range symmetry" as *u8, comp, pass)
73 g_row("SIN(2theta) LAW: R(15)/R(45)~0.5 and R(30)/R(45)~0.866 within 4% -- range follows v^2 sin(2theta)/g" as *u8, shape, pass)
74 g_row("PARABOLIC SYMMETRY: vacuum flight reaches apex at half the total flight time (within 2%)" as *u8, sym, pass)
75 g_row("DRAG REDUCES RANGE: the 45deg shot with air drag falls >5% short of the vacuum shot" as *u8, (Rd45 < R45*95/100) as i64, pass)
76 g_row("LIAR-KILL: a horizontal launch (theta=0) lands at once (range << R45/20) -- vertical launch is essential" as *u8, (R0 < R45/20) as i64, pass)
77 g_row("RASTER: framebuffer drawn (ground + two trajectories) -> knowledge/nx_ballistics.png" as *u8, (nb>100) as i64, pass)
78 var iface: i64=0; if ww()==W { if hh()==H { if fb_off()==O_FB { iface=1 } } }
79 g_row("WASM-READY: init/tick/render/ww/hh/fb_off interface intact" as *u8, iface, pass)
80
81 // ---- MUTATION-DERIVED TEETH (2026-08-01, debt 1785604588) ----
82 // nx_gate_mutation_probe scored this pair 1/4 -- the WORST found so far -- with three survivors:
83 // nx_ballistics.nx:44 `if X > HALF { X = PI - X }` the sine ARGUMENT REDUCTION
84 // nx_ballistics.nx:63 the disc() rasterisation bound
85 // nx_ballistics.nx:143 mem_bytes()
86 // Every physics row above (range law, complementary angles, drag, apex symmetry) is computed from
87 // launch angles of 15..75 degrees, which after reduction never enter the X > HALF branch. So the
88 // gate exercised sine only where the mutated line does not run.
89 // LAW: TESTING A FUNCTION THROUGH ONE CALLER TESTS ONLY THE INPUTS THAT CALLER HAPPENS TO PRODUCE.
90 // Nine rows of real physics still left half the sine domain unvisited.
91 // TOOTH 1 -- an ABSOLUTE sine value in the second quadrant, NOT a symmetry.
92 // ⚠ MY FIRST ATTEMPT WAS BLIND BY CONSTRUCTION and is recorded because the failure is the lesson.
93 // I asserted sin(HALF - d) == sin(HALF + d), the very identity line 44 implements. But inverting
94 // `X > HALF` to `X < HALF` SWAPS which side gets reduced: the low argument is folded and the high
95 // one is not, so both operands take on each other's value and the equality still holds. The tooth
96 // measured a property the mutation PRESERVES.
97 // LAW: A SYMMETRY TEST IS BLIND TO A MUTATION THAT PRESERVES THE SYMMETRY -- when the defect swaps
98 // two sides, comparing those two sides cancels it. Symmetry is a strong tooth for a defect that
99 // breaks one side only (it caught the eyeball colour and the FFT imaginary half); it is the WRONG
100 // tooth when the defect exchanges the sides.
101 // A known value is required here, and sin(2 rad) = 0.909297 is a mathematical constant, not an
102 // implementation detail -- the same standing as the FIPS/RFC vectors other gates assert against.
103 // With the branch intact, 2.0 rad folds to PI-2.0 = 1.141593 and the series is accurate; inverted,
104 // the series is evaluated at 2.0 directly, outside its convergent range.
105 let s2: i64 = sin_fixed(2000000)
106 var sin_ok: i64 = 0
107 if s2 > 899297 { if s2 < 919297 { sin_ok = 1 } } // 0.909297 +/- 1%
108 g_row("SINE KAT: sin(2 rad) == 0.9093 +/-1% -- the >PI/2 reduction branch is exercised and correct" as *u8, sin_ok, pass)
109 // TOOTH 2 -- mem_bytes() must cover the LAST structure's end, O_TR + trail bytes.
110 // ⚠ My first floor (trail bytes alone = 5120) was far below O_TR (524400), so a mutation that
111 // subtracts the trail term still cleared it. A floor only tests what it is above.
112 let mb: i64 = mem_bytes()
113 var mem_ok: i64 = 0
114 if mb > (O_TR + N * TRAILN * 2 * 8) { mem_ok = 1 }
115 g_row("MEM FOOTPRINT: mem_bytes() exceeds the trail storage it must hold (a caller sizes from this)" as *u8, mem_ok, pass)
116
117 g_w("NX-BALLISTICS-GATE rows=11 pass="); g_n(pass[0])
118 if pass[0]==11 { g_w(" verdict=GREEN (real projectile ballistics; sin(2theta) range law + complementary symmetry + drag, browser-ready)\n"); sys_exit(0); return 0 }
119 g_w(" verdict=RED\n"); sys_exit(1); return 1
120}