nx_actor_role_game_logic_test.nx source
↩ module page · 128 lines · 5418 B
1// nx_actor_role_game_logic_test.nx -- smoke for nx_actor_role_game_logic.
2
3import "nx_syscalls.nx"
4import "nx_tier.nx"
5import "nx_actor.nx"
6import "nx_message.nx"
7import "nx_actor_role_game_logic.nx"
8
9func main() -> i64 {
10 let now: nx_size = 1000000
11
12 // 1: enum + difficulty validators
13 if nx_gl_v_is_valid(NX_GL_V_STEPPED) != 1 { return 1 }
14 if nx_gl_v_is_valid(NX_GL_V_COMPLETED) != 1 { return 2 }
15 if nx_gl_v_is_valid(NX_GL_V_NULL) != 1 { return 3 }
16 if nx_gl_v_is_valid(-1) != 0 { return 4 }
17 if nx_gl_v_is_valid(NX_GL_V_N) != 0 { return 5 }
18
19 if nx_gl_difficulty_is_valid(NX_TTT_AI_EASY) != 1 { return 6 }
20 if nx_gl_difficulty_is_valid(NX_TTT_AI_MEDIUM) != 1 { return 7 }
21 if nx_gl_difficulty_is_valid(NX_TTT_AI_PERFECT) != 1 { return 8 }
22 if nx_gl_difficulty_is_valid(99) != 0 { return 9 }
23 if nx_gl_difficulty_is_valid(-1) != 0 { return 10 }
24
25 // 2: invalid construction
26 if nx_gl_actor_new(99, NX_TTT_AI_EASY, NX_TTT_AI_EASY, 42) != (0 as *NxGameLogicCtx) { return 11 }
27 if nx_gl_actor_new(NX_TTT_X, 99, NX_TTT_AI_EASY, 42) != (0 as *NxGameLogicCtx) { return 12 }
28 if nx_gl_actor_new(NX_TTT_X, NX_TTT_AI_EASY, 99, 42) != (0 as *NxGameLogicCtx) { return 13 }
29
30 // 3: PERFECT vs PERFECT -- game theory says always DRAW
31 let ctx: *NxGameLogicCtx = nx_gl_actor_new(NX_TTT_X, NX_TTT_AI_PERFECT, NX_TTT_AI_PERFECT, 17)
32 if (ctx as i64) == 0 { return 14 }
33 if ctx.difficulty_x != NX_TTT_AI_PERFECT { return 15 }
34 if ctx.n_plies_emitted != 0 { return 16 }
35 if ctx.last_outcome != NX_TTT_ONGOING { return 17 }
36 if nx_gl_actor_is_done(ctx) != 0 { return 18 }
37
38 // 4: supporting scheduler + bus
39 let sched: *NxActorScheduler = nx_ac_sched_new(4, 1000000, 16, now)
40 nx_ac_spawn(sched, 3001, 5, 75, 0, now)
41 let bus: *NxMessageBus = nx_ms_bus_new(4, 8, 16)
42 nx_ms_register(bus, 3001)
43 nx_ms_register(bus, 3002)
44 nx_ms_subscribe(bus, 3002, NX_MS_KIND_GAME_ACTION)
45
46 // 5: drive game to completion -- each step plays one ply
47 var step_count: nx_int = 0
48 var tick: nx_size = now + 100
49 var verdict: nx_int = NX_GL_V_STEPPED
50 while verdict == NX_GL_V_STEPPED {
51 verdict = nx_gl_actor_step(ctx, sched, bus, 3001, tick)
52 tick = tick + 50
53 step_count = step_count + 1
54 if step_count > 12 { return 19 } // safety: tic-tac-toe is at most 9 plies
55 }
56 if verdict != NX_GL_V_COMPLETED { return 20 }
57 if nx_gl_actor_is_done(ctx) != 1 { return 21 }
58
59 // PERFECT vs PERFECT must be DRAW (game-theoretic invariant)
60 if nx_gl_actor_outcome(ctx) != NX_TTT_DRAW { return 22 }
61 // Tic-tac-toe full draw = 9 plies
62 if nx_gl_actor_plies(ctx) != 9 { return 23 }
63
64 // 6: scheduler marks COMPLETED
65 let a: *NxActor = nx_ac_find(sched, 3001)
66 if a.state != NX_AC_STATE_COMPLETED { return 24 }
67
68 // 7: listener received exactly 9 GAME_ACTION messages (one per ply)
69 if nx_ms_pending(bus, 3002) != 9 { return 25 }
70
71 // 8: drain + verify each message carries a valid (side, move) payload
72 var drained: nx_int = 0
73 var seen_x: nx_int = 0
74 var seen_o: nx_int = 0
75 while drained < 9 {
76 let m: *NxMessage = nx_ms_receive(bus, 3002)
77 if (m as i64) == 0 { return 30 + drained }
78 if m.kind != NX_MS_KIND_GAME_ACTION { return 40 + drained }
79 let payload: nx_int = m.payload_handle as nx_int
80 let side: nx_int = payload / 16
81 let move: nx_int = payload % 16
82 if move < 0 { return 50 + drained }
83 if move >= 9 { return 60 + drained }
84 if side == NX_TTT_X { seen_x = seen_x + 1 }
85 if side == NX_TTT_O { seen_o = seen_o + 1 }
86 drained = drained + 1
87 }
88 // X moves first then alternating; 9 plies = 5 X + 4 O
89 if seen_x != 5 { return 70 }
90 if seen_o != 4 { return 71 }
91
92 // 9: idempotent stepping past DONE
93 if nx_gl_actor_step(ctx, sched, bus, 3001, tick + 100) != NX_GL_V_COMPLETED { return 72 }
94 // No new message produced
95 if nx_ms_pending(bus, 3002) != 0 { return 73 }
96
97 // 10: PERFECT vs EASY -- PERFECT must NEVER LOSE (over 5 trials with
98 // varied seeds, no trial may show PERFECT losing)
99 var trial: nx_int = 0
100 while trial < 5 {
101 let ctx2: *NxGameLogicCtx = nx_gl_actor_new(NX_TTT_X, NX_TTT_AI_PERFECT, NX_TTT_AI_EASY, (trial as i64) * 13 + 1)
102 var v2: nx_int = NX_GL_V_STEPPED
103 var sc: nx_int = 0
104 while v2 == NX_GL_V_STEPPED {
105 v2 = nx_gl_actor_step(ctx2, (0 as i64) as *NxActorScheduler, (0 as i64) as *NxMessageBus, 0, now)
106 sc = sc + 1
107 if sc > 12 { return 80 }
108 }
109 let outcome: nx_int = nx_gl_actor_outcome(ctx2)
110 let winner: nx_int = nx_gl_actor_winner(ctx2)
111 // PERFECT plays X; if outcome is WIN then winner must be X (not O)
112 if outcome == NX_TTT_WIN {
113 if winner != NX_TTT_X { return 85 + trial }
114 }
115 trial = trial + 1
116 }
117
118 // 11: null guards
119 let null_ctx: *NxGameLogicCtx = (0 as i64) as *NxGameLogicCtx
120 if nx_gl_actor_step(null_ctx, sched, bus, 3001, now) != NX_GL_V_NULL { return 100 }
121 if nx_gl_actor_outcome(null_ctx) != NX_TTT_ONGOING { return 101 }
122 if nx_gl_actor_winner(null_ctx) != 0 { return 102 }
123 if nx_gl_actor_plies(null_ctx) != 0 { return 103 }
124 if nx_gl_actor_last_move(null_ctx) != -1 { return 104 }
125 if nx_gl_actor_is_done(null_ctx) != 0 { return 105 }
126
127 return 0
128}