code wiki / _hdl_build / nx_wire_nethack_gate.nx

nx_wire_nethack_gate.nx source

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