code wiki / _hdl_build / nx_assetvariant_gate.nx

nx_assetvariant_gate.nx source

↩ module page · 219 lines · 10413 B

1// nx_assetvariant_gate.nx -- prove SWAP-AS-DATA v0: ONE part-recipe system emits MANY distinct creature/person 2// variants by swapping slots (species x head x ears x tail x build), all rendered on the SAME photoreal-path 3// shader into a 4x3 CONTACT SHEET (the DnD bestiary card). Asserts: 4// T1 every variant visibly renders (foreground coverage per tile) 5// T2 all 12 variant tiles are pairwise DISTINCT (diversity is real, not one blob) 6// T3 a HEAD-SLOT-ONLY swap changes the render (plain vs horns on the same base = the swap works) 7// T4 species swap changes the render (human vs wolf) 8// Writes knowledge/synth_variants.png (384x216 sheet). license_tier: ORIGINAL expect_exit: 0 9import "nx_syscalls.nx" 10import "nx_sdfrender.nx" 11import "nx_assetvariant.nx" 12import "nx_itrig.nx" 13import "nx_gate_verdict.nx" // D001: inherit the verdict contract rather than re-emit its strings 14 15// Row count named, not inline: T1..T5. The verdict maps fails onto the shared contract as 16// passed = AV_GATE_ROWS - fails, so adding a row means moving ONE constant, not two call sites. 17const AV_GATE_ROWS: i64 = 5 18import "nx_png.nx" 19import "nx_critvis_lib.nx" 20 21func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 22func pn(v: i64) -> i64 { 23 let b: *u8 = sys_mmap(32) as *u8 24 var x: i64 = v; var neg: i64 = 0 25 if x < 0 { neg = 1; x = 0 - x } 26 var i: i64 = 31 27 if x == 0 { b[i] = 48 as u8; i = i - 1 } 28 while x > 0 { b[i] = (48 + x % 10) as u8; x = x / 10; i = i - 1 } 29 if neg == 1 { b[i] = 45 as u8; i = i - 1 } 30 sys_write(1, (b as i64 + i + 1) as *u8, 31 - i) 31 return 0 32} 33 34const SW: i64 = 384 35const SH: i64 = 216 36const TW: i64 = 96 37const TH: i64 = 72 38const TFOCAL: i64 = 109 // 586 * 96 / 512 (same FOV as production) 39 40// render one variant tile into the sheet fb at (tx0,ty0), single ray/px through the production shader 41func tile_render(base: i64, fb: *i64, tx0: i64, ty0: i64) -> i64 { 42 let yaw: i64 = 470 // ~15deg orbit 43 let cy4: i64 = it_cos4096(yaw) 44 let sy4: i64 = it_sin4096(yaw) 45 let R: i64 = 5 * 1024 46 let rox: i64 = 0 - sy4 * R / 4096 47 let roz: i64 = 0 - cy4 * R / 4096 48 var y: i64 = 0 49 while y < TH { 50 let mc1: i64 = 24 + y * 30 / TH 51 let mc2: i64 = 26 + y * 28 / TH 52 let mc3: i64 = 40 + y * 26 / TH 53 let misscol: i64 = mc1 + mc2 * 256 + mc3 * 65536 54 var x: i64 = 0 55 while x < TW { 56 let ndcx: i64 = (2 * x + 1 - TW) * 1024 / (2 * TFOCAL) 57 let ndcy: i64 = (TH - (2 * y + 1)) * 1024 / (2 * TFOCAL) 58 let rl: i64 = sdf_isqrt(ndcx * ndcx + ndcy * ndcy + 1024 * 1024) 59 let vdx: i64 = ndcx * 1024 / rl 60 let rdy: i64 = ndcy * 1024 / rl 61 let vdz: i64 = 1024 * 1024 / rl 62 let rdx: i64 = (cy4 * vdx + sy4 * vdz) / 4096 63 let rdz: i64 = (0 - sy4 * vdx + cy4 * vdz) / 4096 64 let col: i64 = sdf_shade_ray(base, rox, 0, roz, rdx, rdy, rdz, 240, 184, 160, misscol) 65 let idx: i64 = (ty0 + y) * SW + tx0 + x 66 fb[idx] = col 67 x = x + 1 68 } 69 y = y + 1 70 } 71 return 0 72} 73func tile_cksum(fb: *i64, tx0: i64, ty0: i64) -> i64 { 74 var s: i64 = 1469598103 75 var y: i64 = 0 76 while y < TH { 77 var x: i64 = 0 78 while x < TW { 79 let px: i64 = fb[(ty0 + y) * SW + tx0 + x] 80 s = (s * 31 + (px & 0xffffff)) & 0x0fffffffffffffff 81 x = x + 1 82 } 83 y = y + 1 84 } 85 return s 86} 87func tile_fg(fb: *i64, tx0: i64, ty0: i64) -> i64 { 88 var c: i64 = 0 89 var y: i64 = 0 90 while y < TH { 91 var x: i64 = 0 92 while x < TW { 93 let px: i64 = fb[(ty0 + y) * SW + tx0 + x] 94 let w: i64 = cv_pl(px) 95 c = c + w 96 x = x + 1 97 } 98 y = y + 1 99 } 100 return c 101} 102 103func main() -> i64 { 104 hw("=== nx_assetvariant_gate -- one recipe system, 12 creature/person variants via SWAP SLOTS ===\n" as *u8) 105 let base: i64 = sys_mmap(sdf_bytes()) as i64 106 let fb: *i64 = sys_mmap(SW * SH * 8) as *i64 107 // variant tuples {sp, hv, ev, tv, build} 108 let vt: *i64 = sys_mmap(12 * 5 * 8) as *i64 109 var q: i64 = 0 110 vt[q]=0; vt[q+1]=0; vt[q+2]=0; vt[q+3]=0; vt[q+4]=100; q=q+5 // 0 human plain 111 vt[q]=0; vt[q+1]=1; vt[q+2]=0; vt[q+3]=0; vt[q+4]=100; q=q+5 // 1 human HORNS (head-swap-only vs 0) 112 vt[q]=0; vt[q+1]=2; vt[q+2]=1; vt[q+3]=1; vt[q+4]=100; q=q+5 // 2 beastman (snout+ears+tail) 113 vt[q]=0; vt[q+1]=0; vt[q+2]=0; vt[q+3]=0; vt[q+4]=125; q=q+5 // 3 human BULK 114 vt[q]=0; vt[q+1]=0; vt[q+2]=1; vt[q+3]=0; vt[q+4]=80; q=q+5 // 4 elf (pointy ears, slim) 115 vt[q]=2; vt[q+1]=0; vt[q+2]=1; vt[q+3]=0; vt[q+4]=100; q=q+5 // 5 goblin 116 vt[q]=2; vt[q+1]=1; vt[q+2]=1; vt[q+3]=0; vt[q+4]=80; q=q+5 // 6 goblin horned slim 117 vt[q]=2; vt[q+1]=2; vt[q+2]=1; vt[q+3]=1; vt[q+4]=100; q=q+5 // 7 goblin snouted+tail 118 vt[q]=2; vt[q+1]=0; vt[q+2]=1; vt[q+3]=0; vt[q+4]=125; q=q+5 // 8 goblin BULK (hobgoblin) 119 vt[q]=1; vt[q+1]=0; vt[q+2]=1; vt[q+3]=1; vt[q+4]=100; q=q+5 // 9 wolf 120 vt[q]=1; vt[q+1]=1; vt[q+2]=1; vt[q+3]=1; vt[q+4]=125; q=q+5 // 10 DIRE wolf (horned bulk) 121 vt[q]=1; vt[q+1]=0; vt[q+2]=1; vt[q+3]=0; vt[q+4]=80; q=q+5 // 11 coyote (slim, no tail) 122 let cks: *i64 = sys_mmap(12 * 8) as *i64 123 let fgs: *i64 = sys_mmap(12 * 8) as *i64 124 var v: i64 = 0 125 while v < 12 { 126 let sp: i64 = vt[v*5] 127 let hv: i64 = vt[v*5+1] 128 let ev: i64 = vt[v*5+2] 129 let tv: i64 = vt[v*5+3] 130 let bl: i64 = vt[v*5+4] 131 let np: i64 = av_build(base, sp, hv, ev, tv, bl) 132 let tx0: i64 = (v % 4) * TW 133 let ty0: i64 = (v / 4) * TH 134 tile_render(base, fb, tx0, ty0) 135 let ck: i64 = tile_cksum(fb, tx0, ty0) 136 let fg: i64 = tile_fg(fb, tx0, ty0) 137 cks[v] = ck 138 fgs[v] = fg 139 hw(" v" as *u8); pn(v); hw(" sp=" as *u8); pn(sp); hw(" parts=" as *u8); pn(np); hw(" fg=" as *u8); pn(fg); hw("\n" as *u8) 140 v = v + 1 141 } 142 write_png(fb, SW, SH, "knowledge/synth_variants.png" as *u8) 143 hw(" wrote knowledge/synth_variants.png (4x3 contact sheet, 384x216)\n" as *u8) 144 145 var fails: i64 = 0 146 // T1 all visible 147 var t1: i64 = 1 148 let fgmin: i64 = TW * TH * 2 / 100 149 var a: i64 = 0 150 while a < 12 { if fgs[a] < fgmin { t1 = 0 } a = a + 1 } 151 if t1 == 1 { hw("T1 PASS all 12 variants visibly render (fg >= 2 percent each)\n" as *u8) } 152 else { fails = fails + 1; hw("T1 FAIL a variant is invisible\n" as *u8) } 153 // T2 pairwise distinct 154 var distinct: i64 = 0 155 a = 0 156 while a < 12 { 157 var seen: i64 = 0 158 var b: i64 = 0 159 while b < a { if cks[b] == cks[a] { seen = 1 } b = b + 1 } 160 if seen == 0 { distinct = distinct + 1 } 161 a = a + 1 162 } 163 if distinct == 12 { hw("T2 PASS 12/12 tiles pairwise DISTINCT -- swap slots produce real diversity\n" as *u8) } 164 else { fails = fails + 1; hw("T2 FAIL only " as *u8); pn(distinct); hw(" distinct\n" as *u8) } 165 // T3 head-slot-only swap visible 166 if cks[0] != cks[1] { hw("T3 PASS head-slot-ONLY swap (plain vs horns, same base) changes the render\n" as *u8) } 167 else { fails = fails + 1; hw("T3 FAIL head swap invisible\n" as *u8) } 168 // T4 species swap 169 if cks[0] != cks[9] { hw("T4 PASS species swap (human vs wolf) changes the render\n" as *u8) } 170 else { fails = fails + 1; hw("T4 FAIL species identical\n" as *u8) } 171 172 // ---- MUTATION-DERIVED TOOTH: BILATERAL SYMMETRY OF PAIRED FEATURES (2026-08-01, debt 1785604588) ---- 173 // nx_gate_mutation_probe scored this pair 2/4 with TWO survivors, both the same shape: 174 // nx_assetvariant.nx:75 the HORN pair (hx-130 / hx+130, heady+300) 175 // nx_assetvariant.nx:91 the EAR pair (hx-210 / hx+210, heady+210) 176 // Flipping a ` + ` there either drops a feature below the head or puts BOTH of a pair on the same 177 // side. Every tooth above compares render CHECKSUMS for distinctness -- T2 pairwise distinct, T3 178 // head-swap visible, T4 species-swap visible -- and a misplaced horn still yields a DIFFERENT 179 // checksum, so distinctness stays satisfied. The gate could not tell a correct model from a 180 // deformed one; it only knew the twelve were not identical. 181 // LAW: A DISTINCTNESS TEST IS NOT A CORRECTNESS TEST. Proving twelve outputs differ says nothing 182 // about whether any one of them is right, and mutation finds exactly that gap. 183 // The invariant is bilateral symmetry: a human is built about hx=0, so every part must have a 184 // mirror at -cx with the SAME height. Centre-line parts (cx==0) mirror onto themselves. This needs 185 // no hard-coded coordinate, so it cannot rot as the model changes. 186 let sym_np: i64 = av_build(base, AV_HUMAN, 1, 2, 0, 100) // horns + ears, the two mutated slots 187 let sp6: *i64 = (base + O_PARTS) as *i64 188 var asym: i64 = 0 189 var si: i64 = 0 190 while si < sym_np { 191 let scx: i64 = sp6[si * 6] 192 let scy: i64 = sp6[si * 6 + 1] 193 var found: i64 = 0 194 var sj: i64 = 0 195 while sj < sym_np { 196 if sp6[sj * 6] == (0 - scx) { 197 if sp6[sj * 6 + 1] == scy { found = 1 } 198 } 199 sj = sj + 1 200 } 201 if found == 0 { asym = asym + 1 } 202 si = si + 1 203 } 204 if asym == 0 { hw("T5 PASS bilateral symmetry: every part has a mirror at -cx, same height\n" as *u8) } 205 else { fails = fails + 1; hw("T5 FAIL " as *u8); pn(asym); hw(" part(s) have NO mirror -- a paired feature is misplaced\n" as *u8) } 206 207 // D001 MIGRATE-ON-TOUCH, BY HAND. nx_gate_dry_apply SKIPPED this gate as anchor-rung: it prints 208 // "5/5 GREEN" without the literal `verdict=GREEN` the applier keys on. Rather than take the 209 // documented escape hatch a second time, the fails==0 idiom maps cleanly onto the shared contract: 210 // passed = TOTAL - fails. That is the mapping the applier's own FAILSZERO family refuses to do 211 // automatically -- and correctly so, since ctr[1]=0 would score RED -- but it is unambiguous when a 212 // human reads the gate and knows the row count. 213 let ctr: *i64 = gv_ctr() 214 ctr[0] = AV_GATE_ROWS - fails 215 ctr[1] = AV_GATE_ROWS 216 let rc: i64 = gv_verdict("ASSETVARIANT-GATE" as *u8, ctr, "one base system, 12 swap-slot variants; tiles distinct AND bilaterally symmetric" as *u8) 217 sys_exit(rc) 218 return rc 219}