code wiki / _hdl_build / nx_tactics_engine_gate.nx
nx_tactics_engine_gate.nx source
↩ module page · 196 lines · 7698 B
1// nx_tactics_engine_gate.nx -- gate for the data-driven TACTICS SIMULATION engine (G-ECO-005 rung 1).
2// Drives the REAL nx_tactics_engine_lib headlessly (no terminal) and asserts the engine is genuinely
3// CONFIG-DRIVEN + the core mechanics work, by RUNNING (not asserting):
4// (1) DATA-DRIVEN: two distinct configs yield distinct rosters (2v2 vs 2v3, 4 vs 5 units).
5// (2) WIN/LOSE both reachable: clearing team1 -> mode=win; clearing team0 -> mode=lose.
6// (3) COVER matters: hit-chance on open cell > hit-chance on a cover cell (same distance).
7// (4) BOUNDS: out-of-grid reads = wall; a move into a wall is rejected (no move, no move spent).
8// (5) EMERGENT PLAYTHROUGH: a scripted bot (advance-to-range + shoot) drives a full 2v2 battle to a
9// RESOLUTION (win or lose) within a bounded turn budget -- the whole turn loop + AI run end-to-end.
10// TACTICS-ENGINE-GATE verdict=GREEN iff all hold -> knowledge/status/tactics_engine.log.
11// license_tier: ORIGINAL
12import "nx_syscalls.nx"
13import "nx_tactics_engine_lib.nx"
14
15func tg_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 }
16func tg_n(fd: i64, v: i64) -> i64 {
17 var m: i64 = v
18 if m < 0 { sys_write(fd, "-" as *u8, 1); m = 0 - m }
19 let bb: *u8 = sys_mmap(28); let t: *u8 = sys_mmap(28); var k: i64 = 0
20 if m == 0 { t[0] = 48 as u8; k = 1 }
21 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
22 var i: i64 = 0
23 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
24 sys_write(fd, bb, k); return 0
25}
26
27func tg_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
28func tg_has(buf: *u8, len: i64, pat: *u8) -> i64 {
29 let pl: i64 = tg_len(pat)
30 var i: i64 = 0
31 while i + pl <= len {
32 var j: i64 = 0; var ok: i64 = 1
33 while j < pl { if buf[i + j] != pat[j] { ok = 0 } j = j + 1 }
34 if ok == 1 { return 1 }
35 i = i + 1
36 }
37 return 0
38}
39func tg_count_nl(buf: *u8, len: i64) -> i64 {
40 var n: i64 = 0; var i: i64 = 0
41 while i < len { if buf[i] == (10 as u8) { n = n + 1 } i = i + 1 }
42 return n
43}
44
45// scripted bot: advance the active player toward the nearest foe, shoot when in range; drive the
46// whole battle to a resolution. returns final mode (0 if it hit the cap = unresolved). itout[0]=iters.
47func tbot(st: *i64, cap: i64, itout: *i64) -> i64 {
48 var iter: i64 = 0
49 while iter < cap {
50 if st[2] != 0 { itout[0] = iter; return st[2] }
51 let ci: i64 = st[1]
52 if ci < 0 { itout[0] = iter; return st[2] }
53 let ei: i64 = teng_nearest_foe(st, st[16 + ci], st[22 + ci], st[34 + ci])
54 if ei < 0 { teng_check_end(st); itout[0] = iter; return st[2] }
55 let d: i64 = teng_dist(st[16 + ci], st[22 + ci], st[16 + ei], st[22 + ei])
56 if d <= st[71] {
57 teng_input(st, 4)
58 } else {
59 let ox: i64 = st[16 + ci]; let oy: i64 = st[22 + ci]
60 let tx: i64 = st[16 + ei]; let ty: i64 = st[22 + ei]
61 var key: i64 = 0 - 1
62 if teng_abs(tx - ox) >= teng_abs(ty - oy) {
63 if tx > ox { key = 3 }
64 if tx < ox { key = 2 }
65 } else {
66 if ty > oy { key = 1 }
67 if ty < oy { key = 0 }
68 }
69 if key < 0 { teng_input(st, 5) } else {
70 teng_input(st, key)
71 var moved: i64 = 1
72 if st[16 + ci] == ox { if st[22 + ci] == oy { moved = 0 } }
73 if moved == 0 { teng_input(st, 5) } else { if st[5] <= 0 { teng_input(st, 5) } }
74 }
75 }
76 iter = iter + 1
77 }
78 itout[0] = iter
79 return st[2]
80}
81
82func tg_report(fd: i64, r: *i64, ok: i64) -> i64 {
83 tg_w(fd, "TACTICS-ENGINE-GATE authored=organ datadriven[A_squad2->" as *u8); tg_n(fd, r[0])
84 tg_w(fd, " A_enemy2->" as *u8); tg_n(fd, r[1])
85 tg_w(fd, " B_enemy3->" as *u8); tg_n(fd, r[2])
86 tg_w(fd, " B_units5->" as *u8); tg_n(fd, r[3])
87 tg_w(fd, "] winlose[win->" as *u8); tg_n(fd, r[4])
88 tg_w(fd, " lose->" as *u8); tg_n(fd, r[5])
89 tg_w(fd, "] cover[open=" as *u8); tg_n(fd, r[6])
90 tg_w(fd, ">covered=" as *u8); tg_n(fd, r[7])
91 tg_w(fd, "] bounds[oobL=" as *u8); tg_n(fd, r[8])
92 tg_w(fd, " oobR=" as *u8); tg_n(fd, r[9])
93 tg_w(fd, " wallblock=" as *u8); tg_n(fd, r[10])
94 tg_w(fd, "] play[resolved=" as *u8); tg_n(fd, r[13])
95 tg_w(fd, " mode=" as *u8); tg_n(fd, r[11])
96 tg_w(fd, " iters=" as *u8); tg_n(fd, r[12])
97 tg_w(fd, "] render[len=" as *u8); tg_n(fd, r[14])
98 tg_w(fd, " hud=" as *u8); tg_n(fd, r[15])
99 tg_w(fd, " P1=" as *u8); tg_n(fd, r[16])
100 tg_w(fd, " E1=" as *u8); tg_n(fd, r[17])
101 tg_w(fd, " rows=" as *u8); tg_n(fd, r[18])
102 if ok == 1 { tg_w(fd, "] verdict=GREEN\n" as *u8) } else { tg_w(fd, "] verdict=RED\n" as *u8) }
103 return 0
104}
105
106func main() -> i64 {
107 let st: *i64 = sys_mmap(8192) as *i64
108 let cfg: *i64 = sys_mmap(128) as *i64
109 let r: *i64 = sys_mmap(128) as *i64
110 let itp: *i64 = sys_mmap(16) as *i64
111
112 // config A = 2v2
113 cfg[0] = 16; cfg[1] = 12; cfg[2] = 2; cfg[3] = 2; cfg[4] = 10; cfg[5] = 5; cfg[6] = 5
114 cfg[7] = 9; cfg[8] = 10; cfg[9] = 8; cfg[10] = 8; cfg[11] = 3; cfg[12] = 3; cfg[13] = 12345
115
116 // (1) data-driven rosters
117 teng_init(st, cfg)
118 r[0] = teng_team_alive(st, 0)
119 r[1] = teng_team_alive(st, 1)
120 cfg[3] = 3; cfg[4] = 20 // config B = 2v3, tougher
121 teng_init(st, cfg)
122 r[2] = teng_team_alive(st, 1)
123 r[3] = teng_nunits(st)
124 cfg[3] = 2; cfg[4] = 10 // back to A
125
126 // (2) win/lose logic both reachable
127 teng_init(st, cfg)
128 let nu: i64 = teng_nunits(st)
129 var i: i64 = st[66]
130 while i < nu { st[40 + i] = 0; i = i + 1 } // clear team1
131 teng_check_end(st)
132 r[4] = st[2]
133 teng_init(st, cfg)
134 var j: i64 = 0
135 while j < st[66] { st[40 + j] = 0; j = j + 1 } // clear team0
136 teng_check_end(st)
137 r[5] = st[2]
138
139 // (3) cover reduces hit chance
140 teng_init(st, cfg)
141 r[6] = teng_chance(st, 2, 0)
142 r[7] = teng_chance(st, 2, 1)
143
144 // (4) bounds + wall-block
145 r[8] = 0
146 if teng_cell_get(st, 0 - 1, 0) == 1 { r[8] = 1 }
147 r[9] = 0
148 if teng_cell_get(st, st[64], 0) == 1 { r[9] = 1 }
149 let ci: i64 = st[1]
150 st[16 + ci] = 1; st[22 + ci] = 1; st[5] = 5
151 teng_move_unit(st, ci, 0, 0 - 1)
152 r[10] = 0
153 if st[22 + ci] == 1 { if st[5] == 5 { r[10] = 1 } }
154
155 // (5) emergent playthrough -> resolution
156 teng_init(st, cfg)
157 r[11] = tbot(st, 6000, itp)
158 r[12] = itp[0]
159 r[13] = 0
160 if r[11] != 0 { r[13] = 1 }
161
162 // (6) RENDER (rung 2): a fresh battle renders an ANSI frame with HUD + both teams' unit glyphs
163 teng_init(st, cfg)
164 let fb: *u8 = sys_mmap(16384)
165 let flen: i64 = teng_render(fb, st)
166 r[14] = flen
167 r[15] = tg_has(fb, flen, "TACTICS" as *u8)
168 r[16] = tg_has(fb, flen, "P1" as *u8)
169 r[17] = tg_has(fb, flen, "E1" as *u8)
170 r[18] = tg_count_nl(fb, flen)
171
172 var ok: i64 = 1
173 if r[0] != 2 { ok = 0 }
174 if r[1] != 2 { ok = 0 }
175 if r[2] != 3 { ok = 0 }
176 if r[3] != 5 { ok = 0 }
177 if r[4] != 1 { ok = 0 }
178 if r[5] != 2 { ok = 0 }
179 if r[6] <= r[7] { ok = 0 }
180 if r[8] != 1 { ok = 0 }
181 if r[9] != 1 { ok = 0 }
182 if r[10] != 1 { ok = 0 }
183 if r[13] != 1 { ok = 0 }
184 if r[14] <= 200 { ok = 0 }
185 if r[15] != 1 { ok = 0 }
186 if r[16] != 1 { ok = 0 }
187 if r[17] != 1 { ok = 0 }
188 if r[18] < 12 { ok = 0 }
189
190 tg_report(1, r, ok)
191 let lf: i64 = sys_openat_append("knowledge/status/tactics_engine.log" as *u8, 420)
192 if lf >= 0 { tg_report(lf, r, ok); sys_close(lf) }
193 if ok == 1 { sys_exit(0); return 0 }
194 sys_exit(1)
195 return 1
196}