nx_tictactoe.nx
buildroot/runtime/nx_tictactoe.nx
about
nx_tictactoe.nx -- bits-up source-of-truth for C0 of the bootstrap-to-generative
trajectory (NISHI_GAME_ENGINE_HONESTY_AND_MISSIONS_ROADMAP.md §19.1 stage C0).
The smallest joy-bearing game on the ladder. Validates the five honesty gates
(G1 smokes + G2 ≥60s real playtest + G3 benchmark + G4 bits-budget + G5 joy)
at minimum complexity before scaling to checkers/chess/voxels/minecraftclone.
PURE NishiLang. No third-party in the build path. No TypeScript scaffold.
Browser delivery (if needed for G2 family playtest) generated from this source
via nxc2 codegen on the last step IF the family needs to playtest in a browser
tab. Per [[feedback-no-third-party-in-build-path-source-of-truth-is-bits-up]].
State layout: a heap block of 12 i64 cells holds one game state. Pure data,
no globals, no GC required — all allocations happen at game start; minimax
mutates and undoes in place to avoid recursive allocation.
[0..8] 9 board cells: NX_TTT_EMPTY=0, NX_TTT_X=1, NX_TTT_O=2
[9] whose turn: NX_TTT_X or NX_TTT_O
[10] outcome kind: NX_TTT_ONGOING=0, NX_TTT_WIN=1, NX_TTT_DRAW=2
[11] winning side: NX_TTT_X, NX_TTT_O, or 0 if no win yet
genealogy_id: nx_tictactoe_v1_2026_05_19
lineage_id: abstract_classic_game_state_machine
license: operator-as-sole-author (escalated to §23.1 of roadmap)
complexity: O(1) per move; O(8.6) for perfect minimax (tic-tac-toe game tree)
dependencies 3 imports · 7 importers
imports: nx_syscalls.nxnx_tier.nxnx_prng.nx
imported by: _ttt_diag.nx_ttt_test_trace.nxnx_actor_role_game_logic.nxnx_tictactoe_render.nxnx_tictactoe_test.nxnx_ttt_bits_up_e2e.nxnx_user_sim_games.nx
structs
| none |
consts
| 36 | const NX_MAGIC_1099511628211: i64 = 1099511628211 |
| 40 | const NX_TTT_OFF_CELL_0: nx_int = 0 |
| 41 | const NX_TTT_OFF_TURN: nx_int = 9 |
| 42 | const NX_TTT_OFF_OUTCOME: nx_int = 10 |
| 43 | const NX_TTT_OFF_WINNER: nx_int = 11 |
| 44 | const NX_TTT_GAME_CELLS: nx_int = 12 // hash domain ends here |
| 48 | const NX_TTT_OFF_PLAYER_SIDE: nx_int = 12 // NX_TTT_X or NX_TTT_O |
| 49 | const NX_TTT_OFF_OPPONENT: nx_int = 13 // -1 = human-vs-human; else NX_TTT_AI_* |
| 50 | const NX_TTT_OFF_RESERVED: nx_int = 14 |
| 51 | const NX_TTT_STATE_CELLS: nx_int = 15 |
| 55 | const NX_TTT_EMPTY: nx_int = 0 |
| 56 | const NX_TTT_X: nx_int = 1 |
| 57 | const NX_TTT_O: nx_int = 2 |
| 61 | const NX_TTT_ONGOING: nx_int = 0 |
| 62 | const NX_TTT_WIN: nx_int = 1 |
| 63 | const NX_TTT_DRAW: nx_int = 2 |
| 67 | const NX_TTT_AI_EASY: nx_int = 0 // random legal move |
| 68 | const NX_TTT_AI_MEDIUM: nx_int = 1 // one-ply look-ahead (win / block / center / random) |
| 69 | const NX_TTT_AI_PERFECT: nx_int = 2 // minimax with alpha-beta (never loses) |
functions
| 73 | func nx_ttt_new(first_mover: nx_int) -> *i64 |
| 94 | func nx_ttt_cell(s: *i64, idx: nx_int) -> nx_int |
| 100 | func nx_ttt_turn(s: *i64) -> nx_int |
| 104 | func nx_ttt_outcome(s: *i64) -> nx_int |
| 108 | func nx_ttt_winner(s: *i64) -> nx_int |
| 112 | func nx_ttt_other(side: nx_int) -> nx_int |
| 119 | func nx_ttt_is_legal(s: *i64, idx: nx_int) -> nx_int |
| 128 | func nx_ttt_legal_count(s: *i64) -> nx_int |
| 143 | func nx_ttt_evaluate(s: *i64) |
| 253 | func nx_ttt_try_apply(s: *i64, idx: nx_int) -> nx_int |
| 265 | func nx_ttt_undo(s: *i64, idx: nx_int, prev_outcome: nx_int, prev_winner: nx_int) |
| 279 | func nx_ttt_state_hash(s: *i64) -> i64 |
| 296 | func nx_ttt_pick_easy(s: *i64, prng_state: *i64) -> nx_int |
| 314 | func nx_ttt_pick_medium(s: *i64, side: nx_int) -> nx_int |
| 372 | func nx_ttt_minimax(s: *i64, maximizing_side: nx_int, depth: nx_int, |
| 427 | func nx_ttt_pick_perfect(s: *i64, side: nx_int) -> nx_int |
| 451 | func nx_ttt_pick(s: *i64, side: nx_int, difficulty: nx_int, prng_state: *i64) -> nx_int |