aboutsummaryrefslogtreecommitdiff
path: root/src
AgeCommit message (Collapse)Author
2026-09-18working towards hybrid rootstraptslil
2026-09-13move data generation into the epoch looptslil
Previously we would spend all of the training epochs on making the network a better predictor of the *initial* TD-leaf values that we computed before the start of the first epoch. There's definitely something stale about this, given that the TD-leaf training targets involved the net evaluation at PVs. I'm not overly confident that this makes sense, but as a kind of compromise between fully on-line TD-leaf and the fully stale version we had before, we now recompute the TD-leaf values in each epoch with the fine-tuned net instead of the epoch 0 net. Perhaps we were just lucky, but this seems to train a strong network under the default setting. More experimentation is required.
2026-09-12still observing non-monotonic improvements between training generationstslil
2026-09-12flood fill for roads, fix dragon, memoise leaf eval, enable diagonal in ↵tslil
nn_train.py
2026-09-11move from single candidate td-leaf with gating to tournament of a populationtslil
The goal in this change was to overcome the rock-paper-scissors phenomenon that was happening in capability space by having on a single candidate learn to beat the most recent version of itself. In some sense this lead to rotating around in capability space instead of chasing monotonic improvements. I hope that flipping to a system in which many (N=5) candidates play each other and learn from their own set of diverse games in this pool, combined with --replay-iters, will lead to a given network having to have a much more comprehensive understanding of the space of tactics. I haven't yet had time to train this thorougly, will run those experiments on my desktop computer.
2026-09-10credit Mitchell Riley: we can drop 65 parameters for about the same ↵tslil
strength; also train for longer The idea is that the current model is M_theta(board, ply) = "score for player 0", but we can instead create a different model M'_theta(board) = "score for player 0 assuming that it's player 0's move" and for a certain pair of functions sigma, normalise, recover M_theta(board, ply) = sigma(ply) M'_theta(normalise(board, ply)). This pair of functions is sigma(ply) = +/- 1 (parity), and normalise being a conditional interchange of pieces on the board depending on the parity of the ply. The only things stopping this from being completely straightforward is that nn_train.py evaluates M'_theta itself so we need to pass sigma(ply) to the training loop. 6x6 is still untrained.
2026-09-10use silu instead of relu with the intuition being that we avoid dead neuronstslil
In a head-to-head this is +187 elo against the previous weights, although it trained more.
2026-09-07cleanup, match now plays pairs of symmetric gamestslil
2026-09-05prevent overflow of the uint8_t ply counter during selfplay or matchtslil
2026-09-05training rework, best weights so far*tslil
After quite a bit of experimentation it would appear that a far better way to train on self-play data in this setup is to randomise not the moves but the opening. The new selfplay and match binaries have lost the epsilon parameter and now play a random opening of some length before switching to pure, deterministic engine inference. This also resulted in a cleanup of nn_train.py. As an added bonus, in this configuration it makes sense to harvest training data from both sides in self-play and consequently the two sides are given distinct game ids (this was almost surely an oversight in the original version of TD(lambda) that i had). I also noticed that rand32 was not using the top 32bits of xorshitf*. *Under default settings nn_train.py produces a 5x5 weight set that doesn't lose a single game to any other cttei that i cared to look at, when measured using `racetrack` (which has a diverse balanced opening book).
2026-09-04TD-leaf and another negamax bugtslil
TD-lambda and TD-leaf are separated by the observation that minimax with a parameterised leaf heuristic is differentiable* in the parameters. "All" that had to be done was to extract the "principal variation": which one position survived all of the pruning to become the reported value. This was a bit fiddly and uncovered a subtle bug (again) in negamax.c: we were not being complete in our overriding of the TT entry and by omitting key it was possible that later searches would find incorrect values. The more interesting question to me was: how do we introduce enough variety while not corrupting the PV chain? I settled on having selfplay have one of the players be epsilon-random but the other greedy (minimax) and simply report only the greedy portion PV chain. This appears to work well enough in practice and doesn't introduce nonsense training data for the net (we don't force its actions to be nonsensical). I also deciced, perhaps in a departure from tradition, that we had good supervised labels for PVs that happened to be winning states, so the training overrides the targets in that case. Finally rolled back some of the ambition in nn_train.py default arguments. *almost always
2026-09-02default engines to depth 4, add a rolling window to nn_train.pytslil
I've noticed high variability in training efficacy, part of that i think is due to the fact that the dataset per iteration is not really that large. Sure, it has many rows, but it's fundamentally given by the same engine playing itself with some entropy injection. To remedy this i've expanded the input data for any given iteration to contain --replay-iters many previous datasets as input too. Alongside that decreased the epsilon decay rate for slower "cooling", and included much more of the early game in the training set (min ply 8 -> 4).
2026-09-02TD(lambda), and a big reworktslil
NOTE: 6x6 weights are as-yet untrained On the performance side: * switch from linked-list chaining hash table to direct mapping * cache check_win by hash * persist transposition tables across plies (this was a big deal and difficult to puzzle out) * add align directives to the network floats * prepare_nn_input gained a small LUT to avoid branching (i suppose you might call this 1D embedding) * apply mtune=native for the native builds On the correctness side: * Zobrist has now includes the ply parity * the network now predicts something in [-1, 1] using tanh (approximated by polynomials) * the training object is now MSE because we're learning a value estimate * use .clangd instead of compile_commands.json On the training side: * completely forgo human-derived data set * implement td(lambda), self-play, tournaments
2023-01-21might as well enable size 6tslil clingman
Same network architecture, same training principle. Predictably this is too slow. Also statically allocate state in driver programmes.
2023-01-16switch to explicit game state & important bug fix & clang formattslil clingman
Previously the code base assumed that there was a single, global game state which was the implicit target of all actions taken. Looking ahead at architectural improvements, this has now been (almost entirely) made explicit and functions take tak_state_p where necessary (and also where unnecessary). Two important fixes to actions.c were made: - Previously when generating the possible stack moves, stack height overflows (> 15) were not taken into account and this resulted in the tree search corrupting the board state. Now action search does not list all legal actions, rather the subset of these encodeable by the implementation. - The check for crushing on a stack move was incorrect (too strict), and this resulted in many legitimate moves being igonored. Finally, in other changes, weights have also been improved by training all games instead of some subset for chosen players, and clang-format was run on the codebase.
2023-01-15new neural network arch (faster + better) & minor changes + fixestslil clingman
Gone is the convolutional neural network, for it turns out not only is it more difficult to train, but all of the extra information about board layers didn't make much of a difference at this size. So cnn1986 has been replaced by nn1986, a standard, two-layer, dense nn configured as a binary classifier and (mis)used in that capacity. Note: total number of parameters is unchanged. HARK: this new nn exposes a bug somewhere in ctak. Run ctlm with self-play to see the completely borked board state at the end.
2021-10-06Went for more standard unicode symbolstslil clingman
2021-10-06Corrected win screen in geminicttslil clingman
2021-10-06Enforce quoted ptn string for geminicttslil clingman
2021-10-05Welcome geminict!tslil clingman
This is a special interface to negamax_cnn1986 which is designed to generate output for use in a CGI tak interface to be used over gemini. Also in this commit is a reformating of the various source files to use the traditional tab width of 8 spaces.
2021-03-17Corrected generation of training data for 6stslil clingman
2021-02-11Don't generate header for training data + tweakstslil
For some reason it would seem that moving flats to a lower value and increasing the proximity between caps and top flats improves acquisition. Still not great, but every bit counts.
2021-02-10Change the training data generation a littletslil clingman
Although it pains me to say it, ``label smoothing'' appears to be actually work. I'm also currently experimenting with training simply against _all_ games, instead of only bot matches. Once the training finishes i'll pit cttei against itself with old and new weights, hopefully there'll be a noticeable improvement.
2021-02-05Rename ct_k -> ct, IANAL but ...tslil
2021-02-05Small typo in generated output for weightstslil clingman
2021-02-05Correct line clearing behaviour, EXIT_FAILURE <~ -1tslil clingman
2021-02-05Renamed binariestslil clingman
2021-02-05TEI interface working!tslil clingman
2021-02-01Tried some naive iterative deepening. Work on TEI interface nexttslil clingman
If TEI is implemented, then i could make use of Morten's racetrack (https://github.com/MortenLohne/racetrack) and develop a quantitative measure of the bot's performance. This is the current priority.
2021-01-31Just some #weightgoals ;)tslil clingman
It turns out that while i was training on a 0/1 classification problem, i was using 2*eval - 1. Training using this function instead, and on bot-dominated game choices (chosen_player in extract.sh) seems to have given a better evaluation function. At the least, Morten's swindle doesn't work anymore.
2021-01-31Fairly important bug fixes to lcdlib, LCD now echoes input!tslil clingman
Input polling without line-buffering is done using ncurses, so the buildroot configuration had to change accordingly to include that library. The Makefile changed to accommodate stand-alone building of ct1986 and to include -lcurses where appropriate. There were also some typos about copying ct1986 and ctaklm to the correct directories.
2021-01-29Purged uninteresting statisticstslil
2021-01-29Syntax errors, small tweak to training data generationtslil clingman
2021-01-28Added license information!tslil clingman
2021-01-28Print progress before recursion rather than aftertslil clingman
2021-01-26Small oversighttslil clingman
2021-01-26Small changes to build ct1986tslil clingman
2021-01-26Stable negamax-alpha-beta fail-softtslil clingman
2021-01-26Actually it seems before i was mis-countingtslil clingman
2021-01-26There is still a bug, it doesn't appear to be checking enoughtslil clingman
2021-01-25Stripping debug stufftslil clingman
2021-01-25Still bugs...tslil clingman
2021-01-25Still some bugs, standing stone becomes flat at depth4 self-play??tslil clingman
2021-01-25Incidental bugfixtslil clingman
2021-01-25Memory leak fixtslil clingman
2021-01-24This is the basic idea, there's ≥ 1 bug (generates illegals...)tslil clingman
2021-01-24Working on action lists to refactortslil clingman
2021-01-24Still tryingtslil clingman
|
2021-01-23Hash collisionstslil clingman
2021-01-23Fixed some important bugstslil clingman