code wiki / (root) / nx_actor_role_game_logic_test.nx

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}