code wiki / _hdl_build / nx_game_pong_gate.nx
nx_game_pong_gate.nx source
↩ module page · 95 lines · 6150 B
1import "nx_gate_gn.nx"
2import "nx_gate_base.nx"
3// nx_game_pong_gate.nx -- GENRE: Sport (catalog genre, was ABSENT). A real integer/no-float Pong CORE --
4// two paddles, a ball with bounce physics + paddle return + scoring. Scripted match (a tracking paddle beats
5// a frozen one), rendered frame, neg-control (paddle control matters) + return-physics-real.
6// T1 MATCH: the player's tracking paddle returns every ball and scores to the win target. PNG.
7// T2 NEG-CONTROL: a frozen player paddle (no tracking) does NOT reach the win target (control is real).
8// T3 PHYSICS REAL: a ball at a paddle is RETURNED only when the paddle is aligned (else it passes).
9// T4 NEVER-BRICK (#26): deterministic, bounded, no float.
10// expect_exit: 0 license_tier: ORIGINAL
11import "nx_game_raster.nx"
12import "nx_png.nx"
13import "nx_syscalls.nx"
14
15func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
16" as *u8); return ok }
17func p_abs(v: i64) -> i64 { if v<0 { return 0-v } return v }
18func p_clamp(v: i64, lo: i64, hi: i64) -> i64 { if v<lo {return lo} if v>hi {return hi} return v }
19
20// s[0]=bx s[1]=by s[2]=bvx s[3]=bvy s[4]=lpy s[5]=rpy s[6]=lscore s[7]=rscore
21func pong_reset(s: *i64, dir: i64) -> i64 { s[0]=70; s[1]=48; s[2]=dir*2; s[3]=1; return 0 }
22func pong_full_reset(s: *i64) -> i64 { pong_reset(s, 1); s[4]=12; s[5]=12; s[6]=0; s[7]=0; return 0 }
23func pong_tick(s: *i64, w: i64, h: i64, padh: i64, ltrack: i64, rtrack: i64) -> i64 {
24 let lx: i64=6; let rx: i64=w-6
25 if ltrack==1 { s[4]=p_clamp(s[1], padh, h-padh) }
26 if rtrack==1 { s[5]=p_clamp(s[1], padh, h-padh) }
27 s[0]=s[0]+s[2]; s[1]=s[1]+s[3]
28 if s[1]<=2 { s[1]=2; s[3]=0-s[3] }
29 if s[1]>=h-2 { s[1]=h-2; s[3]=0-s[3] }
30 if s[2]<0 { if s[0]<=lx+2 { if p_abs(s[1]-s[4])<padh { s[2]=0-s[2]; s[0]=lx+2 } } }
31 if s[2]>0 { if s[0]>=rx-2 { if p_abs(s[1]-s[5])<padh { s[2]=0-s[2]; s[0]=rx-2 } } }
32 if s[0]<0 { s[7]=s[7]+1; pong_reset(s, 1) }
33 if s[0]>w { s[6]=s[6]+1; pong_reset(s, 0-1) }
34 return 0
35}
36func pong_render(fb: *i64, w: i64, h: i64, padh: i64, s: *i64) -> i64 {
37 gr_clear(fb, w, h, gr_pack(12, 16, 22))
38 var y: i64=4; while y<h { gr_rect(fb, w, h, w/2-1, y, w/2+1, y+6, gr_pack(60,70,84)); y=y+12 } // center line
39 gr_rect(fb, w, h, 4, s[4]-padh, 8, s[4]+padh, gr_pack(120,220,150)) // left paddle (player, green)
40 gr_rect(fb, w, h, w-8, s[5]-padh, w-4, s[5]+padh, gr_pack(220,220,120)) // right paddle (AI, yellow)
41 gr_disc(fb, w, h, s[0], s[1], 2, gr_pack(240,240,240)) // ball
42 gr_rect(fb, w, h, w/2-2-s[6]*4, 4, w/2-2, 7, gr_pack(120,220,150)) // left score bar
43 return 0
44}
45
46func main() -> i64 {
47 gw("=== nx_game_pong_gate: GENRE Sport (Pong) -- a real integer/no-float playable (live evidence) ===\n" as *u8)
48 var pass: i64 = 0; var total: i64 = 0
49 let W: i64=140; let H: i64=96; let PADH: i64=8; let WIN: i64=3
50 let s: *i64=sys_mmap(8*8) as *i64
51
52 // ---- T1: tracking player paddle vs a frozen opponent -> the player reaches the win score ----
53 pong_full_reset(s)
54 var tick: i64=0
55 while tick < 1200 { if s[6]>=WIN { tick=1200 } else { pong_tick(s, W, H, PADH, 1, 0); tick=tick+1 } }
56 let lwon: i64=s[6]
57 total=total+1; if lwon>=WIN { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
58 gw("T1 match: the tracking paddle scored " as *u8); gn(lwon); gw(" (>= " as *u8); gn(WIN); gw(" = WIN)\n" as *u8)
59
60 // ---- render a mid-rally frame ----
61 let fb: *i64=sys_mmap(8*(W*H+8)) as *i64
62 pong_full_reset(s)
63 var k: i64=0; while k < 20 { pong_tick(s, W, H, PADH, 1, 0); k=k+1 }
64 pong_render(fb, W, H, PADH, s)
65 write_png(fb, W, H, "knowledge/nx_game_pong.png" as *u8)
66 gw(" (live evidence: PNG knowledge/nx_game_pong.png -- paddles + ball + score)\n" as *u8)
67
68 // ---- T2: neg-control -- a frozen player paddle never reaches the win score ----
69 pong_full_reset(s)
70 tick=0; while tick < 1200 { if s[6]>=WIN { tick=1200 } else { pong_tick(s, W, H, PADH, 0, 1); tick=tick+1 } }
71 let lfrozen: i64=s[6]
72 total=total+1; if lfrozen < WIN { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
73 gw("T2 neg-control: a frozen player paddle scored " as *u8); gn(lfrozen); gw(" (< " as *u8); gn(WIN); gw(", opponent=" as *u8); gn(s[7]); gw("; paddle control is real)\n" as *u8)
74
75 // ---- T3: physics real -- a ball is returned only when the paddle is aligned ----
76 pong_full_reset(s); s[0]=8; s[2]=0-2; s[1]=48; s[4]=48 // ball at left paddle, paddle aligned
77 pong_tick(s, W, H, PADH, 0, 0)
78 let aligned_vx: i64=s[2] // should be returned (>0)
79 pong_full_reset(s); s[0]=8; s[2]=0-2; s[1]=48; s[4]=10 // paddle far from ball
80 pong_tick(s, W, H, PADH, 0, 0)
81 let missed_vx: i64=s[2] // should NOT be returned (<0)
82 var t3ok: i64=1; if aligned_vx <= 0 {t3ok=0} if missed_vx >= 0 {t3ok=0}
83 total=total+1; if t3ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
84 gw("T3 physics real: aligned paddle returns the ball (vx=" as *u8); gn(aligned_vx); gw("), misaligned lets it pass (vx=" as *u8); gn(missed_vx); gw(")\n" as *u8)
85
86 // ---- T4: never-brick / determinism ----
87 pong_full_reset(s); tick=0; while tick < 1200 { if s[6]>=WIN { tick=1200 } else { pong_tick(s, W, H, PADH, 1, 0); tick=tick+1 } }
88 let lwon2: i64=s[6]
89 total=total+1; if lwon2==lwon { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
90 gw("T4 never-brick (#26): deterministic re-run (score=" as *u8); gn(lwon2); gw("==" as *u8); gn(lwon); gw("), bounded, no float\n" as *u8)
91
92 gw("\n=== nx_game_pong_gate " as *u8); gn(pass); gw("/" as *u8); gn(total)
93 if pass == total { gw(" GREEN (Sport/Pong: a real playable with ball physics, paddle return, scoring, rendered, sovereign)\n" as *u8); sys_exit(0); return 0 }
94 gw(" RED\n" as *u8); sys_exit(1); return 1
95}