code wiki / _hdl_build / nx_plotgen_gate.nx

nx_plotgen_gate.nx source

↩ module page · 208 lines · 9054 B

1// nx_plotgen_gate.nx -- CERTIFICATION of nx_plotgen (gamebench hard GAP; the operator's "our own plot generator"). 2// T1 STRUCTURAL VALIDITY -- every choice target is in range, forward-only (acyclic), endings marked 3// T2 ★SOLVABILITY ACROSS 200 SEEDS -- the graph is WALKED with the real sv_choose from nx_story_vm and must 4// reach an ending every time. This is the unwinnable-quest killer, proven by execution not inspection. 5// T3 NO DEAD ENDS -- every non-ending node offers at least one choice 6// T4 DETERMINISM -- same seed -> byte-identical plot (a story is reproducible from its seed) 7// T5 VARIETY -- different seeds -> materially different plots (not one template) 8// T6 GATES ARE REAL -- generated plots actually contain flag-gated choices; without this, solvability 9// would be trivially satisfied by emitting an ungated linear chain 10// T7 ★ANTI-VACUITY -- a gated choice is GENUINELY BLOCKED when its flag is missing. Proves the gating is 11// enforced rather than decorative, and that T2 passes on merit rather than because nothing gates. 12// T8 composes: runs on nx_story_vm and the plot DATA round-trips through nx_gamesave 13// license_tier: ORIGINAL expect_exit: 0 14import "nx_syscalls.nx" 15import "nx_plotgen.nx" 16import "nx_gamesave.nx" 17import "nx_gate_verdict.nx" 18 19func pw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 20func pn(v: i64) -> i64 { 21 if v==0 { sys_write(1,"0" as *u8,1); return 0 } 22 var m: i64=v; if m<0 { sys_write(1,"-" as *u8,1); m=0-m } 23 let t: *u8=sys_mmap(32); var k: i64=0 24 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } 25 let o: *u8=sys_mmap(32); var q: i64=k-1; var i: i64=0 26 while q>=0 { o[i]=t[q]; i=i+1; q=q-1 } 27 sys_write(1,o,i); return 0 28} 29 30func main() -> i64 { 31 pw("=== nx_plotgen_gate: can the generator emit a plot that is always winnable? ===\n\n") 32 var pass: i64 = 0 33 var checks: i64 = 0 34 let N: i64 = 24 35 let CH: i64 = N*SV_NCH 36 37 let tg: *i64 = sys_mmap(CH*8) as *i64 38 let rq: *i64 = sys_mmap(CH*8) as *i64 39 let sf: *i64 = sys_mmap(CH*8) as *i64 40 let ie: *i64 = sys_mmap(N*8) as *i64 41 42 // ---------- T1 structural validity ---------- 43 checks = checks + 1 44 let gated0: i64 = pg_gen(tg, rq, sf, ie, N, 20260720) 45 var bad: i64 = 0 46 var backward: i64 = 0 47 var i: i64 = 0 48 while i < N { 49 var c: i64 = 0 50 while c < SV_NCH { 51 let t: i64 = tg[i*SV_NCH + c] 52 if t != 0-1 { 53 if t < 0 { bad = bad + 1 } 54 if t >= N { bad = bad + 1 } 55 if t <= i { backward = backward + 1 } // forward-only => acyclic => always terminates 56 } 57 c = c + 1 58 } 59 i = i + 1 60 } 61 if bad==0 { if backward==0 { if ie[N-1]==1 { 62 pw("T1 GREEN structural: all targets in range, forward-only (acyclic), ending marked at node ") 63 pn(N-1); pw("\n"); pass=pass+1 64 } } } 65 if bad>0 { pw("T1 RED "); pn(bad); pw(" out-of-range targets\n") } 66 if bad==0 { if backward>0 { pw("T1 RED "); pn(backward); pw(" backward edges -- graph may cycle\n") } } 67 68 // ---------- T2 solvability across 200 seeds ---------- 69 checks = checks + 1 70 var solved: i64 = 0 71 var trials: i64 = 0 72 var totalgated: i64 = 0 73 var s: i64 = 1 74 while s <= 200 { 75 totalgated = totalgated + pg_gen(tg, rq, sf, ie, N, s*7919) 76 let endnode: i64 = pg_walk(tg, rq, sf, ie, N, N*4) 77 if endnode >= 0 { if sv_is_end(ie, endnode)==1 { solved = solved + 1 } } 78 trials = trials + 1 79 s = s + 1 80 } 81 if solved==trials { 82 pw("T2 GREEN solvability: "); pn(solved); pw("/"); pn(trials) 83 pw(" generated plots WALKED to an ending via the real sv_choose (0 unwinnable), ") 84 pn(totalgated); pw(" gated choices total\n"); pass=pass+1 85 } else { 86 pw("T2 RED "); pn(trials-solved); pw("/"); pn(trials); pw(" plots were UNWINNABLE\n") 87 } 88 89 // ---------- T3 no dead ends ---------- 90 checks = checks + 1 91 pg_gen(tg, rq, sf, ie, N, 4242) 92 var dead: i64 = 0 93 i = 0 94 while i < N { 95 if ie[i]==0 { 96 var any: i64 = 0 97 var c: i64 = 0 98 while c < SV_NCH { if tg[i*SV_NCH + c] != 0-1 { any = 1 } c = c + 1 } 99 if any==0 { dead = dead + 1 } 100 } 101 i = i + 1 102 } 103 if dead==0 { 104 pw("T3 GREEN no dead ends: every non-ending node offers at least one choice\n"); pass=pass+1 105 } else { pw("T3 RED "); pn(dead); pw(" dead-end nodes\n") } 106 107 // ---------- T4 determinism ---------- 108 checks = checks + 1 109 let tg2: *i64 = sys_mmap(CH*8) as *i64 110 let rq2: *i64 = sys_mmap(CH*8) as *i64 111 let sf2: *i64 = sys_mmap(CH*8) as *i64 112 let ie2: *i64 = sys_mmap(N*8) as *i64 113 pg_gen(tg, rq, sf, ie, N, 31337) 114 pg_gen(tg2, rq2, sf2, ie2, N, 31337) 115 var det: i64 = 1 116 i = 0 117 while i < CH { 118 if tg[i]!=tg2[i] { det=0 } 119 if rq[i]!=rq2[i] { det=0 } 120 if sf[i]!=sf2[i] { det=0 } 121 i = i + 1 122 } 123 i = 0 124 while i < N { if ie[i]!=ie2[i] { det=0 } i=i+1 } 125 if det==1 { 126 pw("T4 GREEN deterministic: seed 31337 twice -> identical plot (a story is reproducible from its seed)\n") 127 pass=pass+1 128 } else { pw("T4 RED same seed produced different plots\n") } 129 130 // ---------- T5 variety ---------- 131 checks = checks + 1 132 pg_gen(tg2, rq2, sf2, ie2, N, 90210) 133 var diff: i64 = 0 134 i = 0 135 while i < CH { if tg[i]!=tg2[i] { diff = diff + 1 } i=i+1 } 136 if diff > 8 { 137 pw("T5 GREEN variety: a different seed changed "); pn(diff) 138 pw(" of "); pn(CH); pw(" choice slots -- not one template\n"); pass=pass+1 139 } else { pw("T5 RED seeds barely differ ("); pn(diff); pw(" slots) -- generator is a template\n") } 140 141 // ---------- T6 gates are real ---------- 142 checks = checks + 1 143 pg_gen(tg, rq, sf, ie, N, 555) 144 var ngate: i64 = 0 145 i = 0 146 while i < CH { if rq[i] > 0 { ngate = ngate + 1 } i=i+1 } 147 if ngate > 0 { 148 pw("T6 GREEN gates are real: "); pn(ngate) 149 pw(" flag-gated choices in this plot -- solvability is not trivially satisfied by a linear chain\n") 150 pass=pass+1 151 } else { pw("T6 RED no gated choices generated -- T2 would be vacuous\n") } 152 153 // ---------- T7 anti-vacuity: a gate genuinely BLOCKS ---------- 154 checks = checks + 1 155 // find a gated choice and try it with EMPTY flags -- sv_choose must refuse it 156 var gi: i64 = 0-1 157 i = 0 158 while i < CH { if rq[i] > 0 { if gi < 0 { gi = i } } i=i+1 } 159 var t7: i64 = 0 160 if gi >= 0 { 161 let node: i64 = gi / SV_NCH 162 let cho: i64 = gi % SV_NCH 163 let flags: *i64 = sys_mmap(8) as *i64 164 flags[0] = 0 // no flags raised 165 let blocked: i64 = sv_choose(tg, rq, sf, node, cho, flags) 166 flags[0] = rq[gi] // exactly the required bits 167 let allowed: i64 = sv_choose(tg, rq, sf, node, cho, flags) 168 if blocked == 0-1 { if allowed >= 0 { t7 = 1 } } 169 if t7==1 { 170 pw("T7 GREEN anti-vacuity: gated choice at node "); pn(node); pw(" REFUSED without its flag (-1) and ") 171 pw("ALLOWED with it (-> node "); pn(allowed); pw(") -- gating is enforced, not decorative\n") 172 pass=pass+1 173 } else { 174 pw("T7 RED gate not enforced: blocked="); pn(blocked); pw(" allowed="); pn(allowed); pw("\n") 175 } 176 } else { pw("T7 RED no gated choice found to test\n") } 177 178 // ---------- T8 composes: story_vm execution + gamesave round-trip ---------- 179 checks = checks + 1 180 let PP: *u8 = "knowledge/nx_plotgen_story.sav" as *u8 181 sys_unlinkat(PP) 182 pg_gen(tg, rq, sf, ie, N, 777001) 183 let endn: i64 = pg_walk(tg, rq, sf, ie, N, N*4) 184 let wrote: i64 = gs_save(PP, 70707, tg, CH, 1784580000) 185 let rd: *i64 = sys_mmap(CH*8) as *i64 186 let got: i64 = gs_load(PP, rd, CH, 0 as *i64) 187 var t8: i64 = 1 188 if got != CH { t8 = 0 } 189 if wrote <= 0 { t8 = 0 } 190 if endn < 0 { t8 = 0 } 191 i = 0 192 while i < CH { if rd[i]!=tg[i] { t8=0 } i=i+1 } 193 if t8==1 { 194 pw("T8 GREEN composes: plot ran on nx_story_vm to ending node "); pn(endn) 195 pw(" and its graph round-tripped through nx_gamesave ("); pn(wrote); pw("B)\n"); pass=pass+1 196 } else { pw("T8 RED compose failed: end="); pn(endn); pw(" rc="); pn(got); pw("\n") } 197 198 pw("\n=== nx_plotgen_gate "); pn(pass); pw("/"); pn(checks) 199 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 200 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 201 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 202 let ctr__dry: *i64 = gv_ctr() 203 ctr__dry[0] = pass 204 ctr__dry[1] = checks 205 let rc__dry: i64 = gv_verdict("PLOTGEN-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 206 sys_exit(rc__dry) 207 return rc__dry 208}