code wiki / _hdl_build / nx_wire_nethack_gate.nx
nx_wire_nethack_gate.nx source
↩ module page · 1051 lines · 43733 B
1// nx_wire_nethack_gate.nx -- TITLE WIRING #1: NetHack (roguelike), the first nx_gamebench title wired
2// through the certified parts. Until now every gamebench readiness number was SELF-GRADED (titles_wired=0):
3// the parts exist, but nothing proved they COMPOSE into this title's genre loop. This gate is that proof --
4// an actual playable roguelike loop (procgen dungeon -> descend -> fight -> loot rations -> level up ->
5// save/resume) built ONLY from certified parts, with every exercised capability measured by a tooth.
6//
7// CAPS EXERCISED (bit = nx_gamebench capability index, set ONLY if its tooth passes; the mask is written
8// into the wiring artifact payload[0] so the board's declared mask can be cross-checked by nx_gamebench_gate):
9// bit 3 procgen-world (deterministic carved dungeon, seed-diverse, stairs reachable)
10// bit 7 pathfinding-ai (nx_pathfind pf_astar drives the player every move turn)
11// bit 6 entity-component-sim (nx_entity_store holds monsters; stale handle of a killed monster refused)
12// bit 9 rpg-stats-progression(nx_rpgstats XP curve/level inverse/derived HP drive combat + level-ups)
13// bit 11 inventory-crafting (rations picked up, 2 rations CRAFT a feast, ledger conserves exactly)
14// bit 15 save-load-persistence(nx_gamesave mid-run save -> load into ZEROED state -> resume TRANSPARENT)
15// bit 18 ui-menus-hud (nx_gamehud: the eat decision goes THROUGH a modal menu -- open/wrap/select
16// CAUSES the eat, it is not painted after the fact; HUD slots bind live
17// hp/lvl/depth/kills/food per turn, render-chain deterministic across runs)
18// NOT exercised (title requires, this loop does not prove -> the board must down-score them for NetHack):
19// bit 14 text-render-typography (the HUD blits glyphs, but typography = layout/wrap/paragraphs; honest no-claim).
20//
21// FLAGSHIP PROPERTY (mutation target): save/load TRANSPARENCY -- play N..M continuously vs play N, save,
22// load into a zeroed world, resume to M: EVERY per-turn checksum identical (the GX-9 test most engines
23// cannot run; it catches the whole "the save forgot a field" class). Non-vacuity guard: the resumed world
24// must be non-trivial (live monsters, xp earned, items picked) or the equality proves nothing.
25// license_tier: ORIGINAL expect_exit: 0
26import "nx_syscalls.nx"
27import "nx_gamesave.nx"
28import "nx_rpgstats.nx"
29import "nx_entity_store.nx"
30import "nx_pathfind.nx"
31import "nx_gamehud.nx"
32import "nx_wire_harness.nx"
33import "nx_gate_verdict.nx"
34import "nx_trimesh.nx"
35import "nx_objload.nx"
36
37// ---- world shape (data at the top, per rule 11) ----
38const NH_W: i64 = 24
39const NH_H: i64 = 12
40const NH_DEPTH: i64 = 3
41const NH_NMON: i64 = 6
42const NH_NITEM: i64 = 5
43const NH_CAP: i64 = 16 // entity arena capacity
44const NH_NCOMP: i64 = 4 // components: 0=x 1=y 2=hp 3=xp_reward
45const NH_CARVE: i64 = 140 // carve steps per level
46const NH_AGGRO: i64 = 8 // monster chase radius (manhattan)
47const NH_MAXT: i64 = 600
48// rpg tuning (feeds the data-driven nx_rpgstats curves)
49const NH_XB: i64 = 20 // xp curve base
50const NH_XQ: i64 = 10 // xp curve quad
51const NH_HPB: i64 = 10
52const NH_HPC: i64 = 2
53const NH_HPL: i64 = 3
54const NH_CON: i64 = 14
55const NH_BITE: i64 = 2
56const NH_MHPB: i64 = 12 // monster base hp (tanky enough that the save point sees a LIVE world)
57const NH_HEAL_R: i64 = 10 // ration heal
58const NH_HEAL_F: i64 = 25 // feast heal (crafted from 2 rations -> crafting is worth it)
59// save shape
60const NH_HDRN: i64 = 26 // game header fields serialized
61const NH_NSV: i64 = 106 // 26 hdr + 1 nmon + 16*4 monsters + 5*3 items
62const NH_SCHEMA: i64 = 7307
63const NH_ART_SCHEMA: i64 = 7317 // the wiring-evidence artifact schema
64// transparency test window (the full run wins at ~turn 62; the window must sit fully before the win)
65const NH_T1W: i64 = 20
66const NH_T2W: i64 = 45
67// ---- game arena offsets (i64 words; grid 288, scratch blocks padded to 296) ----
68const NG_O_GRID: i64 = 32
69const NG_O_ITEM: i64 = 320
70const NG_O_ENT: i64 = 336 // en_words(16,4) = 156, padded to 160
71const NG_O_PATH: i64 = 496
72const NG_O_G: i64 = 792
73const NG_O_F: i64 = 1088
74const NG_O_CAME: i64 = 1384
75const NG_O_OPENF: i64 = 1680
76const NG_O_CLOSED: i64 = 1976
77const NG_O_CARVE: i64 = 2272
78const NG_O_HUD: i64 = 2576 // nx_gamehud arena (GH_WORDS=64) APPENDED -- UI state rides the game
79 // arena but stays OUT of the save (transient UI, re-init on restore;
80 // the PART's own gate proves hud state CAN persist -- the TITLE keeps
81 // it ephemeral, which is what real roguelikes do with menu state)
82const NG_WORDS: i64 = 2640
83const NH_HUDINK: i64 = 50 // min ink px for a non-vacuous HUD frame (T10 guard)
84// header field indices inside the game arena
85const F_WS: i64 = 0
86const F_DEPTH: i64 = 1
87const F_TURN: i64 = 2
88const F_PX: i64 = 3
89const F_PY: i64 = 4
90const F_XP: i64 = 5
91const F_LVL: i64 = 6
92const F_HP: i64 = 7
93const F_KILLS: i64 = 8
94const F_RATION: i64 = 9
95const F_FEAST: i64 = 10
96const F_PICKED: i64 = 11
97const F_USED: i64 = 12
98const F_WIN: i64 = 13
99const F_COMB: i64 = 14
100const F_EATR: i64 = 15
101const F_EATF: i64 = 16
102const F_PSTEPS: i64 = 17
103const F_ATT: i64 = 18
104const F_BITES: i64 = 19
105const F_SPAWNED: i64 = 20
106const F_SX: i64 = 21
107const F_SY: i64 = 22
108const F_NCARVE: i64 = 23
109const F_GENRNG: i64 = 24
110const F_ABAND: i64 = 25 // monsters left alive on a level when the player descended
111
112func ww(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
113func wn(v: i64) -> i64 {
114 if v==0 { sys_write(1,"0" as *u8,1); return 0 }
115 var m: i64=v; if m<0 { sys_write(1,"-" as *u8,1); m=0-m }
116 let t: *u8=sys_mmap(32); var k: i64=0
117 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
118 let o: *u8=sys_mmap(32); var q: i64=k-1; var i: i64=0
119 while q>=0 { o[i]=t[q]; i=i+1; q=q-1 }
120 sys_write(1,o,i); return 0
121}
122func nbit(i: i64) -> i64 { var v: i64=1; var k: i64=0; while k<i { v=v*2; k=k+1 } return v }
123func nrng(g: *i64) -> i64 {
124 var x: i64 = g[F_GENRNG]
125 x = x ^ (x << 13)
126 x = x ^ (x >> 7)
127 x = x ^ (x << 17)
128 g[F_GENRNG] = x
129 if x < 0 { return 0 - x }
130 return x
131}
132func nabs(v: i64) -> i64 { if v<0 { return 0-v } return v }
133
134// ---- level structure: PURE function of (worldseed, depth). Restore regenerates it exactly. ----
135func nh_genstruct(g: *i64) -> i64 {
136 g[F_GENRNG] = g[F_WS]*1000 + g[F_DEPTH]*7919
137 if g[F_GENRNG]==0 { g[F_GENRNG]=1 }
138 let grid: *i64 = (g as i64 + NG_O_GRID*8) as *i64
139 let carve: *i64 = (g as i64 + NG_O_CARVE*8) as *i64
140 var i: i64=0
141 while i<NH_W*NH_H { grid[i]=1; i=i+1 }
142 var cx: i64=1
143 var cy: i64=1
144 grid[cy*NH_W+cx]=0
145 carve[0]=cy*NH_W+cx
146 var nc: i64=1
147 var steps: i64=0
148 while steps<NH_CARVE {
149 let d: i64 = nrng(g)%4
150 var tx: i64=cx
151 var ty: i64=cy
152 if d==0 { tx=cx+1 }
153 if d==1 { tx=cx-1 }
154 if d==2 { ty=cy+1 }
155 if d==3 { ty=cy-1 }
156 if tx>=1 { if tx<=NH_W-2 { if ty>=1 { if ty<=NH_H-2 {
157 cx=tx; cy=ty
158 if grid[cy*NH_W+cx]==1 { grid[cy*NH_W+cx]=0; carve[nc]=cy*NH_W+cx; nc=nc+1 }
159 } } } }
160 steps=steps+1
161 }
162 g[F_SX]=cx
163 g[F_SY]=cy
164 g[F_NCARVE]=nc
165 return nc
166}
167// place monsters + items on carved floor, reset player to the entrance (level entry only, never on restore)
168func nh_populate(g: *i64) -> i64 {
169 let carve: *i64 = (g as i64 + NG_O_CARVE*8) as *i64
170 let item: *i64 = (g as i64 + NG_O_ITEM*8) as *i64
171 let a: *i64 = (g as i64 + NG_O_ENT*8) as *i64
172 en_init(a, NH_CAP, NH_NCOMP)
173 let nc: i64 = g[F_NCARVE]
174 let d: i64 = g[F_DEPTH]
175 var m: i64=0
176 while m<NH_NMON {
177 var placed: i64=0
178 var guard: i64=0
179 while placed==0 {
180 guard=guard+1
181 if guard>200 { placed=1 }
182 let pick: i64 = 1 + (nrng(g) % (nc-1))
183 let cell: i64 = carve[pick]
184 let mx: i64 = cell % NH_W
185 let my: i64 = cell / NH_W
186 // not on another monster
187 var clash: i64=0
188 var k: i64=0
189 while k<en_count(a) {
190 let h: i64 = en_nth(a,k)
191 let ox: i64 = en_get(a,h,0)
192 let oy: i64 = en_get(a,h,1)
193 if ox==mx { if oy==my { clash=1 } }
194 k=k+1
195 }
196 if clash==0 { if placed==0 {
197 let h2: i64 = en_spawn(a)
198 if h2!=EN_NULL {
199 en_set(a,h2,0,mx)
200 en_set(a,h2,1,my)
201 let mhp: i64 = NH_MHPB + d*2
202 en_set(a,h2,2,mhp)
203 let mxp: i64 = 15*d
204 en_set(a,h2,3,mxp)
205 g[F_SPAWNED]=g[F_SPAWNED]+1
206 placed=1
207 }
208 } }
209 }
210 m=m+1
211 }
212 var it: i64=0
213 while it<NH_NITEM {
214 var placed2: i64=0
215 var guard2: i64=0
216 while placed2==0 {
217 guard2=guard2+1
218 if guard2>200 { placed2=1 }
219 let pick2: i64 = 1 + (nrng(g) % (nc-1))
220 let cell2: i64 = carve[pick2]
221 let ix: i64 = cell2 % NH_W
222 let iy: i64 = cell2 / NH_W
223 var clash2: i64=0
224 var q: i64=0
225 while q<it { if item[q*3]==ix { if item[q*3+1]==iy { clash2=1 } } q=q+1 }
226 // not on the stairs (items on stairs would be fine, keep them distinct for clarity)
227 if ix==g[F_SX] { if iy==g[F_SY] { clash2=1 } }
228 if clash2==0 { if placed2==0 {
229 item[it*3]=ix
230 item[it*3+1]=iy
231 item[it*3+2]=0
232 placed2=1
233 } }
234 }
235 it=it+1
236 }
237 g[F_PX]=1
238 g[F_PY]=1
239 return 0
240}
241func nh_new(g: *i64, ws: i64) -> i64 {
242 var i: i64=0
243 while i<32 { g[i]=0; i=i+1 }
244 g[F_WS]=ws
245 g[F_DEPTH]=1
246 let l1: i64 = rs_level_for_xp(0, NH_XB, NH_XQ)
247 g[F_LVL]=l1
248 let hp0: i64 = rs_derived_hp(NH_CON, l1, NH_HPB, NH_HPC, NH_HPL)
249 g[F_HP]=hp0
250 nh_genstruct(g)
251 nh_populate(g)
252 let hud: *i64 = (g as i64 + NG_O_HUD*8) as *i64
253 gh_init(hud, 5)
254 return 0
255}
256// serialize game-visible state into S[NH_NSV] (monsters via DENSE order so restore preserves iteration order)
257func nh_serialize(g: *i64, S: *i64) -> i64 {
258 var i: i64=0
259 while i<NH_HDRN { S[i]=g[i]; i=i+1 }
260 let a: *i64 = (g as i64 + NG_O_ENT*8) as *i64
261 let n: i64 = en_count(a)
262 S[NH_HDRN]=n
263 var k: i64=0
264 while k<NH_CAP*4 { S[NH_HDRN+1+k]=0; k=k+1 }
265 k=0
266 while k<n {
267 let h: i64 = en_nth(a,k)
268 let b: i64 = NH_HDRN+1+k*4
269 let v0: i64 = en_get(a,h,0)
270 let v1: i64 = en_get(a,h,1)
271 let v2: i64 = en_get(a,h,2)
272 let v3: i64 = en_get(a,h,3)
273 S[b]=v0
274 S[b+1]=v1
275 S[b+2]=v2
276 S[b+3]=v3
277 k=k+1
278 }
279 let item: *i64 = (g as i64 + NG_O_ITEM*8) as *i64
280 let ib: i64 = NH_HDRN+1+NH_CAP*4
281 var q: i64=0
282 while q<NH_NITEM*3 { S[ib+q]=item[q]; q=q+1 }
283 return NH_NSV
284}
285// restore a game from S into a (possibly ZEROED) arena: header, regenerated structure, monsters, items
286func nh_restore(g: *i64, S: *i64) -> i64 {
287 var i: i64=0
288 while i<32 { g[i]=0; i=i+1 }
289 g[F_WS]=S[F_WS]
290 g[F_DEPTH]=S[F_DEPTH]
291 nh_genstruct(g) // grid + stairs: pure function of (ws,depth), matches by determinism
292 // copy the FULL header AFTER genstruct: genstruct clobbers F_GENRNG (it re-runs the carve), and the
293 // saved value is the post-populate stream -- restoring it after keeps the checksum bit-exact.
294 i=0
295 while i<NH_HDRN { g[i]=S[i]; i=i+1 }
296 let a: *i64 = (g as i64 + NG_O_ENT*8) as *i64
297 en_init(a, NH_CAP, NH_NCOMP)
298 let n: i64 = S[NH_HDRN]
299 var k: i64=0
300 while k<n {
301 let b: i64 = NH_HDRN+1+k*4
302 let h: i64 = en_spawn(a)
303 en_set(a,h,0,S[b])
304 en_set(a,h,1,S[b+1])
305 en_set(a,h,2,S[b+2])
306 en_set(a,h,3,S[b+3])
307 k=k+1
308 }
309 let item: *i64 = (g as i64 + NG_O_ITEM*8) as *i64
310 let ib: i64 = NH_HDRN+1+NH_CAP*4
311 var q: i64=0
312 while q<NH_NITEM*3 { item[q]=S[ib+q]; q=q+1 }
313 let hud: *i64 = (g as i64 + NG_O_HUD*8) as *i64
314 gh_init(hud, 5) // UI state is transient: fresh menu machine on restore, game state untouched
315 return 0
316}
317func nh_ckv(ck: i64, v0: i64) -> i64 {
318 var v: i64 = v0
319 if v<0 { v = (0-v)*2+1 } else { v = v*2 }
320 return (ck*131 + v) & 0x7FFFFFFFFFFFFFFF
321}
322// order-sensitive rolling checksum of the FULL game-visible state.
323// DELIBERATELY INDEPENDENT of nh_serialize: it walks the live arena directly, so a serializer that
324// "forgets a field" produces a restore whose checksum diverges -- the T6 transparency tooth catches it.
325// (A ck routed THROUGH the serializer would inherit the same amnesia and prove nothing.)
326func nh_ck(g: *i64) -> i64 {
327 var ck: i64 = 1469598103
328 var i: i64=0
329 while i<NH_HDRN { ck = nh_ckv(ck, g[i]); i=i+1 }
330 let a: *i64 = (g as i64 + NG_O_ENT*8) as *i64
331 let n: i64 = en_count(a)
332 ck = nh_ckv(ck, n)
333 var k: i64=0
334 while k<n {
335 let h: i64 = en_nth(a,k)
336 var c: i64=0
337 while c<4 {
338 let v: i64 = en_get(a,h,c)
339 ck = nh_ckv(ck, v)
340 c=c+1
341 }
342 k=k+1
343 }
344 let item: *i64 = (g as i64 + NG_O_ITEM*8) as *i64
345 var q: i64=0
346 while q<NH_NITEM*3 { ck = nh_ckv(ck, item[q]); q=q+1 }
347 return ck
348}
349// is any OTHER monster on (x,y)?
350func nh_moncell(a: *i64, x: i64, y: i64, skip: i64) -> i64 {
351 var k: i64=0
352 while k<en_count(a) {
353 let h: i64 = en_nth(a,k)
354 if h!=skip {
355 let ox: i64 = en_get(a,h,0)
356 let oy: i64 = en_get(a,h,1)
357 if ox==x { if oy==y { return 1 } }
358 }
359 k=k+1
360 }
361 return 0
362}
363// one game turn. returns the handle of a monster KILLED this turn (0 if none) -- the stale-handle witness.
364func nh_tick(g: *i64) -> i64 {
365 if g[F_WIN]==1 { return 0 }
366 let grid: *i64 = (g as i64 + NG_O_GRID*8) as *i64
367 let a: *i64 = (g as i64 + NG_O_ENT*8) as *i64
368 let item: *i64 = (g as i64 + NG_O_ITEM*8) as *i64
369 var killed: i64 = 0
370 let px: i64 = g[F_PX]
371 let py: i64 = g[F_PY]
372 // ---- player action: attack an adjacent monster, else descend on stairs, else A* step toward stairs ----
373 var adj: i64 = 0
374 var k: i64=0
375 while k<en_count(a) {
376 if adj==0 {
377 let h: i64 = en_nth(a,k)
378 let mx: i64 = en_get(a,h,0)
379 let my: i64 = en_get(a,h,1)
380 let dd: i64 = nabs(mx-px)+nabs(my-py)
381 if dd==1 { adj=h }
382 }
383 k=k+1
384 }
385 // DIALECT NOTE: no `else if` / `||` / `else` in this file from here on -- multiline else-branches
386 // DESYNC the nx_cc parser (found by THIS gate; minimal repro nx_wire_probe.nx aux2: the next func
387 // fuses into this one as an empty stub and the binary runs silent exit-0). The certified parts are
388 // mostly plain-if code; this file uses flag vars instead of else-chains.
389 var acted: i64=0
390 if adj!=0 {
391 acted=1
392 g[F_ATT]=g[F_ATT]+1
393 let dmg: i64 = 4 + g[F_LVL]*2
394 let mhp: i64 = en_get(a,adj,2) - dmg
395 if mhp<=0 {
396 let reward: i64 = en_get(a,adj,3)
397 en_destroy(a,adj)
398 killed=adj
399 g[F_KILLS]=g[F_KILLS]+1
400 g[F_XP]=g[F_XP]+reward
401 let nl: i64 = rs_level_for_xp(g[F_XP], NH_XB, NH_XQ)
402 if nl>g[F_LVL] {
403 g[F_LVL]=nl
404 let mh: i64 = rs_derived_hp(NH_CON, nl, NH_HPB, NH_HPC, NH_HPL)
405 g[F_HP]=mh
406 }
407 }
408 if mhp>0 { en_set(a,adj,2,mhp) }
409 }
410 if acted==0 { if px==g[F_SX] { if py==g[F_SY] {
411 acted=1
412 g[F_DEPTH]=g[F_DEPTH]+1
413 if g[F_DEPTH]>NH_DEPTH { g[F_WIN]=1 }
414 if g[F_DEPTH]<=NH_DEPTH {
415 let leftover: i64 = en_count(a)
416 g[F_ABAND]=g[F_ABAND]+leftover
417 nh_genstruct(g)
418 nh_populate(g)
419 }
420 } } }
421 if acted==0 { if g[F_WIN]==0 {
422 let pg: *i64 = (g as i64 + NG_O_G*8) as *i64
423 let pf: *i64 = (g as i64 + NG_O_F*8) as *i64
424 let pc: *i64 = (g as i64 + NG_O_CAME*8) as *i64
425 let po: *i64 = (g as i64 + NG_O_OPENF*8) as *i64
426 let pz: *i64 = (g as i64 + NG_O_CLOSED*8) as *i64
427 let pp: *i64 = (g as i64 + NG_O_PATH*8) as *i64
428 let plen: i64 = pf_astar(grid, NH_W, NH_H, px, py, g[F_SX], g[F_SY], pg, pf, pc, po, pz, pp)
429 if plen>=1 {
430 let nxt: i64 = pp[1]
431 let nx: i64 = nxt % NH_W
432 let ny: i64 = nxt / NH_W
433 // never step ONTO a monster (an adjacent one would have been attacked; this is belt+braces)
434 let occ: i64 = nh_moncell(a, nx, ny, 0)
435 if occ==0 { g[F_PX]=nx; g[F_PY]=ny; g[F_PSTEPS]=g[F_PSTEPS]+1 }
436 }
437 } }
438 // ---- pickup ----
439 let qx: i64 = g[F_PX]
440 let qy: i64 = g[F_PY]
441 var q: i64=0
442 while q<NH_NITEM {
443 if item[q*3+2]==0 { if item[q*3]==qx { if item[q*3+1]==qy {
444 item[q*3+2]=1
445 g[F_RATION]=g[F_RATION]+1
446 g[F_PICKED]=g[F_PICKED]+1
447 } } }
448 q=q+1
449 }
450 // ---- craft: 2 rations -> 1 feast (one combine per turn) ----
451 if g[F_RATION]>=2 {
452 g[F_RATION]=g[F_RATION]-2
453 g[F_FEAST]=g[F_FEAST]+1
454 g[F_COMB]=g[F_COMB]+1
455 }
456 // ---- eat when hurt, THROUGH THE MENU (bit 18): the selection CAUSES the eat. Feast-first policy
457 // is encoded as menu ORDER (feast listed first, cursor selects item 0 after a FULL WRAP -- the wrap
458 // exercises gh_menu_move's both-directions arithmetic on every single eat of every run). Same
459 // decisions as the old inline chain => game-state evolution and every banked checksum unchanged. ----
460 let maxhp: i64 = rs_derived_hp(NH_CON, g[F_LVL], NH_HPB, NH_HPC, NH_HPL)
461 let hud: *i64 = (g as i64 + NG_O_HUD*8) as *i64
462 if g[F_HP] < maxhp {
463 var edible: i64=0
464 if g[F_FEAST]>0 { edible=edible+1 }
465 if g[F_RATION]>0 { edible=edible+1 }
466 if edible>0 {
467 gh_menu_open(hud, edible)
468 var slot: i64=0
469 if g[F_FEAST]>0 { gh_menu_item(hud, slot, 1, g[F_FEAST]); slot=slot+1 }
470 if g[F_RATION]>0 { gh_menu_item(hud, slot, 2, g[F_RATION]); slot=slot+1 }
471 let choice: i64 = wh_menu_wrapsel(hud, edible) // MIGRATED: shared variable-menu wrap+select
472 if choice==1 {
473 g[F_FEAST]=g[F_FEAST]-1
474 g[F_HP]=g[F_HP]+NH_HEAL_F
475 g[F_USED]=g[F_USED]+1
476 g[F_EATF]=g[F_EATF]+1
477 }
478 if choice==2 {
479 g[F_RATION]=g[F_RATION]-1
480 g[F_HP]=g[F_HP]+NH_HEAL_R
481 g[F_USED]=g[F_USED]+1
482 g[F_EATR]=g[F_EATR]+1
483 }
484 }
485 if g[F_HP]>maxhp { g[F_HP]=maxhp }
486 }
487 // ---- monsters: bite when adjacent, chase when in aggro range ----
488 // ⚠nx_cc PARSER TRAP (found by THIS gate, minimal repro nx_wire_probe.nx aux2): a var/let declared
489 // DIRECTLY inside an `else if` branch DESYNCS the parser -- later funcs fuse into this one as empty
490 // stubs and the binary exits 0 silently. Workaround: hoist ALL decls above the if/else-if.
491 var mI: i64=0
492 var msx: i64=0
493 var msy: i64=0
494 var mtx: i64=0
495 var mty: i64=0
496 var moved: i64=0
497 var occ2: i64=0
498 var occ3: i64=0
499 let mtot: i64 = en_count(a)
500 while mI<mtot {
501 let h2: i64 = en_nth(a,mI)
502 let mx2: i64 = en_get(a,h2,0)
503 let my2: i64 = en_get(a,h2,1)
504 let da: i64 = nabs(mx2-g[F_PX])
505 let db: i64 = nabs(my2-g[F_PY])
506 let d2: i64 = da+db
507 if d2==1 {
508 g[F_HP]=g[F_HP]-NH_BITE
509 g[F_BITES]=g[F_BITES]+1
510 }
511 if d2>1 { if d2<=NH_AGGRO {
512 msx=0
513 if g[F_PX]>mx2 { msx=1 }
514 if g[F_PX]<mx2 { msx=0-1 }
515 moved=0
516 if msx!=0 {
517 mtx=mx2+msx
518 if grid[my2*NH_W+mtx]==0 {
519 occ2 = nh_moncell(a,mtx,my2,h2)
520 var onp: i64=0
521 if mtx==g[F_PX] { if my2==g[F_PY] { onp=1 } }
522 if occ2==0 { if onp==0 { en_set(a,h2,0,mtx); moved=1 } }
523 }
524 }
525 if moved==0 {
526 msy=0
527 if g[F_PY]>my2 { msy=1 }
528 if g[F_PY]<my2 { msy=0-1 }
529 if msy!=0 {
530 mty=my2+msy
531 if grid[mty*NH_W+mx2]==0 {
532 occ3 = nh_moncell(a,mx2,mty,h2)
533 var onp2: i64=0
534 if mx2==g[F_PX] { if mty==g[F_PY] { onp2=1 } }
535 if occ3==0 { if onp2==0 { en_set(a,h2,1,mty) } }
536 }
537 }
538 }
539 } }
540 mI=mI+1
541 }
542 g[F_TURN]=g[F_TURN]+1
543 return killed
544}
545// T10 instrument: replay a real game for `turns`, binding the HUD to live state and rendering every
546// turn; returns the rolling chain of frame checksums. out[0]=frames that CHANGED vs the previous
547// (a state-blind renderer scores 0), out[1]=final-frame ink px, out[2]=menu selections made in-loop.
548func nh_hudchain(seed: i64, turns: i64, out: *i64) -> i64 {
549 let g: *i64 = sys_mmap(NG_WORDS*8) as *i64
550 nh_new(g, seed)
551 let font: *u8 = font8x8_table()
552 let labels: *i64 = sys_mmap(8*8) as *i64
553 labels[0]="HP" as i64
554 labels[1]="LVL" as i64
555 labels[2]="DEPTH" as i64
556 labels[3]="KILLS" as i64
557 labels[4]="FOOD" as i64
558 let mlabels: *i64 = sys_mmap(8*8) as *i64
559 mlabels[0]="feast" as i64
560 mlabels[1]="ration" as i64
561 let fb: *u8 = sys_mmap(72000)
562 let hud: *i64 = (g as i64 + NG_O_HUD*8) as *i64
563 var chain: i64 = 1469598103
564 var prev: i64 = 0
565 var moved: i64 = 0
566 var t: i64 = 0
567 while t < turns {
568 nh_tick(g)
569 gh_slot_set(hud, 0, g[F_HP])
570 gh_slot_set(hud, 1, g[F_LVL])
571 gh_slot_set(hud, 2, g[F_DEPTH])
572 gh_slot_set(hud, 3, g[F_KILLS])
573 gh_slot_set(hud, 4, g[F_RATION]+g[F_FEAST])
574 var z: i64=0
575 while z<72000 { fb[z]=0 as u8; z=z+1 }
576 gh_render(hud, fb, 200, 120, font, labels, mlabels)
577 let fk: i64 = gh_frame_ck(fb, 200, 120)
578 if t>0 { if fk!=prev { moved=moved+1 } }
579 prev=fk
580 chain = (chain*131 + (fk & 0xFFFFFFFF)) & 0x7FFFFFFFFFFFFFFF
581 t=t+1
582 }
583 out[0]=moved
584 out[1]=gh_ink(fb, 200, 120)
585 out[2]=hud[GH_F_NSEL]
586 return chain
587}
588// T11/T12 helpers. nh_decint writes v0 in decimal at b[at..], returns the new cursor.
589// nh_clearfb / nh_filled / nh_fbck operate on the i64 trimesh framebuffer (same shapes the
590// certified nx_bunny_gate uses on this exact part chain).
591func nh_decint(b: *u8, at: i64, v0: i64) -> i64 {
592 var a: i64 = at
593 var v: i64 = v0
594 if v < 0 { b[a]=45 as u8; a=a+1; v=0-v }
595 let t: *u8 = sys_mmap(24)
596 var k: i64 = 0
597 if v == 0 { t[0]=48 as u8; k=1 }
598 while v > 0 { t[k]=(48+(v%10)) as u8; v=v/10; k=k+1 }
599 var q: i64 = k-1
600 while q >= 0 { b[a]=t[q]; a=a+1; q=q-1 }
601 return a
602}
603func nh_clearfb(fb: *i64, n: i64, c: i64) -> i64 { var i: i64=0; while i<n { fb[i]=c; i=i+1 } return 0 }
604func nh_filled(fb: *i64, n: i64, bg: i64) -> i64 { var c: i64=0; var i: i64=0; while i<n { if fb[i]!=bg { c=c+1 } i=i+1 } return c }
605func nh_fbck(fb: *i64, n: i64) -> i64 { var ck: i64=1469598103; var i: i64=0; while i<n { ck = nh_ckv(ck, fb[i]); i=i+1 } return ck }
606// run until win or maxturns; returns first-killed handle (the stale witness)
607func nh_run_full(g: *i64) -> i64 {
608 var firstkill: i64=0
609 var go: i64=1
610 while go==1 {
611 if g[F_WIN]==1 { go=0 }
612 if g[F_TURN]>=NH_MAXT { go=0 }
613 if g[F_HP]<=0 { go=0 }
614 if go==1 {
615 let kh: i64 = nh_tick(g)
616 if kh!=0 { if firstkill==0 { firstkill=kh } }
617 }
618 }
619 return firstkill
620}
621
622func main() -> i64 {
623 ww("=== nx_wire_nethack_gate: does NetHack's genre loop actually run on the certified parts? ===\n\n")
624 var pass: i64 = 0
625 var checks: i64 = 0
626 var mask: i64 = 0
627 let SEED: i64 = 4242
628 let g1: *i64 = sys_mmap(NG_WORDS*8) as *i64
629 let g2: *i64 = sys_mmap(NG_WORDS*8) as *i64
630 let S: *i64 = sys_mmap(NH_NSV*8+64) as *i64
631 let S2: *i64 = sys_mmap(NH_NSV*8+64) as *i64
632
633 // ---------- T1 procgen-world (bit 3): deterministic, seed-diverse, stairs reachable ----------
634 checks=checks+1
635 nh_new(g1, SEED)
636 nh_new(g2, SEED)
637 let cka: i64 = nh_ck(g1)
638 let ckb: i64 = nh_ck(g2)
639 nh_new(g2, SEED+1)
640 let grid1: *i64 = (g1 as i64 + NG_O_GRID*8) as *i64
641 let grid2: *i64 = (g2 as i64 + NG_O_GRID*8) as *i64
642 var diff: i64=0
643 var i: i64=0
644 while i<NH_W*NH_H { if grid1[i]!=grid2[i] { diff=diff+1 } i=i+1 }
645 let pg1: *i64 = (g1 as i64 + NG_O_G*8) as *i64
646 let pf1: *i64 = (g1 as i64 + NG_O_F*8) as *i64
647 let pc1: *i64 = (g1 as i64 + NG_O_CAME*8) as *i64
648 let po1: *i64 = (g1 as i64 + NG_O_OPENF*8) as *i64
649 let pz1: *i64 = (g1 as i64 + NG_O_CLOSED*8) as *i64
650 let pp1: *i64 = (g1 as i64 + NG_O_PATH*8) as *i64
651 let plen0: i64 = pf_astar(grid1, NH_W, NH_H, 1, 1, g1[F_SX], g1[F_SY], pg1, pf1, pc1, po1, pz1, pp1)
652 // every monster and item sits on a floor cell
653 let a1: *i64 = (g1 as i64 + NG_O_ENT*8) as *i64
654 let item1: *i64 = (g1 as i64 + NG_O_ITEM*8) as *i64
655 var badcell: i64=0
656 var k: i64=0
657 while k<en_count(a1) {
658 let h: i64 = en_nth(a1,k)
659 let mx: i64 = en_get(a1,h,0)
660 let my: i64 = en_get(a1,h,1)
661 if grid1[my*NH_W+mx]!=0 { badcell=badcell+1 }
662 k=k+1
663 }
664 k=0
665 while k<NH_NITEM {
666 let ix: i64=item1[k*3]
667 let iy: i64=item1[k*3+1]
668 if grid1[iy*NH_W+ix]!=0 { badcell=badcell+1 }
669 k=k+1
670 }
671 var t1: i64=0
672 if cka==ckb { if diff>=30 { if plen0>=1 { if badcell==0 {
673 ww("T1 GREEN procgen: same seed identical (ck "); wn(cka)
674 ww("), seed+1 differs in "); wn(diff); ww(" cells, stairs reachable (path "); wn(plen0)
675 ww("), 0 off-floor placements\n")
676 t1=1; pass=pass+1
677 mask=mask+nbit(3)
678 } } } }
679 if t1==0 {
680 ww("T1 RED procgen: ckA="); wn(cka); ww(" ckB="); wn(ckb)
681 ww(" diff="); wn(diff); ww(" plen="); wn(plen0); ww(" badcell="); wn(badcell); ww("\n")
682 }
683
684 // ---------- T2 pathfinding-ai (bit 7): the A* path is valid cell-by-cell ----------
685 checks=checks+1
686 var pathok: i64=1
687 i=1
688 while i<=plen0 {
689 let c0: i64 = pp1[i-1]
690 let c1: i64 = pp1[i]
691 let x0: i64 = c0 % NH_W
692 let y0: i64 = c0 / NH_W
693 let x1: i64 = c1 % NH_W
694 let y1: i64 = c1 / NH_W
695 let ad: i64 = nabs(x1-x0)+nabs(y1-y0)
696 if ad!=1 { pathok=0 }
697 if grid1[c1]!=0 { pathok=0 }
698 i=i+1
699 }
700 var t2: i64=0
701 if plen0>=1 { if pathok==1 {
702 ww("T2 GREEN pathfinding: pf_astar path of "); wn(plen0)
703 ww(" steps is 4-adjacent floor cell-by-cell (the part drives every player move)\n")
704 t2=1; pass=pass+1
705 mask=mask+nbit(7)
706 } }
707 if t2==0 { ww("T2 RED pathfinding: plen="); wn(plen0); ww(" pathok="); wn(pathok); ww("\n") }
708
709 // ---------- full deterministic run (the actual game) ----------
710 nh_new(g1, SEED)
711 let fk: i64 = nh_run_full(g1)
712 let a1b: *i64 = (g1 as i64 + NG_O_ENT*8) as *i64
713 let fck: i64 = nh_ck(g1)
714 let runwin: i64 = g1[F_WIN]
715 let runturn: i64 = g1[F_TURN]
716 let runkills: i64 = g1[F_KILLS]
717 let runxp: i64 = g1[F_XP]
718 let runlvl: i64 = g1[F_LVL]
719 ww(" [run] turns="); wn(g1[F_TURN]); ww(" depth="); wn(g1[F_DEPTH]); ww(" win="); wn(g1[F_WIN])
720 ww(" kills="); wn(g1[F_KILLS]); ww(" xp="); wn(g1[F_XP]); ww(" level="); wn(g1[F_LVL])
721 ww(" hp="); wn(g1[F_HP]); ww(" picked="); wn(g1[F_PICKED]); ww(" used="); wn(g1[F_USED])
722 ww(" combines="); wn(g1[F_COMB]); ww(" psteps="); wn(g1[F_PSTEPS]); ww(" bites="); wn(g1[F_BITES])
723 ww(" ck="); wn(fck); ww("\n")
724
725 // ---------- T3 entity-component-sim (bit 6): stale handle refused, census exact ----------
726 checks=checks+1
727 var t3: i64=0
728 var stale: i64=0-1
729 if fk!=0 { stale = en_valid(a1b, fk) }
730 let alive: i64 = en_count(a1b)
731 let census: i64 = g1[F_SPAWNED] - g1[F_KILLS] - g1[F_ABAND]
732 if fk!=0 { if stale==0 { if alive==census {
733 ww("T3 GREEN entity store: first-killed monster's handle "); wn(fk)
734 ww(" is DETECTABLY STALE (en_valid=0); census exact: spawned "); wn(g1[F_SPAWNED])
735 ww(" - kills "); wn(g1[F_KILLS]); ww(" - abandoned-on-descend "); wn(g1[F_ABAND])
736 ww(" = alive "); wn(alive); ww("\n")
737 t3=1; pass=pass+1
738 mask=mask+nbit(6)
739 } } }
740 if t3==0 {
741 ww("T3 RED entity: firstkill="); wn(fk); ww(" stale-valid="); wn(stale)
742 ww(" alive="); wn(alive); ww(" census="); wn(census); ww("\n")
743 }
744
745 // ---------- T4 rpg-stats-progression (bit 9): curve honored, level inverse exact, hp derived ----------
746 checks=checks+1
747 let wantlvl: i64 = rs_level_for_xp(g1[F_XP], NH_XB, NH_XQ)
748 let maxhp: i64 = rs_derived_hp(NH_CON, g1[F_LVL], NH_HPB, NH_HPC, NH_HPL)
749 let thresh: i64 = rs_xp_for_level(g1[F_LVL], NH_XB, NH_XQ)
750 var t4: i64=0
751 if g1[F_XP]>0 { if g1[F_LVL]>1 { if g1[F_LVL]==wantlvl { if g1[F_HP]<=maxhp { if g1[F_XP]>=thresh {
752 ww("T4 GREEN rpgstats: xp "); wn(g1[F_XP]); ww(" -> level "); wn(g1[F_LVL])
753 ww(" == rs_level_for_xp EXACT (threshold "); wn(thresh); ww(" <= xp), hp "); wn(g1[F_HP])
754 ww(" <= derived max "); wn(maxhp); ww("\n")
755 t4=1; pass=pass+1
756 mask=mask+nbit(9)
757 } } } } }
758 if t4==0 {
759 ww("T4 RED rpgstats: xp="); wn(g1[F_XP]); ww(" lvl="); wn(g1[F_LVL])
760 ww(" want="); wn(wantlvl); ww(" hp="); wn(g1[F_HP]); ww(" maxhp="); wn(maxhp); ww("\n")
761 }
762
763 // ---------- T5 inventory-crafting (bit 11): the ledger conserves exactly ----------
764 checks=checks+1
765 // picked rations = held + eaten + 2*crafted ; crafted feasts = held + eaten
766 let led1: i64 = g1[F_RATION] + g1[F_EATR] + 2*g1[F_COMB]
767 let led2: i64 = g1[F_FEAST] + g1[F_EATF]
768 var t5: i64=0
769 if g1[F_PICKED]>=3 { if g1[F_USED]>=1 { if g1[F_COMB]>=1 {
770 if g1[F_PICKED]==led1 { if g1[F_COMB]==led2 {
771 ww("T5 GREEN inventory-crafting: picked "); wn(g1[F_PICKED]); ww(" == held ")
772 wn(g1[F_RATION]); ww(" + eaten "); wn(g1[F_EATR]); ww(" + 2*crafted "); wn(g1[F_COMB])
773 ww("; feasts "); wn(g1[F_COMB]); ww(" == held "); wn(g1[F_FEAST]); ww(" + eaten "); wn(g1[F_EATF])
774 ww(" (conserved, "); wn(g1[F_USED]); ww(" consumed)\n")
775 t5=1; pass=pass+1
776 mask=mask+nbit(11)
777 } }
778 } } }
779 if t5==0 {
780 ww("T5 RED inventory: picked="); wn(g1[F_PICKED]); ww(" led1="); wn(led1)
781 ww(" comb="); wn(g1[F_COMB]); ww(" led2="); wn(led2); ww(" used="); wn(g1[F_USED]); ww("\n")
782 }
783
784 // ---------- T6 save-load TRANSPARENCY (bit 15, FLAGSHIP) ----------
785 checks=checks+1
786 // run A: continuous 0..T2W, banking a checksum EVERY turn in the window T1W..T2W
787 let bank: *i64 = sys_mmap((NH_T2W-NH_T1W+2)*8) as *i64
788 nh_new(g1, SEED)
789 var t: i64=0
790 while t<NH_T1W { nh_tick(g1); t=t+1 }
791 // non-vacuity guard: the world at the save point must be NON-TRIVIAL
792 let g1a: *i64 = (g1 as i64 + NG_O_ENT*8) as *i64
793 let nv_alive: i64 = en_count(g1a)
794 let nv_xp: i64 = g1[F_XP]
795 let nv_picked: i64 = g1[F_PICKED]
796 let ckt1: i64 = nh_ck(g1)
797 bank[0]=ckt1
798 t=NH_T1W
799 while t<NH_T2W { nh_tick(g1); t=t+1; let c: i64 = nh_ck(g1); bank[t-NH_T1W]=c }
800 let contwin: i64 = g1[F_WIN]
801 // run B: fresh game to T1W, SAVE, load into a ZEROED arena, resume to T2W comparing every turn
802 nh_new(g2, SEED)
803 t=0
804 while t<NH_T1W { nh_tick(g2); t=t+1 }
805 nh_serialize(g2,S2)
806 let sr: i64 = gs_save("knowledge/nx_wire_nethack_mid.sav" as *u8, NH_SCHEMA, S2, NH_NSV, 20260720)
807 var zi: i64=0
808 while zi<NG_WORDS { g2[zi]=0; zi=zi+1 } // ZERO the whole arena: nothing survives but the file
809 let Sl: *i64 = sys_mmap(NH_NSV*8+64) as *i64
810 let lr: i64 = gs_load("knowledge/nx_wire_nethack_mid.sav" as *u8, Sl, NH_NSV, 0 as *i64)
811 nh_restore(g2, Sl)
812 let ckr: i64 = nh_ck(g2)
813 var mismatch: i64=0
814 if ckr!=bank[0] { mismatch=mismatch+1 }
815 t=NH_T1W
816 while t<NH_T2W {
817 nh_tick(g2)
818 t=t+1
819 let c2: i64 = nh_ck(g2)
820 if c2!=bank[t-NH_T1W] { mismatch=mismatch+1 }
821 }
822 // field-by-field final compare
823 nh_serialize(g1,S)
824 nh_serialize(g2,S2)
825 var fdiff: i64=0
826 i=0
827 while i<NH_NSV { if S[i]!=S2[i] { fdiff=fdiff+1 } i=i+1 }
828 var t6: i64=0
829 if sr>0 { if lr==NH_NSV { if mismatch==0 { if fdiff==0 { if nv_alive>=1 { if nv_xp>0 { if nv_picked>=1 { if contwin==0 {
830 ww("T6 GREEN save/load TRANSPARENT: save at turn "); wn(NH_T1W)
831 ww(" -> load into ZEROED arena -> resume to "); wn(NH_T2W)
832 ww(": all "); wn(NH_T2W-NH_T1W+1); ww(" per-turn checksums identical, 0/"); wn(NH_NSV)
833 ww(" state fields differ (non-trivial world: alive="); wn(nv_alive)
834 ww(" xp="); wn(nv_xp); ww(" picked="); wn(nv_picked); ww(")\n")
835 t6=1; pass=pass+1
836 mask=mask+nbit(15)
837 } } } } } } } }
838 if t6==0 {
839 ww("T6 RED transparency: save="); wn(sr); ww(" load="); wn(lr)
840 ww(" ck-mismatches="); wn(mismatch); ww(" field-diffs="); wn(fdiff)
841 ww(" alive="); wn(nv_alive); ww(" xp="); wn(nv_xp); ww(" picked="); wn(nv_picked)
842 ww(" contwin="); wn(contwin); ww("\n")
843 }
844
845 // ---------- T7 corruption refused, state untouched ----------
846 checks=checks+1
847 let lenp: *i64 = sys_mmap(8) as *i64
848 lenp[0]=0
849 let raw: *u8 = sys_read_file("knowledge/nx_wire_nethack_mid.sav" as *u8, lenp)
850 var t7: i64=0
851 if (raw as i64)!=0 {
852 let rl: i64 = lenp[0]
853 let flip: i64 = 48+24 // a payload byte (past the 48B header)
854 raw[flip] = (raw[flip] as i64 ^ 255) as u8
855 let cfd: i64 = sys_openat_wr("knowledge/nx_wire_nethack_corrupt.sav" as *u8, 0x1a4)
856 if cfd>=0 {
857 sys_write(cfd, raw, rl)
858 sys_close(cfd)
859 let Sc: *i64 = sys_mmap(NH_NSV*8+64) as *i64
860 Sc[0]=777777
861 let cr: i64 = gs_load("knowledge/nx_wire_nethack_corrupt.sav" as *u8, Sc, NH_NSV, 0 as *i64)
862 if cr==(0-6) { if Sc[0]==777777 { t7=1 } }
863 if t7==1 {
864 ww("T7 GREEN corruption refused LOUD (rc=-6 CHECKSUM-CORRUPT) and caller state untouched\n")
865 pass=pass+1
866 } else {
867 ww("T7 RED corrupt load rc="); wn(cr); ww(" sentinel="); wn(Sc[0]); ww("\n")
868 }
869 } else { ww("T7 RED cannot write corrupt copy\n") }
870 } else { ww("T7 RED cannot read the mid-save back\n") }
871
872 // ---------- T8 determinism: two independent full runs, bit-identical ----------
873 checks=checks+1
874 nh_new(g2, SEED)
875 nh_run_full(g2)
876 let fck2: i64 = nh_ck(g2)
877 var t8: i64=0
878 if fck==fck2 { if runwin==g2[F_WIN] {
879 ww("T8 GREEN deterministic: two independent full runs -> identical final checksum "); wn(fck)
880 ww(" at turn "); wn(g2[F_TURN]); ww("\n")
881 t8=1; pass=pass+1
882 } }
883 if t8==0 { ww("T8 RED run1 ck="); wn(fck); ww(" run2 ck="); wn(fck2); ww("\n") }
884
885 // ---------- T9 anti-vacuity: the LOOP is a real game that was actually played and WON ----------
886 checks=checks+1
887 var t9: i64=0
888 if g2[F_WIN]==1 { if g2[F_TURN]>20 { if g2[F_TURN]<NH_MAXT { if g2[F_KILLS]>=4 { if g2[F_DEPTH]==NH_DEPTH+1 { if g2[F_HP]>0 {
889 ww("T9 GREEN the game was PLAYED and WON: "); wn(NH_DEPTH); ww(" dungeon levels cleared in ")
890 wn(g2[F_TURN]); ww(" turns, "); wn(g2[F_KILLS]); ww(" kills, survived at hp "); wn(g2[F_HP]); ww("\n")
891 t9=1; pass=pass+1
892 } } } } } }
893 if t9==0 {
894 ww("T9 RED loop hollow: win="); wn(g2[F_WIN]); ww(" turns="); wn(g2[F_TURN])
895 ww(" kills="); wn(g2[F_KILLS]); ww(" depth="); wn(g2[F_DEPTH]); ww(" hp="); wn(g2[F_HP]); ww("\n")
896 }
897
898 // ---------- T10 ui-menus-hud (bit 18): menu CAUSES the eats; HUD binds live state per turn ----------
899 checks=checks+1
900 let hud2: *i64 = (g2 as i64 + NG_O_HUD*8) as *i64
901 let menusel: i64 = hud2[GH_F_NSEL]
902 let o10a: *i64 = sys_mmap(4*8) as *i64
903 let o10b: *i64 = sys_mmap(4*8) as *i64
904 let ch10a: i64 = nh_hudchain(SEED, NH_T2W, o10a)
905 let ch10b: i64 = nh_hudchain(SEED, NH_T2W, o10b)
906 var t10: i64=0
907 if ch10a==ch10b { if o10a[0]>=2 { if o10a[1]>=NH_HUDINK { if menusel>=1 { if menusel==g2[F_USED] {
908 ww("T10 GREEN ui-menus-hud: "); wn(menusel); ww(" eats ALL caused by menu selections (nsel==used), HUD ")
909 ww("render chain deterministic over "); wn(NH_T2W); ww(" turns ("); wn(o10a[0])
910 ww(" frame changes, "); wn(o10a[1]); ww(" ink px)\n")
911 t10=1; pass=pass+1
912 mask=mask+nbit(18)
913 } } } } }
914 if t10==0 {
915 ww("T10 RED ui-menus-hud: chA="); wn(ch10a); ww(" chB="); wn(ch10b)
916 ww(" changes="); wn(o10a[0]); ww(" ink="); wn(o10a[1])
917 ww(" nsel="); wn(menusel); ww(" used="); wn(g2[F_USED]); ww("\n")
918 }
919
920 // ---------- T11 text-render-typography (bit 14): the status line is GLYPH TEXT bound to LIVE
921 // run state, with EXACT ink accounting DERIVED FROM THE FONT TABLE ITSELF (zero picked numbers:
922 // expected ink = sum of set bits over the exact glyphs drawn; cells disjoint at 9px pitch = 8px
923 // glyph + 1px gap, drawn inside 200x120 with no clipping, so equality is exact by construction).
924 // A stub that blits a cached bitmap or a constant blob cannot match the per-state glyph
925 // population, and a text renderer not bound to state cannot make the mid-state frame differ.
926 checks=checks+1
927 let font11: *u8 = font8x8_table()
928 let fb11: *u8 = sys_mmap(72000)
929 let fb11b: *u8 = sys_mmap(72000)
930 let fb11c: *u8 = sys_mmap(72000)
931 let s11: *u8 = sys_mmap(64)
932 var sl: i64 = 0
933 s11[sl]=84 as u8; sl=sl+1
934 sl = nh_decint(s11, sl, g2[F_TURN])
935 s11[sl]=88 as u8; sl=sl+1
936 sl = nh_decint(s11, sl, g2[F_XP])
937 s11[sl]=75 as u8; sl=sl+1
938 sl = nh_decint(s11, sl, g2[F_KILLS])
939 s11[sl]=0 as u8
940 let s11c: *u8 = sys_mmap(64)
941 var slc: i64 = 0
942 s11c[slc]=84 as u8; slc=slc+1
943 slc = nh_decint(s11c, slc, NH_T1W)
944 s11c[slc]=88 as u8; slc=slc+1
945 slc = nh_decint(s11c, slc, nv_xp)
946 s11c[slc]=0 as u8
947 var exp11: i64 = 0
948 var ci11: i64 = 0
949 while s11[ci11]!=(0 as u8) {
950 let ch11: i64 = s11[ci11] & 0xff
951 let go11: i64 = (ch11-0x20)*8
952 var r11: i64 = 0
953 while r11<8 {
954 var b11: i64 = font11[go11+r11] & 0xff
955 while b11>0 { exp11 = exp11 + (b11%2); b11=b11/2 }
956 r11=r11+1
957 }
958 gh_glyph(fb11, 200, 120, font11, ch11, 4+ci11*9, 8)
959 gh_glyph(fb11b, 200, 120, font11, ch11, 4+ci11*9, 8)
960 ci11=ci11+1
961 }
962 var cc11: i64 = 0
963 while s11c[cc11]!=(0 as u8) {
964 gh_glyph(fb11c, 200, 120, font11, s11c[cc11] & 0xff, 4+cc11*9, 8)
965 cc11=cc11+1
966 }
967 let ink11: i64 = gh_ink(fb11, 200, 120)
968 let cka11: i64 = gh_frame_ck(fb11, 200, 120)
969 let ckb11: i64 = gh_frame_ck(fb11b, 200, 120)
970 let ckc11: i64 = gh_frame_ck(fb11c, 200, 120)
971 var t11: i64=0
972 if exp11>0 { if ink11==exp11 { if cka11==ckb11 { if ckc11!=cka11 { if sl>=6 { if slc>=4 {
973 ww("T11 GREEN text-render: status line "); ww(s11); ww(" drawn as "); wn(sl)
974 ww(" glyphs, ink "); wn(ink11); ww(" px == font-table-derived expectation EXACT, deterministic, ")
975 ww("and the mid-save state renders a DIFFERENT frame (state-bound, not a cached bitmap)\n")
976 t11=1; pass=pass+1
977 mask=mask+nbit(14)
978 } } } } } }
979 if t11==0 {
980 ww("T11 RED text-render: exp="); wn(exp11); ww(" ink="); wn(ink11)
981 ww(" ckA="); wn(cka11); ww(" ckB="); wn(ckb11); ww(" ckC="); wn(ckc11)
982 ww(" sl="); wn(sl); ww(" slc="); wn(slc); ww("\n")
983 }
984
985 // ---------- T12 asset-pipeline-import (bit 25): the WON game's victory screen imports and
986 // renders the standard-corpus mesh through the certified OBJ import chain (nx_objload ->
987 // nx_trimesh), the exact part nx_bunny_gate certifies. Provenance is proven by the EXACT
988 // banked Stanford canonical counts (35947 vertices / 69451 triangles); the scale/camera/floor
989 // values are nx_bunny_gate's own calibrated view, reused not re-picked (composition reuses the
990 // incumbent's calibration). Composition condition: this only renders on a WON run -- the way a
991 // real title imports display assets for its win screen.
992 checks=checks+1
993 let tw12: i64 = 300
994 let th12: i64 = 300
995 let npx12: i64 = tw12*th12
996 let bg12: i64 = 24 + 26*256 + 34*65536
997 let fb12: *i64 = sys_mmap(npx12*8) as *i64
998 let zb12: *i64 = sys_mmap(npx12*8) as *i64
999 tm_set_spec(0)
1000 tm_set_tex(0)
1001 let ok12: i64 = obj_load("knowledge/stdassets/bunny.obj" as *u8, 1300, 205+200*256+195*65536)
1002 let nv12: i64 = tm_nv()
1003 let nt12: i64 = tm_nt()
1004 nh_clearfb(fb12, npx12, bg12)
1005 trimesh_zclear(zb12, npx12)
1006 trimesh_render(fb12, zb12, tw12, th12, 1300, 0-250, 2300, 520, 2)
1007 let fil12: i64 = nh_filled(fb12, npx12, bg12)
1008 let rck12: i64 = nh_fbck(fb12, npx12)
1009 nh_clearfb(fb12, npx12, bg12)
1010 trimesh_zclear(zb12, npx12)
1011 trimesh_render(fb12, zb12, tw12, th12, 1300, 0-250, 2300, 520, 2)
1012 let rck12b: i64 = nh_fbck(fb12, npx12)
1013 var t12: i64=0
1014 if g2[F_WIN]==1 { if ok12==1 { if nv12==35947 { if nt12==69451 { if tm_ovf()==0 { if fil12>npx12/12 { if rck12==rck12b {
1015 ww("T12 GREEN asset-import: WON run's victory screen renders the imported standard mesh -- ")
1016 ww("EXACT canonical Stanford counts (35947/69451, provenance by the numbers), ")
1017 wn(fil12); ww(" px rasterized (floor npx/12 = the part's own gate calibration), deterministic\n")
1018 t12=1; pass=pass+1
1019 mask=mask+nbit(25)
1020 } } } } } } }
1021 if t12==0 {
1022 ww("T12 RED asset-import: win="); wn(g2[F_WIN]); ww(" ok="); wn(ok12)
1023 ww(" nv="); wn(nv12); ww(" nt="); wn(nt12); ww(" ovf="); wn(tm_ovf())
1024 ww(" filled="); wn(fil12); ww(" ckA="); wn(rck12); ww(" ckB="); wn(rck12b); ww("\n")
1025 }
1026
1027 // ---------- wiring evidence artifact: measured mask + run summary (board cross-checks payload[0]) ----------
1028 let art: *i64 = sys_mmap(16*8) as *i64
1029 art[0]=mask
1030 art[1]=fck
1031 art[2]=runturn
1032 art[3]=runkills
1033 art[4]=runxp
1034 art[5]=runlvl
1035 art[6]=NH_DEPTH
1036 art[7]=runwin
1037 let aw: i64 = gs_save("knowledge/nx_wire_nethack.sav" as *u8, NH_ART_SCHEMA, art, 8, 20260720)
1038 ww("\nwiring artifact knowledge/nx_wire_nethack.sav bytes="); wn(aw)
1039 ww(" exercised_mask="); wn(mask); ww(" (bits 3,6,7,9,11,14,15,18,25 when all their teeth pass)\n")
1040
1041 ww("\n=== nx_wire_nethack_gate "); wn(pass); ww("/"); wn(checks)
1042 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
1043 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
1044 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
1045 let ctr__dry: *i64 = gv_ctr()
1046 ctr__dry[0] = pass
1047 ctr__dry[1] = checks
1048 let rc__dry: i64 = gv_verdict("WIRE-NETHACK-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
1049 sys_exit(rc__dry)
1050 return rc__dry
1051}