From 7ef68374e8a58684b299b0f46f7ed850d5a87190 Mon Sep 17 00:00:00 2001 From: tslil Date: Sun, 6 Sep 2026 17:24:44 +0100 Subject: cleanup, match now plays pairs of symmetric games --- include/nn.c | 14 +++++++++++--- src/match.c | 60 ++++++++++++++++++++++++++++----------------------------- src/nn_train.py | 27 ++++++-------------------- 3 files changed, 46 insertions(+), 55 deletions(-) diff --git a/include/nn.c b/include/nn.c index 705f258..c1a806d 100644 --- a/include/nn.c +++ b/include/nn.c @@ -45,9 +45,17 @@ static float fast_tanh(const float x) { inline uint8_t cell_idx_at(const tak_state_p state, const uint8_t loc) { const uint8_t d = state->celldat[loc]; - if ((d & NUM_MASK) == 0) - return 0; // empty - return (uint8_t)(1u + 3u * (state->colours[loc] & 1u) + (d & STONE_MASK)); + switch (d & NUM_MASK) { + case 0: { + // index 0 + return 0; + } + default: { + // |{flat, standing, cap} * {black, white}| = 6 + // index 1 .. 6 + return (uint8_t)(1u + 3u * (state->colours[loc] & 1u) + (d & STONE_MASK)); + } + } } inline void nn_cell_idxs(uint8_t *out, tak_state_p state) { diff --git a/src/match.c b/src/match.c index 94afe48..6ae9fa7 100644 --- a/src/match.c +++ b/src/match.c @@ -57,10 +57,6 @@ static inline float six_cand(tak_state_p state) { static tak_state_p state; -// =================================================================== -// negamax progress hook (silent) -// =================================================================== - void negamax_display_progress(const uint8_t cur_depth, const uint8_t init_depth, const uint32_t length) { @@ -116,11 +112,34 @@ static int play_game(const nn_function current_fn, const nn_function cand_fn, } } -// =================================================================== -// Main -// =================================================================== +void do_game(const uint8_t size, const uint8_t random_opening_length, const enum COLOUR current_fn_colour) { + state = new_tak_state(size); + negamax_init(size); + + const nn_function current_fn = + (size == 5) ? &five_nn_fn : &six_nn_fn; + const nn_function cand_fn = (size == 5) ? &five_cand : &six_cand; + + const int winner = play_game(current_fn, cand_fn, current_fn_colour, random_opening_length); -const char *usage = + const char *name; + if (winner == -1) + name = "draw"; + else if (winner == (int)current_fn_colour) + name = "current"; + else + name = "candidate"; + printf("%s %u\n", name, state->ply); + + negamax_free(); + free_tak_state(state); +} + + // =================================================================== + // Main + // =================================================================== + + const char *usage = "match, one Tak game of the current weights against the candidate.\n\n\ usage: match \n\n\ This binary is linked with both the include/weight_* files and the include/cand_weight_* files. It plays a single x game, with a random side choice and both sides using random moves at the start of the game. The result format is:\n\n\ @@ -147,29 +166,8 @@ int main(int argc, char **argv) { 0x9E3779B97F4A7C15ULL + 0xC0FFEE; - state = new_tak_state(size); - negamax_init(size); - - const nn_function current_fn = - (size == 5) ? &five_nn_fn : &six_nn_fn; - const nn_function cand_fn = - (size == 5) ? &five_cand - : &six_cand; - - const enum COLOUR current_fn_colour = rand32() % 2 ? C_WHITE : C_BLACK; - const int winner = play_game(current_fn, cand_fn, current_fn_colour, random_opening_length); - - const char *name; - if (winner == -1) - name = "draw"; - else if (winner == (int)current_fn_colour) - name = "current"; - else - name = "candidate"; - printf("%s %u\n", name, state->ply); - - negamax_free(); - free_tak_state(state); + do_game(size, random_opening_length, C_BLACK); + do_game(size, random_opening_length, C_WHITE); return EXIT_SUCCESS; } diff --git a/src/nn_train.py b/src/nn_train.py index cde7a98..8f1a73e 100755 --- a/src/nn_train.py +++ b/src/nn_train.py @@ -201,8 +201,7 @@ class SelfPlayRecords: class Net(Module): def __init__(self, inp_dim: int, hidden: int) -> None: super().__init__() - # learned 7-way cell-state embedding (empty, {black, white} x - # {flat, standing, road}); the slot layout must match nn.c + # learned cell-state embedding, see nn.c self.embedding = Embedding(7, 1) self.hidden = Linear(inp_dim, hidden, bias=True) self.output = Linear(hidden, 1, bias=True) @@ -302,7 +301,7 @@ class Net(Module): _ = f.write(f"/* {p} */\n") _ = f.write(f'#include "weights_{size}.h"\n\n') _ = f.write( - f"const float {prefix}_embedding[7] =\n" + f"const float {prefix}_embedding[{len(E)}] =\n" f"{{\n{_fmt_flat(E, per_line=7)}\n}};\n\n" ) _ = f.write( @@ -547,24 +546,6 @@ def main() -> None: default=4, help="number of previous self-play instances to mix in with current dataset during training", ) - _ = p.add_argument( - "--eps-start", - type=float, - default=0.15, - help="initial self-play epsilon: in asymmetric self-play one side (chosen at random per game) plays a uniform random move with this probability while the greedy side has its plies recorded", - ) - _ = p.add_argument( - "--eps-min", - type=float, - default=0.01, - help="self-play epsilon floor", - ) - _ = p.add_argument( - "--eps-decay", - type=float, - default=0.9, - help="per-iteration epsilon decay", - ) _ = p.add_argument( "--td-lambda", type=float, @@ -639,6 +620,10 @@ def main() -> None: (root / "data").mkdir(exist_ok=True) champ = setup(size=args.size, root=root) + + num_params = sum(p.numel() for p in champ.parameters() if p.requires_grad) + print(f"Number of trainable parameters: {num_params:,}") + for iter in range(1, args.iters + 1): champ = run_step( batch=args.batch, -- cgit v1.3.1