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}