code wiki / _hdl_build / nx_gx9_saveload_gate.nx

nx_gx9_saveload_gate.nx source

↩ module page · 247 lines · 12912 B

1// nx_gx9_saveload_gate.nx -- Gx-9: SAVE / LOAD, the capability the gamebench ruler ranks #1 across the 2// ingested corpus (it blocks 11 of 12 real titles). Demand-ranked by measurement, not chosen by taste. 3// 4// ★THE TEST THAT ONLY WE CAN RUN. Most engines can only check that a save round-trips its fields. Because 5// our pipeline is bit-deterministic, we can assert the far stronger property: SAVING AND RELOADING MUST BE 6// INDISTINGUISHABLE FROM NEVER HAVING SAVED. Play 14 ticks straight and keep the final frame. Then play 7, 7// save, load into a FRESH state, play the remaining 7 -- and require the final frame to be BYTE-IDENTICAL 8// and the final state to match field for field. That catches the entire class of "the save forgot something" 9// bugs, which field round-trip tests structurally cannot see. 10// 11// The other half is refusal: a damaged save must FAIL WITH A REASON, never half-load. A corrupted save that 12// partially applies strands the player in an impossible world and reads as a game bug forever. 13// license_tier: ORIGINAL expect_exit: 0 14import "nx_game_world.nx" 15import "nx_game_save.nx" 16import "nx_png_write.nx" 17 18const SL_STEP: i64 = 8192 19const SL_TURN: i64 = 3217 20const SL_TICKS: i64 = 14 21const SL_N: i64 = 5 22const SL_PICKR: i64 = 4915 23const SL_EYE: i64 = 6600 24const SL_WORDS: i64 = 6 + SL_N // ppx ppz yaw tick collected won + alive[SL_N] 25 26func sl_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 27func sl_pn(v: i64) -> i64 { let b: *u8=sys_mmap(32) as *u8; var x: i64=v; var ng: i64=0; if x<0{ng=1;x=0-x} var i: i64=31; if x==0{b[i]=48 as u8;i=i-1} while x>0{b[i]=(48+x%10) as u8;x=x/10;i=i-1} if ng==1{b[i]=45 as u8;i=i-1} sys_write(1,(b as i64+i+1) as *u8,31-i); return 0 } 28 29func sl_script(t: i64) -> i64 { 30 if t==2 { return 2 } 31 if t==3 { return 3 } 32 if t==8 { return 2 } 33 if t==9 { return 3 } 34 return 1 35} 36 37func sl_init(st: *i64) -> i64 { 38 st[0] = 14*GW_CELL; st[1] = 4*GW_CELL; st[2] = 0 39 st[3] = 0; st[4] = 0; st[5] = 0 40 var k: i64 = 0 41 while k < SL_N { st[6+k] = 1; k = k + 1 } 42 return 0 43} 44 45// Advance the world from `st` for ticks [from,to), RENDERING EVERY TICK and folding each frame into a 46// running checksum -- so the comparison covers the whole resumed span, not just its last frame. `cap` names 47// the tick whose frame is copied to dst (pass a negative to capture nothing). Rendering every tick matters: 48// the final tick leaves the player on the peak where the remaining grid falls below the viewport, so a 49// last-frame-only comparison would compare two empty skies and pass vacuously. 50func sl_sim(base: i64, st: *i64, from: i64, to: i64, cap: i64, dst: *u8) -> i64 { 51 let ex: *i64 = sys_mmap(8*8) as *i64 52 let ez: *i64 = sys_mmap(8*8) as *i64 53 let scratch: *u8 = sys_mmap(ww()*hh()*3+16) 54 var ck: i64 = 0 55 var k: i64 = 0 56 while k < SL_N { ex[k] = 14*GW_CELL; ez[k] = (8 + k*4)*GW_CELL; k = k + 1 } 57 var t: i64 = from 58 while t < to { 59 let a: i64 = sl_script(t) 60 if a==1 { st[0] = st[0] + it_sin4096(st[2])*SL_STEP/4096; st[1] = st[1] + it_cos4096(st[2])*SL_STEP/4096 } 61 if a==2 { st[2] = st[2] + SL_TURN } 62 if a==3 { st[2] = st[2] - SL_TURN } 63 var c: i64 = 0 64 while c < SL_N { 65 if st[6+c] == 1 { 66 if gw_dist2(st[0], st[1], ex[c], ez[c]) <= SL_PICKR * SL_PICKR { st[6+c] = 0; st[4] = st[4] + 1 } 67 } 68 c = c + 1 69 } 70 if st[4] >= SL_N { st[5] = 1 } 71 st[3] = t + 1 72 sl_render(base, st, ex, ez, scratch) 73 var q: i64 = 0 74 while q < ww()*hh()*3 { ck = ck + (scratch[q] as i64)*(1 + q % 7); q = q + 97 } 75 if t == cap { var w: i64 = 0; while w < ww()*hh()*3 { dst[w] = scratch[w]; w = w + 1 } } 76 t = t + 1 77 } 78 return ck 79} 80 81// render the world implied by `st` -- the same composition the shipped game uses 82func sl_render(base: i64, st: *i64, ex: *i64, ez: *i64, dst: *u8) -> i64 { 83 var gi: i64 = st[0] / GW_CELL; if gi < 0 { gi = 0 } if gi > GW_N { gi = GW_N } 84 var gj: i64 = st[1] / GW_CELL; if gj < 0 { gj = 0 } if gj > GW_N { gj = GW_N } 85 let eyey: i64 = gw_h(gi,gj) + SL_EYE 86 let lax: i64 = st[0] + it_sin4096(st[2]) * (6*GW_CELL) / 4096 87 let laz: i64 = st[1] + it_cos4096(st[2]) * (6*GW_CELL) / 4096 88 var pitch: i64 = (0 - ((gw_ground(lax, laz) + SL_EYE) - eyey)) * 4096 / (6*GW_CELL) 89 if pitch < (0-2800) { pitch = 0-2800 } 90 if pitch > 2800 { pitch = 2800 } 91 gw_build_terrain(base, 0, st[0], st[1], eyey) 92 sg_project_clip_pitch(base, st[2], pitch, 0) 93 sg_render(base, 74, 156, 62) 94 gw_build_terrain(base, 1, st[0], st[1], eyey) 95 sg_project_clip_pitch(base, st[2], pitch, 0) 96 sg_render_pass(base, 128, 112, 100) 97 gw_build_terrain(base, 2, st[0], st[1], eyey) 98 sg_project_clip_pitch(base, st[2], pitch, 0) 99 sg_render_pass(base, 232, 234, 240) 100 let live: i64 = gw_build_entities(base, ex, ez, ((st as i64) + 6*8) as *i64, SL_N, st[0], st[1], eyey, 1400, 5200) 101 if live > 0 { sg_project_clip_pitch(base, st[2], pitch, 0); sg_render_pass(base, 240, 200, 60) } 102 sg_ssao(base, 190) 103 let fb: *i64 = sg_fb(base) 104 var p: i64 = 0 105 while p < ww()*hh() { 106 let v: i64 = fb[p] 107 dst[p*3] = (v % 256) as u8 108 dst[p*3+1] = ((v/256) % 256) as u8 109 dst[p*3+2] = ((v/65536) % 256) as u8 110 p = p + 1 111 } 112 return 0 113} 114 115func main() -> i64 { 116 sl_puts("=== nx_gx9_saveload_gate -- save/load proven INDISTINGUISHABLE from never having saved ===\n" as *u8) 117 var fails: i64 = 0 118 let base: i64 = sys_mmap(swgpu_bytes()) as i64 119 let frameRef: *u8 = sys_mmap(ww()*hh()*3+16) 120 let frameSplit: *u8 = sys_mmap(ww()*hh()*3+16) 121 let stRef: *i64 = sys_mmap(8*32) as *i64 122 let stSplit: *i64 = sys_mmap(8*32) as *i64 123 let stLoad: *i64 = sys_mmap(8*32) as *i64 124 let path: *u8 = "knowledge/nx_gx9_save.bin\x00" as *u8 125 126 // ---- T1 a save round-trips every field exactly ---- 127 let scratch: *u8 = sys_mmap(ww()*hh()*3+16) 128 sl_init(stRef) 129 sl_sim(base, stRef, 0, 7, 0-1, scratch) 130 let wrote: i64 = gs_save(path, stRef, SL_WORDS) 131 let got: i64 = gs_load(path, stLoad, 32) 132 var t1: i64 = 1 133 if got != SL_WORDS { t1 = 0 } 134 var i: i64 = 0 135 while i < SL_WORDS { if stLoad[i] != stRef[i] { t1 = 0 } i = i + 1 } 136 sl_puts(" wrote "); sl_pn(wrote); sl_puts(" bytes, loaded "); sl_pn(got); sl_puts(" words\n" as *u8) 137 if t1==1 { sl_puts("T1 PASS every state field round-trips exactly\n" as *u8) } 138 else { fails=fails+1; sl_puts("T1 FAIL roundtrip\n" as *u8) } 139 140 // ---- T2 THE REAL TEST: straight run vs save/reload/resume must be BYTE-IDENTICAL ---- 141 sl_init(stRef) 142 sl_sim(base, stRef, 0, 7, 0-1, scratch) // play half 143 let ckRef: i64 = sl_sim(base, stRef, 7, SL_TICKS, 10, frameRef) // ...and just keep playing 144 sl_init(stSplit) 145 sl_sim(base, stSplit, 0, 7, 0-1, scratch) // play the same half 146 gs_save(path, stSplit, SL_WORDS) // SAVE 147 var z: i64 = 0 148 while z < 32 { stLoad[z] = 0; z = z + 1 } // FRESH state: nothing carried in memory 149 gs_load(path, stLoad, 32) // LOAD 150 let ckSplit: i64 = sl_sim(base, stLoad, 7, SL_TICKS, 10, frameSplit) // resume from the loaded state 151 var diff: i64 = 0 152 var p: i64 = 0 153 while p < ww()*hh()*3 { if frameRef[p] != frameSplit[p] { diff = diff + 1 } p = p + 1 } 154 sl_puts(" resumed-span frame checksum: continuous="); sl_pn(ckRef); sl_puts(" save-reload="); sl_pn(ckSplit); sl_puts("\n" as *u8) 155 if ckRef != ckSplit { diff = diff + 1 } // every tick in the span must match, not just one 156 var stdiff: i64 = 0 157 i = 0 158 while i < SL_WORDS { if stLoad[i] != stRef[i] { stdiff = stdiff + 1 } i = i + 1 } 159 sl_puts(" straight-run vs save/reload/resume: differing bytes="); sl_pn(diff) 160 sl_puts(" differing state fields="); sl_pn(stdiff); sl_puts("\n" as *u8) 161 // ★NON-VACUITY: "0 differing bytes" is worthless if BOTH frames are empty sky. Require the reference 162 // frame to actually contain rendered world before its equality means anything. 163 var refGeom: i64 = 0 164 var yy: i64 = 0 165 while yy < hh() { 166 let bgr: i64 = 26 + yy*36/hh() 167 let bgg: i64 = 28 + yy*34/hh() 168 let bgb: i64 = 42 + yy*30/hh() 169 var xx: i64 = 0 170 while xx < ww() { 171 let o: i64 = (yy*ww()+xx)*3 172 var d1: i64 = (frameRef[o] as i64)-bgr; if d1<0 {d1=0-d1} 173 var d2: i64 = (frameRef[o+1] as i64)-bgg; if d2<0 {d2=0-d2} 174 var d3: i64 = (frameRef[o+2] as i64)-bgb; if d3<0 {d3=0-d3} 175 if d1+d2+d3 > 18 { refGeom = refGeom + 1 } 176 xx = xx + 1 177 } 178 yy = yy + 1 179 } 180 sl_puts(" reference frame world pixels="); sl_pn(refGeom); sl_puts(" of "); sl_pn(ww()*hh()) 181 sl_puts(" (non-vacuity guard)\n" as *u8) 182 var t2: i64 = 0 183 if diff == 0 { if stdiff == 0 { if refGeom > 20000 { t2 = 1 } } } 184 if t2==1 { sl_puts("T2 PASS save/load is TRANSPARENT: resumed run byte-identical to the uninterrupted one, on a frame with real world in it\n" as *u8) } 185 else { fails=fails+1; sl_puts("T2 FAIL diff="); sl_pn(diff); sl_puts(" stdiff="); sl_pn(stdiff); sl_puts(" refGeom="); sl_pn(refGeom); sl_puts(" (if refGeom is low the comparison was VACUOUS)\n" as *u8) } 186 187 // ---- T3 a CORRUPTED save is refused, not half-applied ---- 188 gs_save(path, stRef, SL_WORDS) 189 let szp: *i64 = sys_mmap(16) as *i64 190 let raw: *u8 = sys_read_file(path, szp) 191 var t3: i64 = 0 192 if (raw as i64) != 0 { 193 raw[40] = ((raw[40] as i64) + 7) % 256 as u8 // flip a payload byte 194 let fd: i64 = sys_openat_wr(path, 420) 195 if fd >= 0 { sys_write(fd, raw, szp[0]); sys_close(fd) } 196 var probe: *i64 = sys_mmap(8*32) as *i64 197 var q: i64 = 0 198 while q < 32 { probe[q] = 0-999; q = q + 1 } // sentinel: must stay untouched 199 let rc: i64 = gs_load(path, probe, 32) 200 var untouched: i64 = 1 201 q = 0 202 while q < SL_WORDS { if probe[q] != (0-999) { untouched = 0 } q = q + 1 } 203 sl_puts(" corrupted save -> rc="); sl_pn(rc); sl_puts(" caller-state-untouched="); sl_pn(untouched); sl_puts("\n" as *u8) 204 if rc == GS_ERR_CHECKSUM { if untouched == 1 { t3 = 1 } } 205 } 206 if t3==1 { sl_puts("T3 PASS corruption REFUSED with a reason and the caller's state left untouched\n" as *u8) } 207 else { fails=fails+1; sl_puts("T3 FAIL corrupt save not properly refused\n" as *u8) } 208 209 // ---- T4 a save from a FUTURE version is refused rather than misparsed ---- 210 gs_save(path, stRef, SL_WORDS) 211 let raw2: *u8 = sys_read_file(path, szp) 212 var t4: i64 = 0 213 if (raw2 as i64) != 0 { 214 gs_put64(raw2, 8, 99) // pretend version 99 215 gs_put64(raw2, 0, GS_MAGIC) 216 let fd2: i64 = sys_openat_wr(path, 420) 217 if fd2 >= 0 { sys_write(fd2, raw2, szp[0]); sys_close(fd2) } 218 let rc2: i64 = gs_load(path, stLoad, 32) 219 sl_puts(" version-99 save -> rc="); sl_pn(rc2); sl_puts("\n" as *u8) 220 if rc2 == GS_ERR_VERSION { t4 = 1 } 221 } 222 if t4==1 { sl_puts("T4 PASS unknown save version refused (no silent misparse of a future format)\n" as *u8) } 223 else { fails=fails+1; sl_puts("T4 FAIL version not checked\n" as *u8) } 224 225 // ---- T5 a missing file and an oversized payload both fail cleanly ---- 226 let rc3: i64 = gs_load("knowledge/nx_gx9_absent.bin\x00" as *u8, stLoad, 32) 227 gs_save(path, stRef, SL_WORDS) 228 let rc4: i64 = gs_load(path, stLoad, 2) // capacity smaller than the payload 229 sl_puts(" missing-file rc="); sl_pn(rc3); sl_puts(" undersized-buffer rc="); sl_pn(rc4); sl_puts("\n" as *u8) 230 var t5: i64 = 0 231 if rc3 == GS_ERR_UNREADABLE { if rc4 == GS_ERR_TOOBIG { t5 = 1 } } 232 if t5==1 { sl_puts("T5 PASS missing file and undersized buffer each refused with their own code\n" as *u8) } 233 else { fails=fails+1; sl_puts("T5 FAIL clean-failure paths\n" as *u8) } 234 235 // ---- T6 artifact: the resumed frame (this is what the player sees after loading) ---- 236 nx_png_write_rgb("knowledge/nx_gx9_resumed.png\x00" as *u8, frameSplit, ww(), hh()) 237 let rb: *u8 = sys_read_file("knowledge/nx_gx9_resumed.png\x00" as *u8, szp) 238 var t6: i64 = 0 239 if (rb as i64)!=0 { if szp[0]>1000 { t6=1 } } 240 if t6==1 { sl_puts("T6 PASS resumed-session artifact written ("); sl_pn(szp[0]); sl_puts(" bytes)\n" as *u8) } 241 else { fails=fails+1; sl_puts("T6 FAIL png\n" as *u8) } 242 243 if fails==0 { sl_puts("GX9-SAVELOAD GREEN -- saving and reloading is indistinguishable from never having saved, and damaged saves are refused\n" as *u8); sys_exit(0); return 0 } 244 sl_puts("GX9-SAVELOAD RED fails="); sl_pn(fails); sl_puts("\n" as *u8) 245 sys_exit(1) 246 return 1 247}