code wiki / _hdl_build / nx_game_gen2.nx

nx_game_gen2.nx source

↩ module page · 297 lines · 13323 B

1// nx_game_gen2.nx -- the POLYMORPHIC game-system generator (X-GAME-GEN-001: "generate-a-game: 2// game systems as emitter SHAPES -> GAME SPEC -> Builder emits the game"). Generalizes 3// nx_game_gen (which hard-codes the dungenc table, dungeon-only) to ALL SIX proven game-system 4// FSM types by SOURCING each type's transition table from its build_<type>.spec -- the same DATA 5// the hand-authored _pe_<type> FSM (shape 18 STATE_MACHINE) was gated on. So the emitted game's 6// FSM is, by construction, the PROVEN one (DRY: the table is never duplicated here). 7// 8// CLI: nx_game_gen2 <name> <THEME> <type> [difficulty] 9// type = dungenc | twrwave | abround | stealth | datesim | fightfsm 10// writes knowledge/specs/<name>.spec (name/title/economy + fsm+rows from build_<type>.spec) 11// SELF-GATE (run with no args, the way nx_sov_build_run invokes it): generate all 6 types to 12// scratch, re-parse each emitted game-spec, assert its FSM == the source build_<type>.spec FSM, 13// assert the 6 tables are DISTINCT (polymorphic, not all-dungenc), and assert an UNKNOWN type is 14// REFUSED. GAMEGEN2-GATE verdict=GREEN iff all sourced-ok + distinct>=2 + neg refused. 15// 16// HONEST SCOPE: the economy shell (walls/foes/hp, difficulty-scaled -- the deterministic tier 17// table) is the dungeon default; the FSM (the game SYSTEM) is the polymorphic shape. Per-type 18// economy templates are a refinement. Sovereign syscalls only. license_tier: ORIGINAL 19import "nx_syscalls.nx" 20import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 21const K_MAGIC_65536: i64 = 65536 22const K_MAGIC_2048: i64 = 2048 23 24func gg_w(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 } 25func gg_c(fd: i64, c: i64) -> i64 { let b: *u8 = sys_mmap(8); b[0] = c as u8; sys_write(fd, b, 1); return 0 } 26// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 27// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 28// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 29// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 30func gg_wn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 } 31func gg_streq(a: *u8, b: *u8) -> i64 { 32 var i: i64 = 0 33 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 34 if b[i] != (0 as u8) { return 0 } 35 return 1 36} 37func gg_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } return off + i } 38 39func gg_read(path: *u8, buf: *u8, cap: i64) -> i64 { 40 let fd: i64 = sys_openat_rd(path) 41 if fd < 0 { return 0 - 1 } 42 var n: i64 = 0 43 var r: i64 = sys_read(fd, buf, cap - 1) 44 while r > 0 { n = n + r; r = sys_read(fd, buf + n, cap - 1 - n) } 45 sys_close(fd) 46 return n 47} 48 49// parse every signed int on line [ls,le) into tmp; returns count (the word "param"/"row"/"fsm" has no digits, skipped) 50func gg_line_ints(buf: *u8, ls: i64, le: i64, tmp: *i64) -> i64 { 51 var cnt: i64 = 0 52 var i: i64 = ls 53 while i < le { 54 let c: i64 = buf[i] as i64 55 var start: i64 = 0 56 if c == 45 { start = 1 } 57 if c >= 48 { if c <= 57 { start = 1 } } 58 if start == 1 { 59 var neg: i64 = 0 60 if c == 45 { neg = 1; i = i + 1 } 61 var v: i64 = 0 62 var go: i64 = 1 63 while go == 1 { 64 if i >= le { go = 0 } else { 65 let d: i64 = buf[i] as i64 66 if d >= 48 { if d <= 57 { v = v * 10 + (d - 48); i = i + 1 } else { go = 0 } } else { go = 0 } 67 } 68 } 69 if neg == 1 { v = 0 - v } 70 tmp[cnt] = v; cnt = cnt + 1 71 } else { i = i + 1 } 72 } 73 return cnt 74} 75 76// extract the FSM table from a spec buffer. key=1 -> build spec ("param" lines: 1st=hdr ns ne s0, rest=rows); 77// key=0 -> game spec ("fsm ns ne" line + "row" lines). out[0]=ns out[1]=ne out[3+r*ne+c]=cell. returns 1 ok. 78func gg_extract_fsm(buf: *u8, n: i64, key: i64, out: *i64) -> i64 { 79 let tmp: *i64 = sys_mmap(64 * 8) as *i64 80 var ns: i64 = 0; var ne: i64 = 0 81 var pcount: i64 = 0 // build: param-lines seen ; game: rows seen 82 var have_hdr: i64 = 0 83 var i: i64 = 0 84 while i < n { 85 var le: i64 = i 86 var s: i64 = 1 87 while s == 1 { if le >= n { s = 0 } else { if buf[le] == (10 as u8) { s = 0 } else { le = le + 1 } } } 88 if le > i { if buf[i] != (35 as u8) { 89 // first non-space token 90 var t0: i64 = i 91 while t0 < le { if buf[t0] == (32 as u8) { t0 = le } else { t0 = t0 + 1 } } // t0 = end of first token (or le) 92 // identify keyword by first char(s) 93 if key == 1 { 94 // build spec: "param ..." lines only 95 if buf[i] == (112 as u8) { // 'p' -> param 96 let m: i64 = gg_line_ints(buf, i, le, tmp) 97 if pcount == 0 { ns = tmp[0]; ne = tmp[1]; out[0] = ns; out[1] = ne; out[2] = tmp[2]; have_hdr = 1 } 98 else { 99 let rr: i64 = pcount - 1 100 var c: i64 = 0 101 while c < ne { out[3 + rr * ne + c] = tmp[c]; c = c + 1 } 102 } 103 pcount = pcount + 1 104 } 105 } else { 106 // game spec: "fsm ns ne" then "row ..." 107 if buf[i] == (102 as u8) { // 'f' -> fsm 108 let m: i64 = gg_line_ints(buf, i, le, tmp) 109 ns = tmp[0]; ne = tmp[1]; out[0] = ns; out[1] = ne; out[2] = 0; have_hdr = 1 110 } else { if buf[i] == (114 as u8) { // 'r' -> row 111 let m: i64 = gg_line_ints(buf, i, le, tmp) 112 var c: i64 = 0 113 while c < ne { out[3 + pcount * ne + c] = tmp[c]; c = c + 1 } 114 pcount = pcount + 1 115 } } 116 } 117 } } 118 i = le + 1 119 } 120 if have_hdr == 0 { return 0 } 121 var rows_needed: i64 = ns 122 if key == 1 { if pcount == ns + 1 { return 1 } return 0 } 123 if pcount == rows_needed { return 1 } 124 return 0 125} 126 127// read knowledge/specs/build_<type>.spec -> out fsm table. returns 1 ok / 0 missing-or-malformed. 128func gg_build_fsm(typ: *u8, out: *i64) -> i64 { 129 let path: *u8 = sys_mmap(256) 130 var o: i64 = 0 131 o = gg_cat(path, o, "knowledge/specs/build_" as *u8); o = gg_cat(path, o, typ); o = gg_cat(path, o, ".spec" as *u8); path[o] = 0 as u8 132 let buf: *u8 = sys_mmap(K_MAGIC_65536) 133 let n: i64 = gg_read(path, buf, K_MAGIC_65536) 134 if n <= 0 { return 0 } 135 return gg_extract_fsm(buf, n, 1, out) 136} 137 138// difficulty tier 0..3 139func gg_tier(d: *u8) -> i64 { 140 if gg_streq(d, "easy" as *u8) == 1 { return 0 } 141 if gg_streq(d, "normal" as *u8) == 1 { return 1 } 142 if gg_streq(d, "hard" as *u8) == 1 { return 2 } 143 if gg_streq(d, "nightmare" as *u8) == 1 { return 3 } 144 return 1 145} 146 147func gg_kv(fd: i64, key: *u8, v: i64) -> i64 { gg_w(fd, key); gg_c(fd, 32); gg_wn(fd, v); gg_c(fd, 10); return 0 } 148 149// write a complete game-spec: name/title/economy(tier-scaled)/needs + fsm+rows from ftab. 150func gg_emit_spec(fd: i64, name: *u8, theme: *u8, typ: *u8, tier: i64, ftab: *i64) -> i64 { 151 let walls: i64 = 16 + tier * 6 152 let eb: i64 = 2 + tier 153 let hb: i64 = 14 + tier * 6 154 let hpl: i64 = 3 + tier 155 var loot: i64 = 4 - tier 156 if loot < 1 { loot = 1 } 157 var db: i64 = 10 - tier 158 if db < 6 { db = 6 } 159 gg_w(fd, "# generated by nx_game_gen2 (polymorphic intent->spec); FSM sourced from build_" as *u8); gg_w(fd, typ); gg_w(fd, ".spec\n" as *u8) 160 gg_w(fd, "name " as *u8); gg_w(fd, name); gg_c(fd, 10) 161 gg_w(fd, "title THE " as *u8); gg_w(fd, theme); gg_c(fd, 10) 162 gg_kv(fd, "walls" as *u8, walls) 163 gg_kv(fd, "enemy_base" as *u8, eb) 164 gg_kv(fd, "enemy_per_level" as *u8, 1) 165 gg_kv(fd, "ehp_base" as *u8, hb) 166 gg_kv(fd, "ehp_per_level" as *u8, hpl) 167 gg_kv(fd, "loot" as *u8, loot) 168 gg_kv(fd, "dmg_base" as *u8, db) 169 gg_kv(fd, "dmg_rand" as *u8, 8) 170 gg_w(fd, "needs save\n" as *u8) 171 let ns: i64 = ftab[0]; let ne: i64 = ftab[1] 172 gg_w(fd, "fsm " as *u8); gg_wn(fd, ns); gg_c(fd, 32); gg_wn(fd, ne); gg_c(fd, 10) 173 var r: i64 = 0 174 while r < ns { 175 gg_w(fd, "row" as *u8) 176 var c: i64 = 0 177 while c < ne { gg_c(fd, 32); gg_wn(fd, ftab[3 + r * ne + c]); c = c + 1 } 178 gg_c(fd, 10) 179 r = r + 1 180 } 181 return 0 182} 183 184// compare two extracted fsm tables (ns,ne,cells). 1 equal / 0 differ. 185func gg_fsm_eq(a: *i64, b: *i64) -> i64 { 186 if a[0] != b[0] { return 0 } 187 if a[1] != b[1] { return 0 } 188 let cells: i64 = a[0] * a[1] 189 var i: i64 = 0 190 while i < cells { if a[3 + i] != b[3 + i] { return 0 } i = i + 1 } 191 return 1 192} 193// a cheap order-sensitive signature of an fsm table (for distinctness) 194func gg_fsm_sig(a: *i64) -> i64 { 195 var sig: i64 = a[0] * 131 + a[1] 196 let cells: i64 = a[0] * a[1] 197 var i: i64 = 0 198 while i < cells { sig = sig * 31 + a[3 + i] + 7; i = i + 1 } 199 return sig 200} 201 202func gg_gate_report(fd: i64, navail: i64, sourced: i64, distinct: i64, neg: i64, ok: i64) -> i64 { 203 gg_w(fd, "GAMEGEN2-GATE authored=organ types_available=" as *u8); gg_wn(fd, navail) 204 gg_w(fd, " fsm_sourced_ok=" as *u8); gg_wn(fd, sourced) 205 gg_w(fd, " distinct_tables=" as *u8); gg_wn(fd, distinct) 206 gg_w(fd, " neg_unknown_refused=" as *u8); gg_wn(fd, neg) 207 if ok == 1 { gg_w(fd, " verdict=GREEN\n" as *u8) } else { gg_w(fd, " verdict=RED\n" as *u8) } 208 return 0 209} 210 211func main(argc: i64, argv: *i64) -> i64 { 212 if argc >= 4 { 213 // CLI generate 214 let name: *u8 = argv[1] as *u8 215 let theme: *u8 = argv[2] as *u8 216 let typ: *u8 = argv[3] as *u8 217 var diff: *u8 = "normal" as *u8 218 if argc >= 5 { diff = argv[4] as *u8 } 219 let ftab: *i64 = sys_mmap(K_MAGIC_2048) as *i64 220 if gg_build_fsm(typ, ftab) == 0 { gg_w(1, "GAMEGEN2 REFUSED: unknown type (no build_" as *u8); gg_w(1, typ); gg_w(1, ".spec)\n" as *u8); sys_exit(1); return 1 } 221 let path: *u8 = sys_mmap(256) 222 var o: i64 = gg_cat(path, 0, "knowledge/specs/" as *u8); o = gg_cat(path, o, name); o = gg_cat(path, o, ".spec" as *u8); path[o] = 0 as u8 223 let fd: i64 = sys_openat_wr(path, 0x1a4) 224 if fd < 0 { gg_w(1, "GAMEGEN2 REFUSED: cannot open spec path\n" as *u8); sys_exit(1); return 1 } 225 gg_emit_spec(fd, name, theme, typ, gg_tier(diff), ftab) 226 sys_close(fd) 227 gg_w(1, "GAMEGEN2 GREEN name=" as *u8); gg_w(1, name); gg_w(1, " type=" as *u8); gg_w(1, typ) 228 gg_w(1, " -> " as *u8); gg_w(1, path); gg_c(1, 10) 229 sys_exit(0); return 0 230 } 231 232 // SELF-GATE: generate all 6 proven types, prove each emitted FSM == its build-spec source. 233 let N: i64 = 6 234 let types: *i64 = sys_mmap(N * 8) as *i64 235 types[0] = "dungenc" as *u8 as i64 236 types[1] = "twrwave" as *u8 as i64 237 types[2] = "abround" as *u8 as i64 238 types[3] = "stealth" as *u8 as i64 239 types[4] = "datesim" as *u8 as i64 240 types[5] = "fightfsm" as *u8 as i64 241 242 let src: *i64 = sys_mmap(K_MAGIC_2048) as *i64 243 let gen: *i64 = sys_mmap(K_MAGIC_2048) as *i64 244 let sigs: *i64 = sys_mmap(N * 8) as *i64 245 var navail: i64 = 0 246 var sourced: i64 = 0 247 var t: i64 = 0 248 while t < N { 249 let typ: *u8 = types[t] as *u8 250 if gg_build_fsm(typ, src) == 1 { 251 navail = navail + 1 252 // emit to scratch game-spec 253 let sp: *u8 = sys_mmap(256) 254 var o: i64 = gg_cat(sp, 0, "/tmp/_ggtest_" as *u8); o = gg_cat(sp, o, typ); o = gg_cat(sp, o, ".spec" as *u8); sp[o] = 0 as u8 255 let fd: i64 = sys_openat_wr(sp, 0x1a4) 256 if fd >= 0 { 257 gg_emit_spec(fd, "_ggtest" as *u8, "PROOF" as *u8, typ, 1, src) 258 sys_close(fd) 259 // re-parse the emitted game-spec FSM 260 let buf: *u8 = sys_mmap(K_MAGIC_65536) 261 let nn: i64 = gg_read(sp, buf, K_MAGIC_65536) 262 if nn > 0 { if gg_extract_fsm(buf, nn, 0, gen) == 1 { 263 if gg_fsm_eq(src, gen) == 1 { sourced = sourced + 1; sigs[t] = gg_fsm_sig(gen) } 264 } } 265 } 266 } else { sigs[t] = 0 } 267 t = t + 1 268 } 269 // distinctness over the sourced tables 270 var distinct: i64 = 0 271 var a: i64 = 0 272 while a < N { 273 if sigs[a] != 0 { 274 var seen: i64 = 0 275 var b: i64 = 0 276 while b < a { if sigs[b] == sigs[a] { seen = 1 } b = b + 1 } 277 if seen == 0 { distinct = distinct + 1 } 278 } 279 a = a + 1 280 } 281 // negative control: an unknown type must NOT yield a build fsm 282 let bog: *i64 = sys_mmap(K_MAGIC_2048) as *i64 283 var neg: i64 = 1 284 if gg_build_fsm("nosuchtype" as *u8, bog) == 1 { neg = 0 } 285 286 var ok: i64 = 1 287 if sourced != navail { ok = 0 } 288 if navail < 2 { ok = 0 } 289 if distinct < 2 { ok = 0 } 290 if neg != 1 { ok = 0 } 291 292 gg_gate_report(1, navail, sourced, distinct, neg, ok) 293 let lf: i64 = sys_openat_append("knowledge/status/game_gen2.log" as *u8, 420) 294 if lf >= 0 { gg_gate_report(lf, navail, sourced, distinct, neg, ok); sys_close(lf) } 295 if ok == 1 { sys_exit(0); return 0 } 296 sys_exit(1); return 1 297}