code wiki / _hdl_build / nx_game_td_gate.nx
nx_game_td_gate.nx source
↩ module page · 112 lines · 6851 B
1import "nx_gate_gn.nx"
2import "nx_gate_base.nx"
3// nx_game_td_gate.nx -- GENRE: Tower Defence (catalog genre, was ABSENT). A real integer/no-float TD CORE --
4// a creep wave walks a lane toward the base; towers shoot creeps in range; defend the base. Scripted
5// playthrough (towers clear the wave before it leaks), rendered frame (live evidence), neg-control (towers
6// matter) + collision-real.
7// T1 PLAYTHROUGH: the towers clear the whole wave before the base -- 0 leaked, base intact (DEFENDED). PNG.
8// T2 NEG-CONTROL: with NO towers the wave reaches the base (base damaged) -- towers are real.
9// T3 RANGE REAL: a tower only damages creeps within its range (an out-of-range creep is untouched).
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
19// cstatus[i]: 0=on-field, 1=killed, 2=leaked. cx[i]=x along the lane, chp[i]=hp. base[0]=base hp.
20func td_tick(cx: *i64, chp: *i64, cstatus: *i64, nc: i64, tx: *i64, nt: i64, rng: i64, dmg: i64, w: i64, base: *i64) -> i64 {
21 var t: i64=0
22 while t < nt {
23 var i: i64=0; var hit: i64=0
24 while i < nc {
25 if hit==0 { if cstatus[i]==0 { if cx[i]>=0 {
26 var dx: i64=cx[i]-tx[t]; if dx<0 {dx=0-dx}
27 if dx < rng { chp[i]=chp[i]-dmg; if chp[i]<=0 { cstatus[i]=1 } hit=1 }
28 } } }
29 i=i+1
30 }
31 t=t+1
32 }
33 var i: i64=0
34 while i < nc {
35 if cstatus[i]==0 { cx[i]=cx[i]+2; if cx[i]>=w { cstatus[i]=2; base[0]=base[0]-3 } }
36 i=i+1
37 }
38 return 0
39}
40func td_reset(cx: *i64, chp: *i64, cstatus: *i64, nc: i64, base: *i64) -> i64 {
41 var i: i64=0; while i<nc { cx[i]=0-(i*18); chp[i]=6; cstatus[i]=0; i=i+1 }
42 base[0]=10
43 return 0
44}
45func td_resolved(cstatus: *i64, nc: i64) -> i64 {
46 var i: i64=0; while i<nc { if cstatus[i]==0 { return 0 } i=i+1 }
47 return 1
48}
49func td_count(cstatus: *i64, nc: i64, want: i64) -> i64 { var c: i64=0; var i: i64=0; while i<nc { if cstatus[i]==want { c=c+1 } i=i+1 } return c }
50
51func td_render(fb: *i64, w: i64, h: i64, py: i64, cx: *i64, cstatus: *i64, nc: i64, tx: *i64, ty: i64, nt: i64, base: *i64) -> i64 {
52 gr_clear(fb, w, h, gr_pack(28, 36, 26))
53 gr_rect(fb, w, h, 0, py-8, w, py+8, gr_pack(120, 110, 86)) // the lane (dirt path)
54 gr_rect(fb, w, h, w-10, py-12, w, py+12, gr_pack(90, 200, 120)) // the base (green)
55 var t: i64=0
56 while t < nt { gr_rect(fb, w, h, tx[t]-5, ty-5, tx[t]+5, ty+9, gr_pack(70, 110, 210)); gr_disc(fb, w, h, tx[t], ty-7, 3, gr_pack(150,180,240)) t=t+1 } // towers (blue)
57 var i: i64=0
58 while i < nc { if cstatus[i]==0 { if cx[i]>=0 { gr_disc(fb, w, h, cx[i], py, 5, gr_pack(224, 80, 58)) } } i=i+1 } // creeps (red)
59 return 0
60}
61
62func main() -> i64 {
63 gw("=== nx_game_td_gate: GENRE Tower-Defence -- a real integer/no-float playable (live evidence) ===\n" as *u8)
64 var pass: i64 = 0; var total: i64 = 0
65 let W: i64=140; let H: i64=96; let NC: i64=4; let PY: i64=48; let TY: i64=PY-14; let RNG: i64=16; let DMG: i64=4
66 let cx: *i64=sys_mmap(8*8) as *i64; let chp: *i64=sys_mmap(8*8) as *i64; let cstatus: *i64=sys_mmap(8*8) as *i64
67 let tx: *i64=sys_mmap(8*8) as *i64; let base: *i64=sys_mmap(8*2) as *i64
68 tx[0]=42; tx[1]=82; tx[2]=118; let NT: i64=3
69
70 // ---- T1: towers defend -- the whole wave is cleared before the base ----
71 td_reset(cx, chp, cstatus, NC, base)
72 var tick: i64=0
73 while tick < 200 { if td_resolved(cstatus, NC)==1 { tick=200 } else { td_tick(cx, chp, cstatus, NC, tx, NT, RNG, DMG, W, base); tick=tick+1 } }
74 let killed: i64=td_count(cstatus, NC, 1); let leaked: i64=td_count(cstatus, NC, 2)
75 var t1ok: i64=1; if killed!=NC {t1ok=0} if leaked!=0 {t1ok=0} if base[0]!=10 {t1ok=0}
76 total=total+1; if t1ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
77 gw("T1 playthrough: towers killed " as *u8); gn(killed); gw("/" as *u8); gn(NC); gw(" creeps, leaked=" as *u8); gn(leaked); gw(", base_hp=" as *u8); gn(base[0]); gw(" (DEFENDED)\n" as *u8)
78
79 // ---- render a mid-wave frame for live evidence ----
80 let fb: *i64=sys_mmap(8*(W*H+8)) as *i64
81 td_reset(cx, chp, cstatus, NC, base)
82 var k: i64=0; while k < 24 { td_tick(cx, chp, cstatus, NC, tx, NT, RNG, DMG, W, base); k=k+1 }
83 td_render(fb, W, H, PY, cx, cstatus, NC, tx, TY, NT, base)
84 write_png(fb, W, H, "knowledge/nx_game_td.png" as *u8)
85 gw(" (live evidence: PNG knowledge/nx_game_td.png -- lane + 3 towers + creeps + base)\n" as *u8)
86
87 // ---- T2: neg-control -- no towers, the wave reaches the base ----
88 td_reset(cx, chp, cstatus, NC, base)
89 tick=0; while tick < 200 { if td_resolved(cstatus, NC)==1 { tick=200 } else { td_tick(cx, chp, cstatus, NC, tx, 0, RNG, DMG, W, base); tick=tick+1 } }
90 let leaked2: i64=td_count(cstatus, NC, 2)
91 total=total+1; if leaked2 > 0 { if base[0] < 10 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) }
92 gw("T2 neg-control: with NO towers " as *u8); gn(leaked2); gw(" creeps reached the base, base_hp=" as *u8); gn(base[0]); gw(" (towers are real)\n" as *u8)
93
94 // ---- T3: range real -- a creep outside all tower ranges takes no damage in one tick ----
95 td_reset(cx, chp, cstatus, NC, base)
96 cx[0]=4; chp[0]=6 // a creep at x=4, far from towers (42,82,118)
97 cx[1]=0-99; cx[2]=0-99; cx[3]=0-99 // others off-field
98 td_tick(cx, chp, cstatus, NC, tx, NT, RNG, DMG, W, base)
99 total=total+1; if chp[0]==6 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
100 gw("T3 range real: a creep at x=4 (outside every tower range) took no damage, hp=" as *u8); gn(chp[0]); gw("\n" as *u8)
101
102 // ---- T4: never-brick / determinism ----
103 td_reset(cx, chp, cstatus, NC, base)
104 tick=0; while tick < 200 { if td_resolved(cstatus, NC)==1 { tick=200 } else { td_tick(cx, chp, cstatus, NC, tx, NT, RNG, DMG, W, base); tick=tick+1 } }
105 let killed2: i64=td_count(cstatus, NC, 1)
106 total=total+1; if killed2==killed { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
107 gw("T4 never-brick (#26): deterministic (re-run killed=" as *u8); gn(killed2); gw("==" as *u8); gn(killed); gw("), bounded, no float\n" as *u8)
108
109 gw("\n=== nx_game_td_gate " as *u8); gn(pass); gw("/" as *u8); gn(total)
110 if pass == total { gw(" GREEN (Tower-Defence: a real playable wave-defence with range/damage/leak, rendered, sovereign)\n" as *u8); sys_exit(0); return 0 }
111 gw(" RED\n" as *u8); sys_exit(1); return 1
112}