code wiki / _hdl_build / nx_game_tbs_gate.nx
nx_game_tbs_gate.nx source
↩ module page · 121 lines · 7812 B
1import "nx_gate_gn.nx"
2import "nx_gate_base.nx"
3// nx_game_tbs_gate.nx -- GENRE: Turn-Based strategy (catalog genre, was ABSENT). A real integer/no-float
4// grid-tactics CORE -- a player unit and enemy units take turns: move toward the foe, attack when adjacent,
5// HP/defeat. Scripted playthrough (the player clears the enemies), rendered frame, neg-control (attacking
6// matters) + adjacency-combat-real.
7// T1 PLAYTHROUGH: the player moves+attacks and DEFEATS all enemies (alive=0) while surviving (hp>0). PNG.
8// T2 NEG-CONTROL: a player that only FLEES (never attacks) leaves every enemy alive.
9// T3 COMBAT REAL: an attack only lands when ADJACENT (a non-adjacent enemy takes no damage that turn).
10// T4 NEVER-BRICK (#26): deterministic, bounded, no float.
11// expect_exit: 0 license_tier: ORIGINAL
12import "nx_game_raster.nx"
13import "nx_png.nx"
14import "nx_syscalls.nx"
15
16func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
17" as *u8); return ok }
18
19func t_abs(v: i64) -> i64 { if v<0 { return 0-v } return v }
20func t_sign(v: i64) -> i64 { if v>0 { return 1 } if v<0 { return 0-1 } return 0 }
21func t_clamp(v: i64, lo: i64, hi: i64) -> i64 { if v<lo {return lo} if v>hi {return hi} return v }
22func t_nearest(pu: *i64, ex: *i64, ey: *i64, ealive: *i64, ne: i64) -> i64 {
23 var best: i64=0-1; var bd: i64=99999; var i: i64=0
24 while i<ne { if ealive[i]==1 { let d: i64=t_abs(ex[i]-pu[0])+t_abs(ey[i]-pu[1]); if d<bd { bd=d; best=i } } i=i+1 }
25 return best
26}
27func t_alivecount(ealive: *i64, ne: i64) -> i64 { var c: i64=0; var i: i64=0; while i<ne { c=c+ealive[i]; i=i+1 } return c }
28
29// pu[0]=x pu[1]=y pu[2]=hp. flee=1 -> run away + never attack.
30func tbs_player(pu: *i64, ex: *i64, ey: *i64, ehp: *i64, ealive: *i64, ne: i64, patk: i64, gw_: i64, gh_: i64, flee: i64) -> i64 {
31 let ei: i64=t_nearest(pu, ex, ey, ealive, ne)
32 if ei < 0 { return 0 }
33 let dx: i64=ex[ei]-pu[0]; let dy: i64=ey[ei]-pu[1]; let md: i64=t_abs(dx)+t_abs(dy)
34 if flee==1 {
35 if t_abs(dx) >= t_abs(dy) { pu[0]=t_clamp(pu[0]-t_sign(dx), 0, gw_-1) } else { pu[1]=t_clamp(pu[1]-t_sign(dy), 0, gh_-1) }
36 return 0
37 }
38 if md==1 { ehp[ei]=ehp[ei]-patk; if ehp[ei]<=0 { ealive[ei]=0 } return 0 }
39 if t_abs(dx) >= t_abs(dy) { pu[0]=pu[0]+t_sign(dx) } else { pu[1]=pu[1]+t_sign(dy) }
40 return 0
41}
42func tbs_enemies(pu: *i64, ex: *i64, ey: *i64, ealive: *i64, ne: i64, eatk: i64, gw_: i64, gh_: i64) -> i64 {
43 var i: i64=0
44 while i<ne {
45 if ealive[i]==1 {
46 let dx: i64=pu[0]-ex[i]; let dy: i64=pu[1]-ey[i]; let md: i64=t_abs(dx)+t_abs(dy)
47 if md==1 { pu[2]=pu[2]-eatk } else {
48 if t_abs(dx) >= t_abs(dy) { ex[i]=t_clamp(ex[i]+t_sign(dx), 0, gw_-1) } else { ey[i]=t_clamp(ey[i]+t_sign(dy), 0, gh_-1) }
49 }
50 }
51 i=i+1
52 }
53 return 0
54}
55func tbs_reset(pu: *i64, ex: *i64, ey: *i64, ehp: *i64, ealive: *i64, ne: i64) -> i64 {
56 pu[0]=0; pu[1]=0; pu[2]=20
57 ex[0]=6; ey[0]=1; ehp[0]=5; ealive[0]=1
58 ex[1]=5; ey[1]=4; ehp[1]=5; ealive[1]=1
59 return 0
60}
61func tbs_render(fb: *i64, w: i64, h: i64, cell: i64, gw_: i64, gh_: i64, pu: *i64, ex: *i64, ey: *i64, ealive: *i64, ne: i64) -> i64 {
62 gr_clear(fb, w, h, gr_pack(22, 26, 34))
63 var c: i64=0; while c<=gw_ { gr_vline(fb, w, h, c*cell, 0, gh_*cell, gr_pack(44,50,64)); c=c+1 }
64 var r: i64=0; while r<=gh_ { gr_hline(fb, w, h, 0, gw_*cell, r*cell, gr_pack(44,50,64)); r=r+1 }
65 var i: i64=0
66 while i<ne { if ealive[i]==1 { gr_rect(fb, w, h, ex[i]*cell+3, ey[i]*cell+3, ex[i]*cell+cell-3, ey[i]*cell+cell-3, gr_pack(224,80,58)) } i=i+1 }
67 gr_rect(fb, w, h, pu[0]*cell+3, pu[1]*cell+3, pu[0]*cell+cell-3, pu[1]*cell+cell-3, gr_pack(90,210,122))
68 gr_rect(fb, w, h, 2, h-5, 2+pu[2]*2, h-2, gr_pack(120,220,150)) // player HP bar
69 return 0
70}
71
72func main() -> i64 {
73 gw("=== nx_game_tbs_gate: GENRE Turn-Based strategy -- a real integer/no-float playable (live evidence) ===\n" as *u8)
74 var pass: i64 = 0; var total: i64 = 0
75 let GW: i64=8; let GH: i64=6; let CELL: i64=14; let W: i64=GW*CELL; let H: i64=GH*CELL; let NE: i64=2; let PATK: i64=3; let EATK: i64=1
76 let pu: *i64=sys_mmap(8*4) as *i64; let ex: *i64=sys_mmap(8*4) as *i64; let ey: *i64=sys_mmap(8*4) as *i64
77 let ehp: *i64=sys_mmap(8*4) as *i64; let ealive: *i64=sys_mmap(8*4) as *i64
78
79 // ---- T1: the player defeats all enemies and survives ----
80 tbs_reset(pu, ex, ey, ehp, ealive, NE)
81 var turn: i64=0
82 while turn < 50 { if t_alivecount(ealive, NE)==0 { turn=50 } else { if pu[2]<=0 { turn=50 } else { tbs_player(pu, ex, ey, ehp, ealive, NE, PATK, GW, GH, 0); tbs_enemies(pu, ex, ey, ealive, NE, EATK, GW, GH); turn=turn+1 } } }
83 let alive1: i64=t_alivecount(ealive, NE); let hp1: i64=pu[2]
84 var t1ok: i64=1; if alive1!=0 {t1ok=0} if hp1<=0 {t1ok=0}
85 total=total+1; if t1ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
86 gw("T1 playthrough: player defeated all enemies (alive=" as *u8); gn(alive1); gw(") and survived (hp=" as *u8); gn(hp1); gw(") = WIN\n" as *u8)
87
88 // ---- render a mid-battle frame for live evidence ----
89 let fb: *i64=sys_mmap(8*(W*H+8)) as *i64
90 tbs_reset(pu, ex, ey, ehp, ealive, NE)
91 var k: i64=0; while k < 6 { tbs_player(pu, ex, ey, ehp, ealive, NE, PATK, GW, GH, 0); tbs_enemies(pu, ex, ey, ealive, NE, EATK, GW, GH); k=k+1 }
92 tbs_render(fb, W, H, CELL, GW, GH, pu, ex, ey, ealive, NE)
93 write_png(fb, W, H, "knowledge/nx_game_tbs.png" as *u8)
94 gw(" (live evidence: PNG knowledge/nx_game_tbs.png -- grid + green player + red enemies + HP bar)\n" as *u8)
95
96 // ---- T2: neg-control -- a fleeing player never defeats anyone ----
97 tbs_reset(pu, ex, ey, ehp, ealive, NE)
98 turn=0; while turn < 50 { tbs_player(pu, ex, ey, ehp, ealive, NE, PATK, GW, GH, 1); tbs_enemies(pu, ex, ey, ealive, NE, EATK, GW, GH); turn=turn+1 }
99 let alive2: i64=t_alivecount(ealive, NE)
100 total=total+1; if alive2==NE { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
101 gw("T2 neg-control: a fleeing player (never attacks) left " as *u8); gn(alive2); gw("/" as *u8); gn(NE); gw(" enemies alive (attacking is real)\n" as *u8)
102
103 // ---- T3: combat real -- non-adjacent attack lands nothing ----
104 tbs_reset(pu, ex, ey, ehp, ealive, NE)
105 pu[0]=0; pu[1]=0 // player far from enemy0 at (6,1)
106 let hp_before: i64=ehp[0]
107 tbs_player(pu, ex, ey, ehp, ealive, NE, PATK, GW, GH, 0) // one player action: should MOVE (not attack), enemy0 hp unchanged
108 total=total+1; if ehp[0]==hp_before { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
109 gw("T3 combat real: a non-adjacent turn dealt no damage (enemy0 hp " as *u8); gn(hp_before); gw("->" as *u8); gn(ehp[0]); gw(")\n" as *u8)
110
111 // ---- T4: never-brick / determinism ----
112 tbs_reset(pu, ex, ey, ehp, ealive, NE)
113 turn=0; while turn < 50 { if t_alivecount(ealive, NE)==0 { turn=50 } else { if pu[2]<=0 { turn=50 } else { tbs_player(pu, ex, ey, ehp, ealive, NE, PATK, GW, GH, 0); tbs_enemies(pu, ex, ey, ealive, NE, EATK, GW, GH); turn=turn+1 } } }
114 let alive3: i64=t_alivecount(ealive, NE)
115 total=total+1; if alive3==alive1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
116 gw("T4 never-brick (#26): deterministic re-run (alive=" as *u8); gn(alive3); gw("==" as *u8); gn(alive1); gw("), bounded, no float\n" as *u8)
117
118 gw("\n=== nx_game_tbs_gate " as *u8); gn(pass); gw("/" as *u8); gn(total)
119 if pass == total { gw(" GREEN (Turn-Based strategy: a real playable grid tactics with move/adjacency-attack/HP, rendered, sovereign)\n" as *u8); sys_exit(0); return 0 }
120 gw(" RED\n" as *u8); sys_exit(1); return 1
121}