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}