aboutsummaryrefslogtreecommitdiff
path: root/CLAUDE.md
diff options
context:
space:
mode:
Diffstat (limited to 'CLAUDE.md')
-rw-r--r--CLAUDE.md4
1 files changed, 2 insertions, 2 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index 35305bc..e9b236b 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -39,9 +39,9 @@ The toolchain comes from a Nix flake (`flake.nix`, loaded via direnv `.envrc` â†
- `src/local-player.rs` (module `local_player`, pulled in via `#[path]` because of the hyphen) — `LocalPlayer`, a human using the mouse: click a point to place a sheep, or click a piece then one of its highlighted destinations.
- `src/claude-player.rs` — `ClaudePlayer`, the minimax computer player: `time_limit` and `max_depth` are public fields, with a 2^20-slot `TranspositionTable` and killer moves.
- `src/bench.rs` — the `bench` mode (see above).
-- `src/network.rs` — `Network`, a small MLP (52 inputs → 64 ReLU → 32 ReLU → tanh, positive favours tigers) with hand-written backprop and an `Adam` optimiser, all in one flat `params` Vec at the `W1`/`B1`/… offsets. Inputs are only the raw position: a bit per point for tigers and for sheep, sheep in hand / 20, and whether tigers are to move. `forward` adds up the layer-1 weight rows of the set bits rather than multiplying through the zeros. Also `Rng` (xorshift64*) and `Sample` (board, turn, target). File format (`save`/`load`): `TSNN`, then version, inputs, hidden1, hidden2 as little-endian u32s, then every param as a little-endian f32; changing the layer sizes makes old files fail to load.
+- `src/network.rs` — `Network`, a small MLP (52 inputs → 64 ReLU → 32 ReLU → tanh, positive favours tigers) with hand-written backprop and an `Adam` optimiser, all in one flat `params` Vec at the `W1`/`B1`/… offsets. Inputs are only the raw position: a bit per point for tigers and for sheep, sheep in hand / 20, and whether tigers are to move. `forward` adds up the layer-1 weight rows of the set bits rather than multiplying through the zeros. `gradient` (used by `train_batch`) and `loss` run on rayon's pool in fixed-size chunks (`GRAD_CHUNK` = 32, `LOSS_CHUNK` = 1024) summed in order, so the result doesn't depend on the number of threads (`training_is_the_same_on_any_number_of_threads`); Adam's step stays serial. Also `Rng` (xorshift64*) and `Sample` (board, turn, target). File format (`save`/`load`): `TSNN`, then version, inputs, hidden1, hidden2 as little-endian u32s, then every param as a little-endian f32; changing the layer sizes makes old files fail to load.
- `src/neural-player.rs` — `NeuralPlayer`, a `MinimaxPlayer` like `ClaudePlayer` whose `evaluate` is a `Network`.
-- `src/train.rs` — self-play training. Each generation plays `games_per_generation` games of the net against itself at a fixed depth (default 3) across all cores. Moves are random for the first `random_plies`, and with chance `epsilon` after that. A game is a draw on a threefold repetition (`play` keeps the earlier boards and passes them to `choose`, so the search sees them too), or on reaching `ply_cap`, a backstop for long games that never repeat. Each position is labelled with a TD(λ) target worked backwards from the result (±1, or 0) through the searched scores. A position where a random move was played is labelled with its own search score instead, which cuts the chain there. Without that cut, the sheep's random moves (which usually hang a sheep) taught the net that tigers nearly always win. Self-play still ends mostly in tiger wins, because of those random moves, so judge progress by the match against `ClaudePlayer` (`match_games` = 100). The net then trains on the last `buffer_generations` generations, each position shown in a random one of the board's 8 symmetries (`GameBoard::transformed`). With `balance_outcomes` (the default), `balanced` builds each epoch from as many positions of sheep-win games as of tiger-win games: their mean, so the epoch stays the same size. Tiger-win positions are subsampled and sheep-win ones repeated; draws are kept as they are. The output shows the mean error on tiger-win (T) and sheep-win (S) positions separately, before and after training. After that it plays matches against the previous generation and against `ClaudePlayer`, which is only a yardstick and never a training signal. Each generation is saved as `out_dir/gen-NNN.bin` and `latest.bin`, and a rerun carries on from the newest `gen-NNN.bin`. `Config::from` (`--from`) starts from any weights file instead. Generation numbers still follow on from the out dir. The Adam state and the sample buffer are not saved, so they start empty either way.
+- `src/train.rs` — self-play training. Each generation plays `games_per_generation` games of the net against itself at a fixed depth (default 3) across all cores (`parallel`, on rayon's pool, which the training steps share; `RAYON_NUM_THREADS` limits it). Moves are random for the first `random_plies`, and with chance `epsilon` after that. A game is a draw on a threefold repetition (`play` keeps the earlier boards and passes them to `choose`, so the search sees them too), or on reaching `ply_cap`, a backstop for long games that never repeat. Each position is labelled with a TD(λ) target worked backwards from the result (±1, or 0) through the searched scores. A position where a random move was played is labelled with its own search score instead, which cuts the chain there. Without that cut, the sheep's random moves (which usually hang a sheep) taught the net that tigers nearly always win. Self-play still ends mostly in tiger wins, because of those random moves, so judge progress by the match against `ClaudePlayer` (`match_games` = 100). The net then trains on the last `buffer_generations` generations, each position shown in a random one of the board's 8 symmetries (`GameBoard::transformed`). With `balance_outcomes` (the default), `balanced` builds each epoch from as many positions of sheep-win games as of tiger-win games: their mean, so the epoch stays the same size. Tiger-win positions are subsampled and sheep-win ones repeated; draws are kept as they are. The output shows the mean error on tiger-win (T) and sheep-win (S) positions separately, before and after training, and how long the training part took (`train s`). On generations whose number is a multiple of `match_every` (default 10, `--match-every`), it then plays matches against the generation that last played them (N - `match_every`, loaded from its `gen-NNN.bin`, or the network the run started from if that file isn't there) and against `ClaudePlayer`, which is only a yardstick and never a training signal; other generations show `-` in those columns. Each generation is saved as `out_dir/gen-NNN.bin` and `latest.bin`, and a rerun carries on from the newest `gen-NNN.bin`. `Config::from` (`--from`) starts from any weights file instead. Generation numbers still follow on from the out dir. The Adam state and the sample buffer are not saved, so they start empty either way.
- `src/main.rs` — the command line, parsed with clap's derive API (`Cli`, `Command`, `PlayArgs`, `TrainArgs`). With no command, or `play`, it plays a game in the TUI and prints the result; `--sheep`/`--tiger` take `human`, `claude` or a weights file (`parse_player` → `PlayerKind`, turned into a `Box<dyn Player>` by `make_player`), and `--time` is each computer player's time limit in seconds. `neural <weights> [sheep|tiger]` is shorthand for you against a `NeuralPlayer`. `bench [depth]` runs the benchmark. `train [generations] [games] [out dir]` trains a network (use `--release`); every other `train::Config` field is a `--long-option` whose default is read from `Config::default()`, so keep `TrainArgs` and its `From` impl in step with `Config`.
Notation (`pos_name`, `impl Display for Move` in board.rs): files `a`–`e` are x 0–4, ranks are `5 - y` (y = 0 is drawn at the top as rank 5). Moves are `sc3` (place), `sc3c2` (sheep move), `ta5b4` (tiger step), `ta5xc3` (tiger jump).