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}