code wiki / _hdl_build / nx_quad_gaits_gate.nx
nx_quad_gaits_gate.nx source
↩ module page · 169 lines · 9658 B
1// nx_quad_gaits_gate.nx -- DIVERSIFY the animal motion: walk/trot/gallop as data on the quadruped rig, plus a
2// HORNED creature composition (quadruped + swap slots). Proves:
3// T1 the 3 gaits are DISTINCT (FR-foot phase differs: walk=diagonal-antiphase, gallop=rotary-near-phase)
4// T2 each gait produces MOTION (rendered frame checksums differ)
5// T3 horns compose (quad_add_horns adds parts on the head)
6// T4 valid APNGs written (gallop + a horned trot)
7// Emits knowledge/synth_gallop.png + knowledge/synth_horncreature.png. license_tier: ORIGINAL expect_exit: 0
8import "nx_syscalls.nx"
9import "nx_quadruped.nx"
10import "nx_apng.nx"
11import "nx_gate_verdict.nx"
12
13// MIGRATED off a hand-rolled verdict 2026-08-25. It printed "QUAD-GAITS-GATE 4/4 verdict=GREEN" itself, so
14// /api/promote refused it D001 and it had NEVER BEEN PROMOTED -- 128 KB of compiled proof that nothing could
15// run and nothing could read. Per-tooth gv_check also makes declared == executed by construction.
16// ⚠ QG_DISTINCT_MIN is INHERITED, NOT DERIVED: the original compared the walk-vs-gallop FR-foot phase gap
17// against a bare 150 with no stated basis. It is named here so it is visible and greppable rather than
18// buried in an expression, and it is flagged as owed: the honest bar is a measured separation from the rig
19// geometry, not a number someone liked. Naming it is not deriving it and this comment does not pretend it is.
20const QG_DISTINCT_MIN: i64 = 150
21// Fixed-point scales. These are not tunables at all -- they are the units the surrounding arithmetic
22// is written in, and changing either silently rescales geometry rather than adjusting a behaviour.
23const QG_TRIG_Q12: i64 = 4096 // MUST match it_sin4096/it_cos4096's output scale
24const QG_Q10_ONE: i64 = 1024 // q10 one, the scale the camera distance is expressed in
25const QG_CAM_UNITS: i64 = 5 // camera standoff, in world units
26// A CHECKSUM SEED IS ARBITRARY BY DESIGN, AND THAT IS EXACTLY WHY IT NEEDS A PURPOSE NAME. Any seed
27// produces a valid checksum, so there is no "correct" value to look up and no citation to chase -- the
28// only thing a reader needs to know is what it is FOR. Naming it for its digits would hide that the
29// arbitrariness here is legitimate, which is the opposite of the material-constant case where naming
30// for the digits hid that the arbitrariness was a defect.
31const QG_CKSUM_SEED: i64 = 1469598103
32
33func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
34func pn(v: i64) -> i64 { let b: *u8=sys_mmap(32) as *u8; var x: i64=v; var ng: i64=0; if x<0{ng=1;x=0-x} var i: i64=31; if x==0{b[i]=48 as u8;i=i-1} while x>0{b[i]=(48+x%10) as u8;x=x/10;i=i-1} if ng==1{b[i]=45 as u8;i=i-1} sys_write(1,(b as i64+i+1) as *u8,31-i); return 0 }
35func cks(fb: *i64, n: i64) -> i64 { var s: i64=QG_CKSUM_SEED; var i: i64=0; while i<n{s=(s*31+(fb[i]&0xffffff))&0x0fffffffffffffff;i=i+1} return s }
36func find4(buf: *u8, n: i64, a: i64, b: i64, c: i64, d: i64) -> i64 { var i: i64=0; while i+4<=n{ if (buf[i]&0xff)==a{ if (buf[i+1]&0xff)==b{ if (buf[i+2]&0xff)==c{ if (buf[i+3]&0xff)==d{return i}}}} i=i+1} return 0-1 }
37
38const RW: i64 = 144
39const RH: i64 = 96
40const RFOCAL: i64 = 164
41func gr_render(base: i64, fb: *i64, yaw: i64) -> i64 {
42 let R: i64 = QG_CAM_UNITS * QG_Q10_ONE
43 let cy4: i64 = it_cos4096(yaw); let sy4: i64 = it_sin4096(yaw)
44 let rox: i64 = 0 - sy4 * R / QG_TRIG_Q12; let roz: i64 = 0 - cy4 * R / QG_TRIG_Q12
45 var y: i64 = 0
46 while y < RH {
47 let mc1: i64 = 24 + y*30/RH; let mc2: i64 = 26 + y*28/RH; let mc3: i64 = 40 + y*26/RH
48 let misscol: i64 = mc1 + mc2*256 + mc3*65536
49 var x: i64 = 0
50 while x < RW {
51 let ndcx: i64 = (2*x+1-RW)*1024/(2*RFOCAL); let ndcy: i64 = (RH-(2*y+1))*1024/(2*RFOCAL)
52 let rl: i64 = sdf_isqrt(ndcx*ndcx + ndcy*ndcy + 1024*1024)
53 let vdx: i64 = ndcx*1024/rl; let rdy: i64 = ndcy*1024/rl; let vdz: i64 = 1024*1024/rl
54 let rdx: i64 = (cy4*vdx + sy4*vdz)/QG_TRIG_Q12; let rdz: i64 = (0-sy4*vdx + cy4*vdz)/QG_TRIG_Q12
55 let col: i64 = sdf_shade_ray(base, rox, 0, roz, rdx, rdy, rdz, 176, 158, 138, misscol)
56 fb[y*RW+x] = col
57 x = x + 1
58 }
59 y = y + 1
60 }
61 return 0
62}
63// Pose to a gait's peak, then ask the RIG for the swing. quad_leg_swing lives in nx_quadruped because two
64// gates needed it; this gate no longer carries its own copy of what a leg swing is.
65func leg_sig(sk: i64, gait: i64, foot: i64, hip: i64) -> i64 {
66 quad_gait_pose_g(sk, 1024, gait); sk_update(sk)
67 return quad_leg_swing(sk, foot, hip)
68}
69// FR foot swing signature -- delegates, so there is one signature function and not two.
70func fr_sig(sk: i64, gait: i64) -> i64 { return leg_sig(sk, gait, QR_FRFOOT, QR_FRHIP) }
71
72func main() -> i64 {
73 let ctr: *i64 = gv_ctr()
74 gv_head("nx_quad_gaits_gate -- walk/trot/gallop as data + horned creature" as *u8)
75 let base: i64 = sys_mmap(sdf_bytes()) as i64
76 let fb: *i64 = sys_mmap(RW*RH*8) as *i64
77 let sk: i64 = sys_mmap(sk_bytes()) as i64
78 quad_build(sk)
79
80 // T1 gaits distinct via FR-foot phase
81 let gw: i64 = fr_sig(sk, 0)
82 let gt: i64 = fr_sig(sk, 1)
83 let gg: i64 = fr_sig(sk, 2)
84 hw(" FR-foot swing @peak: walk="); pn(gw); hw(" trot="); pn(gt); hw(" gallop="); pn(gg); hw("\n" as *u8)
85 var d_wg: i64 = gw - gg; if d_wg < 0 { d_wg = 0 - d_wg }
86 var t1: i64 = 0
87 if d_wg > QG_DISTINCT_MIN { t1 = 1 }
88 // NAME NARROWED 2026-08-25. This tooth was called "the three gaits are DISTINCT" and tests exactly
89 // ONE PAIR. Measured on its first run ever: walk=-146 trot=-156 gallop=400 -- walk and trot are TEN
90 // APART on this signature, so the unqualified name was asserting something the tooth never checked.
91 gv_check("T1 WALK and GALLOP separate on front-right foot swing (this pair only -- see T5)" as *u8, t1, ctr)
92
93 // T5 THE PAIR T1 CANNOT SEE. A single foot's peak swing cannot tell a walk from a trot, because the
94 // difference between them is not how far one leg reaches -- it is WHICH LEGS MOVE TOGETHER. A trot is
95 // 2-beat with DIAGONAL pairs in phase; a walk is a 4-beat lateral sequence where they are not. So the
96 // discriminator is the sign relationship between the front-right and its diagonal, the hind-left.
97 // ASKS THE RIG, does not re-decide. quad_diag_phase is in nx_quadruped precisely because
98 // nx_wolf_walk_gate T3 already tested this same diagonal relationship -- two gates, one question.
99 quad_gait_pose_g(sk, 1024, 1); sk_update(sk)
100 let dp_trot: i64 = quad_diag_phase(sk)
101 quad_gait_pose_g(sk, 1024, 0); sk_update(sk)
102 let dp_walk: i64 = quad_diag_phase(sk)
103 hw(" diagonal FR/HL coupling @peak: trot="); pn(dp_trot); hw(" walk="); pn(dp_walk)
104 hw(" (+1 together, -1 opposed, 0 not swinging)\n" as *u8)
105 var t5: i64 = 0
106 if dp_trot == 1 { if dp_walk != 1 { t5 = 1 } }
107 gv_check("T5 WALK and TROT differ in DIAGONAL PHASE (trot moves FR+HL together, walk does not)" as *u8, t5, ctr)
108
109 // T3 horns compose
110 quad_gait_pose_g(sk, 0, 0); sk_update(sk)
111 let nbody: i64 = quadpose_fill(base, sk)
112 let nhorn: i64 = quad_add_horns(base, nbody, sk)
113 hw(" parts: body="); pn(nbody); hw(" with-horns="); pn(nhorn); hw("\n" as *u8)
114 var t3: i64 = 0
115 if nhorn == nbody + 2 { t3 = 1 }
116 gv_check("T3 horns COMPOSE onto the quadruped (two parts added on the head)" as *u8, t3, ctr)
117
118 // T2+T4 render gallop -> APNG, motion present
119 let apbuf: *u8 = sys_mmap(12*1024*1024)
120 let seq: *i64 = sys_mmap(8) as *i64
121 let nf: i64 = 8
122 var sum0: i64 = 0; var summid: i64 = 0
123 seq[0] = 0
124 var o: i64 = apng_open(apbuf, RW, RH, nf)
125 var f: i64 = 0
126 while f < nf {
127 quad_gait_pose_g(sk, f*QG_TRIG_Q12/nf, 2); sk_update(sk)
128 quadpose_fill(base, sk)
129 gr_render(base, fb, 220)
130 if f == 0 { sum0 = cks(fb, RW*RH) }
131 if f == nf/2 { summid = cks(fb, RW*RH) }
132 var first: i64 = 0; if f == 0 { first = 1 }
133 o = apng_frame(apbuf, o, seq, fb, RW, RH, 6, first)
134 f = f + 1
135 }
136 o = apng_close(apbuf, o)
137 let fd: i64 = sys_openat_wr("knowledge/synth_gallop.png" as *u8, 0x1a4)
138 if fd >= 0 { sys_write(fd, apbuf, o); sys_close(fd) }
139 hw(" wrote knowledge/synth_gallop.png bytes="); pn(o); hw("\n" as *u8)
140 var t2: i64 = 0
141 if sum0 != summid { t2 = 1 }
142 gv_check("T2 the gallop produces MOTION (rendered frame checksums differ across the cycle)" as *u8, t2, ctr)
143 var t4: i64 = 1
144 if find4(apbuf, o, 97, 99, 84, 76) < 0 { t4 = 0 }
145
146 // horned trot render
147 seq[0] = 0
148 var o2: i64 = apng_open(apbuf, RW, RH, nf)
149 f = 0
150 while f < nf {
151 quad_gait_pose_g(sk, f*QG_TRIG_Q12/nf, 1); sk_update(sk)
152 let nb: i64 = quadpose_fill(base, sk)
153 quad_add_horns(base, nb, sk)
154 gr_render(base, fb, 220)
155 var first2: i64 = 0; if f == 0 { first2 = 1 }
156 o2 = apng_frame(apbuf, o2, seq, fb, RW, RH, 6, first2)
157 f = f + 1
158 }
159 o2 = apng_close(apbuf, o2)
160 let fd2: i64 = sys_openat_wr("knowledge/synth_horncreature.png" as *u8, 0x1a4)
161 if fd2 >= 0 { sys_write(fd2, apbuf, o2); sys_close(fd2) }
162 hw(" wrote knowledge/synth_horncreature.png bytes="); pn(o2); hw("\n" as *u8)
163 if find4(apbuf, o2, 97, 99, 84, 76) < 0 { t4 = 0 }
164 gv_check("T4 both emitted APNGs are VALID (the acTL animation chunk is present in each)" as *u8, t4, ctr)
165
166 return gv_verdict("QUAD-GAITS" as *u8, ctr,
167 "walk/trot/gallop as data on one quadruped rig, plus a horned composition -- sovereign, and readable from outside for the first time" as *u8)
168}
169