code wiki / _hdl_build / nx_game_agent_gate.nx
nx_game_agent_gate.nx source
↩ module page · 262 lines · 13275 B
1// nx_game_agent_gate.nx -- proves the NPC/enemy brain (perceive -> chase -> attack -> flee) actually
2// behaves, with the lane's discipline: anti-vacuity guards on every claim, a U-pocket maze tooth that a
3// GREEDY chaser provably jams in (so CHASE is real routing, not wishful adjacency), causal ATTACK events
4// (every one emitted at manhattan distance 1), and determinism across full replays.
5// MUTATION TARGET (documented): sever the LOS check in ag_tick (perceive through walls) => T2 wall-blind
6// tooth RED while the open-field teeth stay GREEN. license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_game_agent.nx"
9
10func p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
11func pn(v: i64) -> i64 {
12 let t: *u8 = sys_mmap(32) as *u8
13 var m: i64 = v
14 var w: i64 = 0
15 if m < 0 { t[w] = 45 as u8; w = w + 1; m = 0 - m }
16 if m == 0 { t[w] = 48 as u8; sys_write(1, t, w + 1); return 0 }
17 let d: *u8 = sys_mmap(32) as *u8
18 var k: i64 = 0
19 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
20 var j: i64 = 0
21 while j < k { t[w] = d[k - 1 - j]; w = w + 1; j = j + 1 }
22 sys_write(1, t, w)
23 return 0
24}
25func nl() -> i64 { p("\n" as *u8); return 0 }
26
27const W: i64 = 20
28const H: i64 = 14
29
30func world_border(wd: *i64) -> i64 {
31 var i: i64 = 0
32 while i < W { wd_set_wall(wd, i, 0, 1); wd_set_wall(wd, i, H - 1, 1); i = i + 1 }
33 var j: i64 = 0
34 while j < H { wd_set_wall(wd, 0, j, 1); wd_set_wall(wd, W - 1, j, 1); j = j + 1 }
35 return 0
36}
37
38func main() -> i64 {
39 p("=== nx_game_agent_gate (NPC/enemy brain: perceive/chase/attack/flee) ===\n" as *u8)
40 var pass: i64 = 0
41 let checks: i64 = 7
42 let out: *i64 = sys_mmap(64) as *i64
43 let tbl: *i64 = sys_mmap(16 * 8) as *i64
44 ag_species_default(tbl)
45 let scr: *i64 = sys_mmap(ag_scratch_words(W, H) * 8) as *i64
46
47 // ---------- T1: out of range -> stays PATROL and never leaves home radius ----------
48 let wd1: *i64 = sys_mmap(wd_bytes(W, H)) as *i64
49 wd_init(wd1, W, H); world_border(wd1)
50 let ar1: *i64 = sys_mmap(en_bytes(16, AC_NC)) as *i64
51 en_init(ar1, 16, AC_NC)
52 let pl1: i64 = ac_spawn(wd1, ar1, 17, 11, 0, 30, 0, 3, 1)
53 let ag1: i64 = ac_spawn(wd1, ar1, 3, 3, 1, 20, 1, 2, 99) // hunter, aggro 7; player is ~14 away
54 let ev1: *i64 = sys_mmap((1 + 64*EV_STRIDE) * 8) as *i64
55 ev1[0] = 0
56 var t: i64 = 0
57 var maxdev: i64 = 0
58 while t < 60 {
59 ag_tick(wd1, ar1, ag1, pl1, tbl, scr, ev1, 64, t, out)
60 var dx: i64 = en_get(ar1, ag1, A_X) - en_get(ar1, ag1, A_HOMEX)
61 if dx < 0 { dx = 0 - dx }
62 var dy: i64 = en_get(ar1, ag1, A_Y) - en_get(ar1, ag1, A_HOMEY)
63 if dy < 0 { dy = 0 - dy }
64 if dx + dy > maxdev { maxdev = dx + dy }
65 t = t + 1
66 }
67 var t1: i64 = 0
68 if en_get(ar1, ag1, A_STATE) == AG_PATROL { if maxdev <= 4 { if maxdev >= 1 { if ev_count(ev1) == 0 { t1 = 1 } } } }
69 if t1 == 1 { pass = pass + 1; p("T1 GREEN patrol holds range: 60 ticks, max home deviation " as *u8); pn(maxdev); p(", 0 events, state PATROL (and it DID move)\n" as *u8) }
70 if t1 == 0 { p("T1 RED patrol maxdev=" as *u8); pn(maxdev); p(" state=" as *u8); pn(en_get(ar1, ag1, A_STATE)); p(" ev=" as *u8); pn(ev_count(ev1)); nl() }
71
72 // ---------- T2: WALL-BLIND perception -- in radius but behind a full wall: must NOT aggro ----------
73 let wd2: *i64 = sys_mmap(wd_bytes(W, H)) as *i64
74 wd_init(wd2, W, H); world_border(wd2)
75 var wy: i64 = 1
76 while wy < H - 1 { wd_set_wall(wd2, 9, wy, 1); wy = wy + 1 } // solid vertical wall, no gap
77 let ar2: *i64 = sys_mmap(en_bytes(16, AC_NC)) as *i64
78 en_init(ar2, 16, AC_NC)
79 let pl2: i64 = ac_spawn(wd2, ar2, 11, 6, 0, 30, 0, 3, 1) // 4 cells away but walled off
80 let ag2: i64 = ac_spawn(wd2, ar2, 7, 6, 1, 20, 1, 2, 55)
81 let ev2: *i64 = sys_mmap((1 + 64*EV_STRIDE) * 8) as *i64
82 ev2[0] = 0
83 var t2ticks: i64 = 0
84 while t2ticks < 40 { ag_tick(wd2, ar2, ag2, pl2, tbl, scr, ev2, 64, t2ticks, out); t2ticks = t2ticks + 1 }
85 var spotted2: i64 = 0
86 var e2: i64 = 0
87 while e2 < ev_count(ev2) { if ev_kind(ev2, e2) == EV_SPOTTED { spotted2 = spotted2 + 1 } e2 = e2 + 1 }
88 var t2: i64 = 0
89 if spotted2 == 0 {
90 // anti-vacuity: SAME geometry with the wall opened -> must spot within 40 ticks
91 wd_set_wall(wd2, 9, 6, 0)
92 var t2b: i64 = 0
93 while t2b < 40 { ag_tick(wd2, ar2, ag2, pl2, tbl, scr, ev2, 64, 100 + t2b, out); t2b = t2b + 1 }
94 var spotted2b: i64 = 0
95 var e2b: i64 = 0
96 while e2b < ev_count(ev2) { if ev_kind(ev2, e2b) == EV_SPOTTED { spotted2b = spotted2b + 1 } e2b = e2b + 1 }
97 if spotted2b > 0 { t2 = 1 }
98 }
99 if t2 == 1 { pass = pass + 1; p("T2 GREEN wall-blind: walled player never spotted in 40 ticks; open the wall -> SPOTTED (LOS is real)\n" as *u8) }
100 if t2 == 0 { p("T2 RED wall-blind spotted=" as *u8); pn(spotted2); nl() }
101
102 // ---------- T3: U-POCKET MAZE -- greedy jams, the brain must still reach + attack ----------
103 // pocket: walls form a U opening AWAY from the player; agent starts inside the U.
104 let wd3: *i64 = sys_mmap(wd_bytes(W, H)) as *i64
105 wd_init(wd3, W, H); world_border(wd3)
106 // U around (4,6)..(6,8): top wall (3,5)-(7,5), right wall (7,6)-(7,8), bottom wall (3,9)-(7,9)
107 var ux: i64 = 3
108 while ux <= 7 { wd_set_wall(wd3, ux, 5, 1); wd_set_wall(wd3, ux, 9, 1); ux = ux + 1 }
109 var uy: i64 = 6
110 while uy <= 8 { wd_set_wall(wd3, 7, uy, 1); uy = uy + 1 }
111 let ar3: *i64 = sys_mmap(en_bytes(16, AC_NC)) as *i64
112 en_init(ar3, 16, AC_NC)
113 let pl3: i64 = ac_spawn(wd3, ar3, 10, 7, 0, 30, 0, 3, 1) // due EAST, outside the pocket
114 let ag3: i64 = ac_spawn(wd3, ar3, 5, 7, 2, 20, 2, 2, 7) // greedy CONTROL, inside the U
115 let ag3b: i64 = ac_spawn(wd3, ar3, 5, 6, 2, 20, 2, 2, 8) // the A* walker, inside the U
116 // greedy control: prove the pocket actually jams a greedy chaser (the tooth's neg-control)
117 var gj: i64 = 0
118 while gj < 30 { ag_step_toward(wd3, ar3, ag3, en_get(ar3, pl3, A_X), en_get(ar3, pl3, A_Y), out); gj = gj + 1 }
119 var greedy_x: i64 = en_get(ar3, ag3, A_X)
120 var jammed: i64 = 0
121 if greedy_x <= 6 { jammed = 1 } // still stuck at/inside the U wall
122 // the routing primitive (what CHASE moves with) must ESCAPE the U and reach adjacency
123 var steps3: i64 = 0
124 var arrived3: i64 = 0
125 var w3: i64 = 0
126 while w3 < 60 {
127 if arrived3 == 0 {
128 ag_step_astar(wd3, ar3, ag3b, en_get(ar3, pl3, A_X), en_get(ar3, pl3, A_Y), scr, out)
129 steps3 = steps3 + 1
130 if ac_adjacent(ar3, ag3b, pl3) == 1 { arrived3 = 1 }
131 }
132 w3 = w3 + 1
133 }
134 var t3: i64 = 0
135 if jammed == 1 { if arrived3 == 1 { if steps3 > 10 { t3 = 1 } } } // >10 = it genuinely detoured (direct is 6)
136 if t3 == 1 { pass = pass + 1; p("T3 GREEN real routing: greedy control JAMS in the U (x=" as *u8); pn(greedy_x); p("), A* escapes + reaches adjacency in " as *u8); pn(steps3); p(" steps (direct would be 6)\n" as *u8) }
137 if t3 == 0 { p("T3 RED maze jammed=" as *u8); pn(jammed); p(" arrived=" as *u8); pn(arrived3); p(" steps=" as *u8); pn(steps3); nl() }
138
139 // ---------- T4: ATTACK events are CAUSAL -- every one emitted at manhattan distance exactly 1 ----------
140 // re-run a fresh open-field chase logging distances at each ATTACK emission
141 let wd4: *i64 = sys_mmap(wd_bytes(W, H)) as *i64
142 wd_init(wd4, W, H); world_border(wd4)
143 let ar4: *i64 = sys_mmap(en_bytes(16, AC_NC)) as *i64
144 en_init(ar4, 16, AC_NC)
145 let pl4: i64 = ac_spawn(wd4, ar4, 12, 7, 0, 30, 0, 3, 1)
146 let ag4: i64 = ac_spawn(wd4, ar4, 6, 7, 1, 20, 1, 2, 11)
147 let ev4: *i64 = sys_mmap((1 + 128*EV_STRIDE) * 8) as *i64
148 ev4[0] = 0
149 var t4t: i64 = 0
150 var bad_dist: i64 = 0
151 var atk4: i64 = 0
152 while t4t < 80 {
153 let st4: i64 = ag_tick(wd4, ar4, ag4, pl4, tbl, scr, ev4, 128, t4t, out)
154 if st4 == AG_ATTACK {
155 atk4 = atk4 + 1
156 var mdx: i64 = en_get(ar4, ag4, A_X) - en_get(ar4, pl4, A_X)
157 if mdx < 0 { mdx = 0 - mdx }
158 var mdy: i64 = en_get(ar4, ag4, A_Y) - en_get(ar4, pl4, A_Y)
159 if mdy < 0 { mdy = 0 - mdy }
160 if mdx + mdy != 1 { bad_dist = bad_dist + 1 }
161 }
162 t4t = t4t + 1
163 }
164 var t4: i64 = 0
165 if atk4 > 10 { if bad_dist == 0 { t4 = 1 } }
166 if t4 == 1 { pass = pass + 1; p("T4 GREEN causal attacks: " as *u8); pn(atk4); p(" ATTACK ticks, every one at distance exactly 1\n" as *u8) }
167 if t4 == 0 { p("T4 RED attacks=" as *u8); pn(atk4); p(" bad_dist=" as *u8); pn(bad_dist); nl() }
168
169 // ---------- T5: FLEE -- weak agent runs, distance to player must GROW ----------
170 let wd5: *i64 = sys_mmap(wd_bytes(W, H)) as *i64
171 wd_init(wd5, W, H); world_border(wd5)
172 let ar5: *i64 = sys_mmap(en_bytes(16, AC_NC)) as *i64
173 en_init(ar5, 16, AC_NC)
174 let pl5: i64 = ac_spawn(wd5, ar5, 9, 7, 0, 30, 0, 3, 1)
175 let ag5: i64 = ac_spawn(wd5, ar5, 11, 7, 1, 20, 0, 2, 23) // docile scout: flees below 500 permil
176 en_set(ar5, ag5, A_HP, 5) // 250 permil -> weak
177 let ev5: *i64 = sys_mmap((1 + 64*EV_STRIDE) * 8) as *i64
178 ev5[0] = 0
179 let d5a: i64 = ac_dist2(ar5, ag5, pl5)
180 var t5t: i64 = 0
181 var fled_ev: i64 = 0
182 while t5t < 30 {
183 ag_tick(wd5, ar5, ag5, pl5, tbl, scr, ev5, 64, t5t, out)
184 t5t = t5t + 1
185 }
186 var e5: i64 = 0
187 while e5 < ev_count(ev5) { if ev_kind(ev5, e5) == EV_FLED { fled_ev = fled_ev + 1 } e5 = e5 + 1 }
188 let d5b: i64 = ac_dist2(ar5, ag5, pl5)
189 var t5: i64 = 0
190 if d5b > d5a { if fled_ev >= 1 { if en_get(ar5, ag5, A_STATE) == AG_FLEE { t5 = 1 } } }
191 if t5 == 1 { pass = pass + 1; p("T5 GREEN flee: weak scout ran, dist2 " as *u8); pn(d5a); p(" -> " as *u8); pn(d5b); p(", FLED event emitted\n" as *u8) }
192 if t5 == 0 { p("T5 RED flee d5a=" as *u8); pn(d5a); p(" d5b=" as *u8); pn(d5b); p(" state=" as *u8); pn(en_get(ar5, ag5, A_STATE)); nl() }
193
194 // ---------- T6: determinism -- full T4-style chase replayed twice, event stream + ck identical ----------
195 var rep: i64 = 0
196 var ck1: i64 = 0
197 var ck2: i64 = 0
198 var evck1: i64 = 0
199 var evck2: i64 = 0
200 while rep < 2 {
201 let wd6: *i64 = sys_mmap(wd_bytes(W, H)) as *i64
202 wd_init(wd6, W, H); world_border(wd6)
203 wd_set_wall(wd6, 9, 4, 1); wd_set_wall(wd6, 9, 5, 1)
204 let ar6: *i64 = sys_mmap(en_bytes(16, AC_NC)) as *i64
205 en_init(ar6, 16, AC_NC)
206 let pl6: i64 = ac_spawn(wd6, ar6, 13, 8, 0, 30, 0, 3, 1)
207 let ag6a: i64 = ac_spawn(wd6, ar6, 4, 3, 1, 20, 1, 2, 31)
208 let ag6b: i64 = ac_spawn(wd6, ar6, 4, 10, 1, 20, 0, 1, 32)
209 let ev6: *i64 = sys_mmap((1 + 256*EV_STRIDE) * 8) as *i64
210 ev6[0] = 0
211 var t6t: i64 = 0
212 var s6: i64 = 777
213 while t6t < 100 {
214 ag_tick(wd6, ar6, ag6a, pl6, tbl, scr, ev6, 256, t6t, out)
215 ag_tick(wd6, ar6, ag6b, pl6, tbl, scr, ev6, 256, t6t, out)
216 // player takes a small scripted walk too (world under motion, not a statue)
217 s6 = s6 * 1103515245 + 12345
218 var pd: i64 = (s6 >> 16) & 3
219 if pd < 0 { pd = 0 - pd }
220 var pdx: i64 = 0
221 var pdy: i64 = 0
222 if pd == 0 { pdx = 1 }
223 if pd == 1 { pdx = 0-1 }
224 if pd == 2 { pdy = 1 }
225 if pd == 3 { pdy = 0-1 }
226 ac_step(wd6, ar6, pl6, pdx, pdy, out)
227 t6t = t6t + 1
228 }
229 var ckw: i64 = ac_ck(wd6, ar6)
230 var cke: i64 = 1469598103
231 var e6: i64 = 0
232 let en6: i64 = ev_count(ev6)
233 while e6 < en6 * EV_STRIDE + 1 { cke = ((cke ^ ev6[e6]) * 1099511) & 4611686018427387903; e6 = e6 + 1 }
234 if rep == 0 { ck1 = ckw; evck1 = cke }
235 if rep == 1 { ck2 = ckw; evck2 = cke }
236 rep = rep + 1
237 }
238 var t6: i64 = 0
239 if ck1 == ck2 { if evck1 == evck2 { if ck1 != 0 { if evck1 != 0 { t6 = 1 } } } }
240 if t6 == 1 { pass = pass + 1; p("T6 GREEN deterministic: 2x100-tick 2-agent replay, world ck + event ck identical (ck=" as *u8); pn(ck1); p(")\n" as *u8) }
241 if t6 == 0 { p("T6 RED determinism\n" as *u8) }
242
243 // ---------- T7: anti-vacuity roll-up -- all states visited, events of 3+ kinds, agents MOVED ----------
244 var kinds: i64 = 0
245 var sawspot: i64 = 0
246 var sawatk: i64 = 0
247 var sawfled: i64 = 0
248 var e7: i64 = 0
249 while e7 < ev_count(ev4) { if ev_kind(ev4, e7) == EV_SPOTTED { sawspot = 1 } if ev_kind(ev4, e7) == EV_ATTACK { sawatk = 1 } e7 = e7 + 1 }
250 var e7b: i64 = 0
251 while e7b < ev_count(ev5) { if ev_kind(ev5, e7b) == EV_FLED { sawfled = 1 } e7b = e7b + 1 }
252 kinds = sawspot + sawatk + sawfled
253 var t7: i64 = 0
254 if kinds == 3 { if atk4 > 0 { if maxdev > 0 { t7 = 1 } } }
255 if t7 == 1 { pass = pass + 1; p("T7 GREEN anti-vacuity: SPOTTED+ATTACK+FLED all real, patrol moved, attacks landed\n" as *u8) }
256 if t7 == 0 { p("T7 RED anti-vacuity kinds=" as *u8); pn(kinds); nl() }
257
258 p("nx_game_agent_gate: " as *u8); pn(pass); p("/" as *u8); pn(checks); nl()
259 if pass == checks { p("VERDICT GREEN\n" as *u8); return 0 }
260 p("VERDICT RED\n" as *u8)
261 return 1
262}