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}