code wiki / _hdl_build / nx_wasm_thirdperson_gate.nx

nx_wasm_thirdperson_gate.nx source

↩ module page · 117 lines · 7011 B

1import "nx_gate_gn.nx" 2import "nx_gate_base.nx" 3// nx_wasm_thirdperson_gate.nx -- NATIVE gate for the browser-playable third-person explorer (nx_wasm_thirdperson). 4// T1 COMPLETE: collect every item then reach the goal -> win. 5// T2 RENDER REAL: the avatar is drawn GREEN at the screen CENTER (the follow-camera), on the world. 6// T3 CAMERA FOLLOWS: at an interior position the camera centers on the avatar. 7// T4 NEVER-BRICK (#26): deterministic, bounded, no float, base-relative. 8// expect_exit: 0 license_tier: ORIGINAL 9import "nx_wasm_thirdperson.nx" 10import "nx_png.nx" 11import "nx_syscalls.nx" 12import "nx_gate_verdict.nx" 13 14// TWO checker tiles. render_impl paints a 16 px checker in two shades, so a floor sample taken ONE 15// tile over lands on the OTHER shade and differs from the centre for reasons that have nothing to do 16// with the avatar -- which would let the tooth below pass on an empty frame. Two tiles over keeps the 17// same parity, so any difference the tooth sees is the avatar and nothing else. 18const TPG_TILE_PAIR: i64 = 32 19 20func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw(" 21" as *u8); return ok } 22 23func goto(base: i64, tx: i64, ty: i64) -> i64 { 24 let st: *i64 = (base + O_ST) as *i64 25 var g: i64 = 0 26 while g < 600 { 27 var inp: i64 = 0 28 if st[0] < tx { inp = inp | 8 } if st[0] > tx { inp = inp | 4 } 29 if st[1] < ty { inp = inp | 2 } if st[1] > ty { inp = inp | 1 } 30 if inp == 0 { g = 600 } else { tick_impl(base, inp); g = g + 1 } 31 } 32 return 0 33} 34 35func main() -> i64 { 36 gw("=== nx_wasm_thirdperson_gate: browser-playable third-person explorer, verified NATIVE before wasm ===\n" as *u8) 37 var pass: i64 = 0; var total: i64 = 0 38 let base: i64 = sys_mmap(2*1024*1024) as i64 39 let ix: *i64 = (base + O_IX) as *i64 40 let iy: *i64 = (base + O_IY) as *i64 41 let st: *i64 = (base + O_ST) as *i64 42 43 // ---- T1: collect every item then reach the goal ---- 44 init_impl(base) 45 var i: i64=0 46 while i < NI { goto(base, ix[i], iy[i]); i=i+1 } 47 goto(base, st[4], st[5]) 48 var t1ok: i64=1; if st[2] != NI {t1ok=0} if st[3] != 1 {t1ok=0} 49 total=total+1; if t1ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 50 gw("T1 complete: collected " as *u8); gn(st[2]); gw("/" as *u8); gn(NI); gw(" items then reached the goal, won=" as *u8); gn(st[3]); gw("\n" as *u8) 51 52 // ---- render a mid-explore frame (follow-camera) ---- 53 init_impl(base) 54 goto(base, ix[4], iy[4]) // walk to a central item 55 render_impl(base) 56 let fb: *i64 = (base + O_FB) as *i64 57 write_png(fb, W, H, "knowledge/nx_wasm_thirdperson_native.png" as *u8) 58 gw(" (live evidence: PNG knowledge/nx_wasm_thirdperson_native.png -- follow-camera: avatar centered, world scrolled, items+goal)\n" as *u8) 59 60 // ---- T2: render real -- THE AVATAR IS ACTUALLY IN THE PICTURE ---- 61 // ★WHY THIS IS A DIFFERENCE AND NOT A COLOUR TEST. The old predicate asked whether the centre 62 // pixel was "greener than red". The checker floor is rgb(40,70,46) and rgb(48,84,54) -- BOTH 63 // greener than red -- so it passed on a frame with NO AVATAR DRAWN AT ALL. It was measuring the 64 // terrain and reporting it as the avatar. Found 2026-08-14 by nx_vacuity_census after the same 65 // shape cost nx_wasm_craft_gate 58 green teeth over a world with no characters in it. 66 // The honest question is whether anything at the centre differs from the FLOOR, so this compares 67 // the centre against floor two tiles over. It names no colour, so it cannot rot when a palette 68 // moves, and it fails closed the moment the avatar draw is removed. 69 let terrA: i64 = fb[(H/2)*W + (W/2) + TPG_TILE_PAIR] 70 let centerA: i64 = fb[(H/2)*W + (W/2)] 71 var t2ok: i64=1; if centerA == terrA {t2ok=0} 72 total=total+1; if t2ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 73 gw("T2 render real: the centre is NOT the floor, so the avatar is in frame (centre=" as *u8); gn(centerA); gw(" floor-two-tiles-over=" as *u8); gn(terrA); gw(")\n" as *u8) 74 75 // ---- T5: neg-control-avatar-off-centre ---- 76 // A PRESENCE TEST WITH NO ABSENCE CASE IS UNVERIFIED. Park the avatar at the world corner where 77 // the camera CLAMPS -- it then leaves the screen centre while everything else about the frame is 78 // unchanged -- and the SAME measurement must now read plain floor. If this ever passes at the 79 // same time as T2 failing, the tooth is reading something other than the avatar. 80 st[0] = 0; st[1] = 0 81 render_impl(base) 82 let centerB: i64 = fb[(H/2)*W + (W/2)] 83 let terrB: i64 = fb[(H/2)*W + (W/2) + TPG_TILE_PAIR] 84 var t5ok: i64=1; if centerB != terrB {t5ok=0} 85 total=total+1; if t5ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 86 gw("T5 neg-control-avatar-off-centre: with the avatar parked at a clamped corner the centre reads plain floor (centre=" as *u8); gn(centerB); gw(" floor=" as *u8); gn(terrB); gw(")\n" as *u8) 87 88 // ---- T3: camera follows ---- 89 init_impl(base) 90 st[0] = 144; st[1] = 112 // interior 91 let camx: i64 = st[0] - W/2; let camy: i64 = st[1] - H/2 // expected (both in range) 92 var t3ok: i64=1 93 if camx < 0 { t3ok = 0 } if camx > WW-W { t3ok = 0 } 94 total=total+1; if t3ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 95 gw("T3 camera follows: at an interior tile the camera centers on the avatar (camx=" as *u8); gn(camx); gw(")\n" as *u8) 96 97 // ---- T4: determinism ---- 98 init_impl(base) 99 i=0; while i < NI { goto(base, ix[i], iy[i]); i=i+1 } goto(base, st[4], st[5]) 100 var t4ok: i64=1; if st[3] != 1 {t4ok=0} 101 total=total+1; if t4ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 102 gw("T4 never-brick (#26): deterministic re-run won=" as *u8); gn(st[3]); gw(", base-relative, no float, no sys_mmap in the core\n" as *u8) 103 104 gw("\n=== nx_wasm_thirdperson_gate " as *u8); gn(pass); gw("/" as *u8); gn(total) 105 // D001 ANCHOR: the rich line above is this gate's public signature and stays. The canonical 106 // verdict goes LAST because the estate's judge anchors by POSITION (gv_last_line takes the final 107 // line), which is what makes it safe against a gate that echoes the word elsewhere in its output. 108 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 109 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 110 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 111 let ctr__dry: *i64 = gv_ctr() 112 ctr__dry[0] = pass 113 ctr__dry[1] = total 114 let rc__dry: i64 = gv_verdict("WASM-THIRDPERSON-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 115 sys_exit(rc__dry) 116 return rc__dry 117}