code wiki / _hdl_build / nx_game_edit.nx

nx_game_edit.nx source

↩ module page · 510 lines · 21655 B

1// nx_game_edit.nx -- the FINE-CONTROL EDITOR KERNEL (G-ECO-004): the Godot-class editor's 2// core primitive, the second authoring front-end of the Nishi game ecosystem. A structured, 3// VALIDATED, preservation-correct EDIT on a data game-spec. Two mutation kinds (both = the 4// same responsibility: edit ONE spec field, copy everything else verbatim, keep it emitter-valid): 5// 6// (1) DIFFICULTY KNOB: nx_game_edit <in.spec> <out.spec> <key> <value> 7// key = one of the 8 economy knobs (walls enemy_base enemy_per_level ehp_base ehp_per_level 8// loot dmg_base dmg_rand); value must be in the knob's DATA-DRIVEN range. 9// (2) FSM TRANSITION CELL (the visual FSM-table editor's core): 10// nx_game_edit <in.spec> <out.spec> fsm <state> <event> <value> 11// sets transition table cell (state,event) = value (-1 = no transition, else a state index). 12// 13// Both copy every line of <in.spec> VERBATIM except the one edited field -- name/title/other 14// params/other FSM rows/needs/comments all preserved. 15// 16// SELF-GATE (no args, the way nx_sov_build_run invokes it): on game_crypt.spec assert BOTH 17// (a) KNOB walls=30 applies, enemy_base/ehp_base/dmg_base + 6 FSM rows + title unchanged, and 18// unknown-key/over-range/under-range REFUSE; and (b) FSM cell (1,4)->-1 applies, cells (1,1)=2 19// and (1,3)=3 + row count + walls unchanged, and bad-state/bad-event/bad-value REFUSE. 20// GAMEEDIT-GATE verdict=GREEN iff all hold. Live-preview-by-emit is the next rung (here 21// preview = the edited spec RE-PARSES valid, the contract the emitter requires). 22// 23// HONEST: knob/cell RANGES are DATA (no buried magic numbers). TUTOR-authored tooling (the editor 24// front-end); the GAMES remain emitter output. Feature-add, not a measured-exceed claim. 25// Sovereign syscalls only. license_tier: ORIGINAL 26import "nx_syscalls.nx" 27const K_MAGIC_65536: i64 = 65536 28const K_MAGIC_99999: i64 = 99999 29 30func ge_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 } 31func ge_c(fd: i64, c: i64) -> i64 { let b: *u8 = sys_mmap(8); b[0] = c as u8; sys_write(fd, b, 1); return 0 } 32func ge_wn(fd: i64, v: i64) -> i64 { 33 var m: i64 = v 34 if m < 0 { ge_c(fd, 45); m = 0 - m } 35 let bb: *u8 = sys_mmap(28); let t: *u8 = sys_mmap(28); var k: i64 = 0 36 if m == 0 { t[0] = 48 as u8; k = 1 } 37 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 38 var i: i64 = 0 39 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 40 sys_write(fd, bb, k); return 0 41} 42 43func ge_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 44func ge_streq(a: *u8, b: *u8) -> i64 { 45 var i: i64 = 0 46 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 47 if b[i] != (0 as u8) { return 0 } 48 return 1 49} 50func ge_atoi(s: *u8) -> i64 { 51 var p: i64 = 0; var neg: i64 = 0 52 if s[0] == (45 as u8) { neg = 1; p = 1 } 53 var v: i64 = 0 54 while s[p] != (0 as u8) { let c: i64 = s[p] as i64; if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } p = p + 1 } 55 if neg == 1 { return 0 - v } 56 return v 57} 58 59func ge_read(path: *u8, buf: *u8, cap: i64) -> i64 { 60 let fd: i64 = sys_openat_rd(path) 61 if fd < 0 { return 0 - 1 } 62 var n: i64 = 0 63 var r: i64 = sys_read(fd, buf, cap - 1) 64 while r > 0 { n = n + r; r = sys_read(fd, buf + n, cap - 1 - n) } 65 sys_close(fd) 66 return n 67} 68 69// write a base-10 int into dst at off; return new offset 70func ge_catn(dst: *u8, off: i64, v: i64) -> i64 { 71 var m: i64 = v; var w: i64 = off 72 if m < 0 { dst[w] = 45 as u8; w = w + 1; m = 0 - m } 73 let t: *u8 = sys_mmap(28); var k: i64 = 0 74 if m == 0 { t[0] = 48 as u8; k = 1 } 75 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 76 var i: i64 = 0 77 while i < k { dst[w] = t[k - 1 - i]; w = w + 1; i = i + 1 } 78 return w 79} 80 81// --- spec re-parse helpers (same line-start+space convention as nx_game_emit) --- 82func ge_match(buf: *u8, pos: i64, key: *u8) -> i64 { 83 var i: i64 = 0 84 while key[i] != (0 as u8) { if buf[pos + i] != key[i] { return 0 } i = i + 1 } 85 return 1 86} 87func ge_linestart(buf: *u8, pos: i64) -> i64 { 88 if pos == 0 { return 1 } 89 if buf[pos - 1] == (10 as u8) { return 1 } 90 return 0 91} 92func ge_find(buf: *u8, len: i64, key: *u8) -> i64 { 93 let kl: i64 = ge_len(key) 94 var i: i64 = 0 95 while i < len { 96 if ge_linestart(buf, i) == 1 { if ge_match(buf, i, key) == 1 { if buf[i + kl] == (32 as u8) { return i + kl + 1 } } } 97 i = i + 1 98 } 99 return 0 - 1 100} 101func ge_pint(buf: *u8, pos: i64) -> i64 { 102 var p: i64 = pos 103 while buf[p] == (32 as u8) { p = p + 1 } 104 var neg: i64 = 0 105 if buf[p] == (45 as u8) { neg = 1; p = p + 1 } 106 var v: i64 = 0; var go: i64 = 1 107 while go == 1 { let c: i64 = buf[p] as i64; if c < 48 { go = 0 } if c > 57 { go = 0 } if go == 1 { v = v * 10 + (c - 48); p = p + 1 } } 108 if neg == 1 { return 0 - v } 109 return v 110} 111// advance p past one signed integer token (p must sit on its first char after space-skip) 112func ge_skipint(buf: *u8, pos: i64) -> i64 { 113 var p: i64 = pos 114 if buf[p] == (45 as u8) { p = p + 1 } 115 var d: i64 = 1 116 while d == 1 { let c: i64 = buf[p] as i64; if c < 48 { d = 0 } if c > 57 { d = 0 } if d == 1 { p = p + 1 } } 117 return p 118} 119func ge_int(buf: *u8, len: i64, key: *u8, dflt: i64) -> i64 { 120 let p: i64 = ge_find(buf, len, key) 121 if p < 0 { return dflt } 122 return ge_pint(buf, p) 123} 124// the 2nd int on the "fsm ns ne" line (= ne); -1 if absent 125func ge_fsm_ne(buf: *u8, len: i64) -> i64 { 126 let p: i64 = ge_find(buf, len, "fsm" as *u8) 127 if p < 0 { return 0 - 1 } 128 var q: i64 = p 129 while buf[q] == (32 as u8) { q = q + 1 } 130 q = ge_skipint(buf, q) 131 return ge_pint(buf, q) 132} 133func ge_count_rows(buf: *u8, len: i64) -> i64 { 134 var n: i64 = 0; var i: i64 = 0 135 while i < len { 136 if ge_linestart(buf, i) == 1 { if ge_match(buf, i, "row" as *u8) == 1 { if buf[i + 3] == (32 as u8) { n = n + 1 } } } 137 i = i + 1 138 } 139 return n 140} 141func ge_title_crypt(buf: *u8, len: i64) -> i64 { 142 let p: i64 = ge_find(buf, len, "title" as *u8) 143 if p < 0 { return 0 } 144 return ge_match(buf, p, "THE CRYPT" as *u8) 145} 146// value of FSM cell (state,event) by walking "row" lines; -999 if not found 147func ge_fsm_cell(buf: *u8, len: i64, state: i64, event: i64) -> i64 { 148 var rowidx: i64 = 0; var i: i64 = 0 149 while i < len { 150 if ge_linestart(buf, i) == 1 { if ge_match(buf, i, "row" as *u8) == 1 { if buf[i + 3] == (32 as u8) { 151 if rowidx == state { 152 var p: i64 = i + 3 153 var col: i64 = 0 154 while col <= event { 155 while buf[p] == (32 as u8) { p = p + 1 } 156 if col == event { return ge_pint(buf, p) } 157 p = ge_skipint(buf, p) 158 col = col + 1 159 } 160 } 161 rowidx = rowidx + 1 162 } } } 163 i = i + 1 164 } 165 return 0 - 999 166} 167 168// the knob DESIGN TABLE (data-driven ranges): 1 iff <key> is a known economy knob AND value in [min,max]. 169func ge_knob_ok(key: *u8, value: i64) -> i64 { 170 let names: *i64 = sys_mmap(8 * 8) as *i64 171 let mins: *i64 = sys_mmap(8 * 8) as *i64 172 let maxs: *i64 = sys_mmap(8 * 8) as *i64 173 names[0] = "walls" as *u8 as i64; mins[0] = 0; maxs[0] = 120 174 names[1] = "enemy_base" as *u8 as i64; mins[1] = 0; maxs[1] = 50 175 names[2] = "enemy_per_level" as *u8 as i64; mins[2] = 0; maxs[2] = 20 176 names[3] = "ehp_base" as *u8 as i64; mins[3] = 1; maxs[3] = 500 177 names[4] = "ehp_per_level" as *u8 as i64; mins[4] = 0; maxs[4] = 100 178 names[5] = "loot" as *u8 as i64; mins[5] = 0; maxs[5] = 50 179 names[6] = "dmg_base" as *u8 as i64; mins[6] = 1; maxs[6] = 200 180 names[7] = "dmg_rand" as *u8 as i64; mins[7] = 0; maxs[7] = 200 181 var i: i64 = 0 182 while i < 8 { 183 if ge_streq(key, names[i] as *u8) == 1 { 184 if value >= mins[i] { if value <= maxs[i] { return 1 } } 185 return 0 186 } 187 i = i + 1 188 } 189 return 0 190} 191 192// apply ONE knob edit into outbuf. returns new length, or -1 if REFUSED (bad knob/value/absent key). 193func ge_apply(inbuf: *u8, inlen: i64, key: *u8, value: i64, outbuf: *u8) -> i64 { 194 if ge_knob_ok(key, value) == 0 { return 0 - 1 } 195 let kl: i64 = ge_len(key) 196 var w: i64 = 0 197 var i: i64 = 0 198 var replaced: i64 = 0 199 while i < inlen { 200 var le: i64 = i 201 var go: i64 = 1 202 while go == 1 { 203 if le >= inlen { go = 0 } else { if inbuf[le] == (10 as u8) { go = 0 } else { le = le + 1 } } 204 } 205 var ismatch: i64 = 0 206 if replaced == 0 { if i + kl < le { 207 var j: i64 = 0; var ok: i64 = 1 208 while j < kl { if inbuf[i + j] != key[j] { ok = 0 } j = j + 1 } 209 if ok == 1 { if inbuf[i + kl] == (32 as u8) { ismatch = 1 } } 210 } } 211 if ismatch == 1 { 212 var c: i64 = 0 213 while c < kl { outbuf[w] = key[c]; w = w + 1; c = c + 1 } 214 outbuf[w] = 32 as u8; w = w + 1 215 w = ge_catn(outbuf, w, value) 216 replaced = 1 217 } else { 218 var c: i64 = i 219 while c < le { outbuf[w] = inbuf[c]; w = w + 1; c = c + 1 } 220 } 221 if le < inlen { outbuf[w] = 10 as u8; w = w + 1 } 222 i = le + 1 223 } 224 if replaced == 0 { return 0 - 1 } 225 return w 226} 227 228// parse up to ne signed ints on a row line [after, le) into tmp; returns count 229func ge_row_ints(buf: *u8, after: i64, le: i64, tmp: *i64, ne: i64) -> i64 { 230 var p: i64 = after 231 var c: i64 = 0 232 while c < ne { 233 var sk: i64 = 1 234 while sk == 1 { if p >= le { sk = 0 } else { if buf[p] == (32 as u8) { p = p + 1 } else { sk = 0 } } } 235 if p >= le { return c } 236 tmp[c] = ge_pint(buf, p) 237 p = ge_skipint(buf, p) 238 c = c + 1 239 } 240 return c 241} 242 243// apply ONE FSM-cell edit into outbuf. returns new length, or -1 if REFUSED. 244func ge_apply_fsm(inbuf: *u8, inlen: i64, state: i64, event: i64, value: i64, outbuf: *u8) -> i64 { 245 let ns: i64 = ge_int(inbuf, inlen, "fsm" as *u8, 0 - 1) 246 let ne: i64 = ge_fsm_ne(inbuf, inlen) 247 if ns < 0 { return 0 - 1 } 248 if ne < 0 { return 0 - 1 } 249 if state < 0 { return 0 - 1 } 250 if state >= ns { return 0 - 1 } 251 if event < 0 { return 0 - 1 } 252 if event >= ne { return 0 - 1 } 253 if value < 0 - 1 { return 0 - 1 } 254 if value >= ns { return 0 - 1 } 255 let tmp: *i64 = sys_mmap(256 * 8) as *i64 256 var w: i64 = 0 257 var i: i64 = 0 258 var rowidx: i64 = 0 259 var done: i64 = 0 260 while i < inlen { 261 var le: i64 = i 262 var go: i64 = 1 263 while go == 1 { if le >= inlen { go = 0 } else { if inbuf[le] == (10 as u8) { go = 0 } else { le = le + 1 } } } 264 var edit_this: i64 = 0 265 if ge_match(inbuf, i, "row" as *u8) == 1 { if i + 3 < le { if inbuf[i + 3] == (32 as u8) { 266 if rowidx == state { if done == 0 { edit_this = 1 } } 267 rowidx = rowidx + 1 268 } } } 269 if edit_this == 1 { 270 ge_row_ints(inbuf, i + 3, le, tmp, ne) 271 tmp[event] = value 272 outbuf[w] = 114 as u8; w = w + 1 273 outbuf[w] = 111 as u8; w = w + 1 274 outbuf[w] = 119 as u8; w = w + 1 275 var c: i64 = 0 276 while c < ne { outbuf[w] = 32 as u8; w = w + 1; w = ge_catn(outbuf, w, tmp[c]); c = c + 1 } 277 done = 1 278 } else { 279 var c4: i64 = i 280 while c4 < le { outbuf[w] = inbuf[c4]; w = w + 1; c4 = c4 + 1 } 281 } 282 if le < inlen { outbuf[w] = 10 as u8; w = w + 1 } 283 i = le + 1 284 } 285 if done == 0 { return 0 - 1 } 286 return w 287} 288 289// copy the idx-th space-delimited token of line [ls,le) into out (NUL-term); 1 if found, 0 if absent. 290func ge_tok(buf: *u8, ls: i64, le: i64, idx: i64, out: *u8) -> i64 { 291 var p: i64 = ls 292 var cur: i64 = 0 293 while p < le { 294 var sk: i64 = 1 295 while sk == 1 { if p >= le { sk = 0 } else { if buf[p] == (32 as u8) { p = p + 1 } else { sk = 0 } } } 296 if p >= le { return 0 } 297 var te: i64 = p 298 var sk2: i64 = 1 299 while sk2 == 1 { if te >= le { sk2 = 0 } else { if buf[te] == (32 as u8) { sk2 = 0 } else { te = te + 1 } } } 300 if cur == idx { 301 var w: i64 = 0; var q: i64 = p 302 while q < te { out[w] = buf[q]; w = w + 1; q = q + 1 } 303 out[w] = 0 as u8 304 return 1 305 } 306 cur = cur + 1 307 p = te 308 } 309 return 0 310} 311 312// apply a multi-line edit SCRIPT atomically (all-or-nothing). Each non-blank/non-# line is either 313// "knob <key> <value>" or "fsm <state> <event> <value>" 314// returns final spec length in outbuf, or -1 if ANY edit is invalid (then NOTHING is written = transactional). 315func ge_apply_batch(inbuf: *u8, inlen: i64, script: *u8, slen: i64, outbuf: *u8) -> i64 { 316 let A: *u8 = sys_mmap(K_MAGIC_65536) 317 let B: *u8 = sys_mmap(K_MAGIC_65536) 318 var ci: i64 = 0 319 while ci < inlen { A[ci] = inbuf[ci]; ci = ci + 1 } 320 var curIsA: i64 = 1 321 var curlen: i64 = inlen 322 var okall: i64 = 1 323 var applied: i64 = 0 324 let t0: *u8 = sys_mmap(64); let t1: *u8 = sys_mmap(64); let t2: *u8 = sys_mmap(64); let t3: *u8 = sys_mmap(64) 325 var i: i64 = 0 326 while i < slen { 327 var le: i64 = i 328 var go: i64 = 1 329 while go == 1 { if le >= slen { go = 0 } else { if script[le] == (10 as u8) { go = 0 } else { le = le + 1 } } } 330 if okall == 1 { if le > i { if script[i] != (35 as u8) { 331 if ge_tok(script, i, le, 0, t0) == 1 { if t0[0] != (0 as u8) { 332 var src: *u8 = A; var dst: *u8 = B 333 if curIsA == 0 { src = B; dst = A } 334 var res: i64 = 0 - 1 335 if ge_streq(t0, "knob" as *u8) == 1 { 336 if ge_tok(script, i, le, 1, t1) == 1 { if ge_tok(script, i, le, 2, t2) == 1 { 337 res = ge_apply(src, curlen, t1, ge_atoi(t2), dst) 338 } } 339 } 340 if ge_streq(t0, "fsm" as *u8) == 1 { 341 if ge_tok(script, i, le, 1, t1) == 1 { if ge_tok(script, i, le, 2, t2) == 1 { if ge_tok(script, i, le, 3, t3) == 1 { 342 res = ge_apply_fsm(src, curlen, ge_atoi(t1), ge_atoi(t2), ge_atoi(t3), dst) 343 } } } 344 } 345 if res < 0 { okall = 0 } else { curlen = res; curIsA = 1 - curIsA; applied = applied + 1 } 346 } } 347 } } } 348 i = le + 1 349 } 350 if okall == 0 { return 0 - 1 } 351 if applied == 0 { return 0 - 1 } 352 var src2: *u8 = A 353 if curIsA == 0 { src2 = B } 354 var c: i64 = 0 355 while c < curlen { outbuf[c] = src2[c]; c = c + 1 } 356 return curlen 357} 358 359func ge_report(fd: i64, r: *i64, ok: i64) -> i64 { 360 ge_w(fd, "GAMEEDIT-GATE authored=organ knob[walls30->" as *u8); ge_wn(fd, r[0]) 361 ge_w(fd, " eb2->" as *u8); ge_wn(fd, r[1]) 362 ge_w(fd, " ehp18->" as *u8); ge_wn(fd, r[2]) 363 ge_w(fd, " dmg8->" as *u8); ge_wn(fd, r[3]) 364 ge_w(fd, " rows6->" as *u8); ge_wn(fd, r[4]) 365 ge_w(fd, " title=" as *u8); ge_wn(fd, r[5]) 366 ge_w(fd, " neg=" as *u8); ge_wn(fd, r[6]); ge_wn(fd, r[7]); ge_wn(fd, r[8]) 367 ge_w(fd, "] fsm[cell_1_4->" as *u8); ge_wn(fd, r[9]) 368 ge_w(fd, " keep_1_1=2->" as *u8); ge_wn(fd, r[10]) 369 ge_w(fd, " keep_1_3=3->" as *u8); ge_wn(fd, r[11]) 370 ge_w(fd, " rows6->" as *u8); ge_wn(fd, r[12]) 371 ge_w(fd, " walls20->" as *u8); ge_wn(fd, r[13]) 372 ge_w(fd, " neg=" as *u8); ge_wn(fd, r[14]); ge_wn(fd, r[15]); ge_wn(fd, r[16]) 373 ge_w(fd, "] batch[walls30->" as *u8); ge_wn(fd, r[17]) 374 ge_w(fd, " dmg25->" as *u8); ge_wn(fd, r[18]) 375 ge_w(fd, " cell_1_4->" as *u8); ge_wn(fd, r[19]) 376 ge_w(fd, " title=" as *u8); ge_wn(fd, r[20]) 377 ge_w(fd, " eb2->" as *u8); ge_wn(fd, r[21]) 378 ge_w(fd, " rows6->" as *u8); ge_wn(fd, r[22]) 379 ge_w(fd, " atomic_badbatch_refused=" as *u8); ge_wn(fd, r[23]) 380 if ok == 1 { ge_w(fd, "] verdict=GREEN\n" as *u8) } else { ge_w(fd, "] verdict=RED\n" as *u8) } 381 return 0 382} 383 384func main(argc: i64, argv: *i64) -> i64 { 385 if argc >= 5 { 386 let inp: *u8 = argv[1] as *u8 387 let outp: *u8 = argv[2] as *u8 388 let inbuf: *u8 = sys_mmap(K_MAGIC_65536) 389 let n: i64 = ge_read(inp, inbuf, K_MAGIC_65536) 390 if n <= 0 { ge_w(1, "GAMEEDIT REFUSED: cannot read in-spec\n" as *u8); sys_exit(1); return 1 } 391 let outbuf: *u8 = sys_mmap(K_MAGIC_65536) 392 var m: i64 = 0 - 1 393 var handled: i64 = 0 394 if ge_streq(argv[3] as *u8, "fsm" as *u8) == 1 { 395 handled = 1 396 if argc < 7 { ge_w(1, "GAMEEDIT REFUSED: fsm edit needs <state> <event> <value>\n" as *u8); sys_exit(1); return 1 } 397 m = ge_apply_fsm(inbuf, n, ge_atoi(argv[4] as *u8), ge_atoi(argv[5] as *u8), ge_atoi(argv[6] as *u8), outbuf) 398 } 399 if ge_streq(argv[3] as *u8, "batch" as *u8) == 1 { 400 handled = 1 401 let scriptbuf: *u8 = sys_mmap(K_MAGIC_65536) 402 let sn: i64 = ge_read(argv[4] as *u8, scriptbuf, K_MAGIC_65536) 403 if sn <= 0 { ge_w(1, "GAMEEDIT REFUSED: cannot read batch script\n" as *u8); sys_exit(1); return 1 } 404 m = ge_apply_batch(inbuf, n, scriptbuf, sn, outbuf) 405 } 406 if handled == 0 { 407 m = ge_apply(inbuf, n, argv[3] as *u8, ge_atoi(argv[4] as *u8), outbuf) 408 } 409 if m < 0 { ge_w(1, "GAMEEDIT REFUSED: invalid knob/cell, value out of range, or field absent\n" as *u8); sys_exit(1); return 1 } 410 let fd: i64 = sys_openat_wr(outp, 0x1a4) 411 if fd < 0 { ge_w(1, "GAMEEDIT REFUSED: cannot open out-spec\n" as *u8); sys_exit(1); return 1 } 412 sys_write(fd, outbuf, m); sys_close(fd) 413 ge_w(1, "GAMEEDIT GREEN " as *u8); ge_w(1, argv[3] as *u8); ge_w(1, " -> " as *u8); ge_w(1, outp); ge_c(1, 10) 414 sys_exit(0); return 0 415 } 416 417 // SELF-GATE 418 let r: *i64 = sys_mmap(64 * 8) as *i64 419 let inbuf: *u8 = sys_mmap(K_MAGIC_65536) 420 let n: i64 = ge_read("knowledge/specs/game_crypt.spec" as *u8, inbuf, K_MAGIC_65536) 421 if n <= 0 { ge_w(1, "GAMEEDIT-GATE verdict=RED (cannot read game_crypt.spec)\n" as *u8); sys_exit(1); return 1 } 422 let scratch: *u8 = sys_mmap(K_MAGIC_65536) 423 424 // (a) KNOB edit: walls=30 425 let outbuf: *u8 = sys_mmap(K_MAGIC_65536) 426 let m: i64 = ge_apply(inbuf, n, "walls" as *u8, 30, outbuf) 427 r[0] = 0 - 1; r[1] = 0 - 1; r[2] = 0 - 1; r[3] = 0 - 1; r[4] = 0 - 1; r[5] = 0 428 if m > 0 { 429 r[0] = ge_int(outbuf, m, "walls" as *u8, 0 - 1) 430 r[1] = ge_int(outbuf, m, "enemy_base" as *u8, 0 - 1) 431 r[2] = ge_int(outbuf, m, "ehp_base" as *u8, 0 - 1) 432 r[3] = ge_int(outbuf, m, "dmg_base" as *u8, 0 - 1) 433 r[4] = ge_count_rows(outbuf, m) 434 r[5] = ge_title_crypt(outbuf, m) 435 } 436 r[6] = 0 437 if ge_apply(inbuf, n, "foo" as *u8, 5, scratch) < 0 { r[6] = 1 } 438 r[7] = 0 439 if ge_apply(inbuf, n, "walls" as *u8, K_MAGIC_99999, scratch) < 0 { r[7] = 1 } 440 r[8] = 0 441 if ge_apply(inbuf, n, "walls" as *u8, 0 - 5, scratch) < 0 { r[8] = 1 } 442 443 // (b) FSM-cell edit: (1,4) -> -1 (remove the flee transition) 444 let fbuf: *u8 = sys_mmap(K_MAGIC_65536) 445 let fm: i64 = ge_apply_fsm(inbuf, n, 1, 4, 0 - 1, fbuf) 446 r[9] = 0 - 999; r[10] = 0 - 999; r[11] = 0 - 999; r[12] = 0 - 1; r[13] = 0 - 1 447 if fm > 0 { 448 r[9] = ge_fsm_cell(fbuf, fm, 1, 4) 449 r[10] = ge_fsm_cell(fbuf, fm, 1, 1) 450 r[11] = ge_fsm_cell(fbuf, fm, 1, 3) 451 r[12] = ge_count_rows(fbuf, fm) 452 r[13] = ge_int(fbuf, fm, "walls" as *u8, 0 - 1) 453 } 454 r[14] = 0 455 if ge_apply_fsm(inbuf, n, 99, 0, 0, scratch) < 0 { r[14] = 1 } 456 r[15] = 0 457 if ge_apply_fsm(inbuf, n, 0, 99, 0, scratch) < 0 { r[15] = 1 } 458 r[16] = 0 459 if ge_apply_fsm(inbuf, n, 1, 4, 99, scratch) < 0 { r[16] = 1 } 460 461 // (c) TRANSACTIONAL BATCH: 3 chained edits applied atomically 462 let bbuf: *u8 = sys_mmap(K_MAGIC_65536) 463 let sc: *u8 = "knob walls 30\nknob dmg_base 25\nfsm 1 4 -1\n" as *u8 464 let bm: i64 = ge_apply_batch(inbuf, n, sc, ge_len(sc), bbuf) 465 r[17] = 0 - 1; r[18] = 0 - 1; r[19] = 0 - 999; r[20] = 0; r[21] = 0 - 1; r[22] = 0 - 1 466 if bm > 0 { 467 r[17] = ge_int(bbuf, bm, "walls" as *u8, 0 - 1) 468 r[18] = ge_int(bbuf, bm, "dmg_base" as *u8, 0 - 1) 469 r[19] = ge_fsm_cell(bbuf, bm, 1, 4) 470 r[20] = ge_title_crypt(bbuf, bm) 471 r[21] = ge_int(bbuf, bm, "enemy_base" as *u8, 0 - 1) 472 r[22] = ge_count_rows(bbuf, bm) 473 } 474 // atomicity: a batch with ONE bad edit must refuse the WHOLE batch (no partial apply / no output) 475 let badsc: *u8 = "knob walls 30\nknob nosuch 5\nfsm 1 4 -1\n" as *u8 476 r[23] = 0 477 if ge_apply_batch(inbuf, n, badsc, ge_len(badsc), scratch) < 0 { r[23] = 1 } 478 479 var ok: i64 = 1 480 if r[0] != 30 { ok = 0 } 481 if r[1] != 2 { ok = 0 } 482 if r[2] != 18 { ok = 0 } 483 if r[3] != 8 { ok = 0 } 484 if r[4] != 6 { ok = 0 } 485 if r[5] != 1 { ok = 0 } 486 if r[6] != 1 { ok = 0 } 487 if r[7] != 1 { ok = 0 } 488 if r[8] != 1 { ok = 0 } 489 if r[9] != 0 - 1 { ok = 0 } 490 if r[10] != 2 { ok = 0 } 491 if r[11] != 3 { ok = 0 } 492 if r[12] != 6 { ok = 0 } 493 if r[13] != 20 { ok = 0 } 494 if r[14] != 1 { ok = 0 } 495 if r[15] != 1 { ok = 0 } 496 if r[16] != 1 { ok = 0 } 497 if r[17] != 30 { ok = 0 } 498 if r[18] != 25 { ok = 0 } 499 if r[19] != 0 - 1 { ok = 0 } 500 if r[20] != 1 { ok = 0 } 501 if r[21] != 2 { ok = 0 } 502 if r[22] != 6 { ok = 0 } 503 if r[23] != 1 { ok = 0 } 504 505 ge_report(1, r, ok) 506 let lf: i64 = sys_openat_append("knowledge/status/game_edit.log" as *u8, 420) 507 if lf >= 0 { ge_report(lf, r, ok); sys_close(lf) } 508 if ok == 1 { sys_exit(0); return 0 } 509 sys_exit(1); return 1 510}