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}