Skip to content

Commit

Permalink
feat: major improvements to search logic, several bug fixes, and mult…
Browse files Browse the repository at this point in the history
…iple new search features. (#99)

resolves #95 
resolves #96 
resolves #90 

I'll start off by stating that I definitely should not have let this PR
get so large. I originally planned to fix a few bugs and make another
release, but then kept getting sidetracked with small, easy-to-implement
features like razoring and iir. I intend to have much smaller PRs to
`main` moving forward.

Anyway, here's a summary of what's new:
- Razoring
- Internal Iterative Reductions
- Internal Iterative Deepening
- This may be removed in the future, as I have been told that IIR is
much more potent and tends to cover IID sufficiently.
- Late move pruning
- Mate distance pruning
  - Alternate mate score adjustments, courtesy of @87flowers
- Proper PV tracking
- Search info now also displays `seldepth`
- Project restructure to support other crates in the workspace
- Intent is to build the texel tuner in with the engine, as a separate
crate.
- Search parameters are now contained in their own struct, initialized
once at engine startup.
- Will eventually set this up so that each parameter has a UCI option
for tweaking with SPSA
- TT storage and cutoffs in qsearch
- New `take` and `place` commands for manually modifying the board
- Improvements to detecting if the search should be cancelled
- Support for fractional depth during search

And some bug fixes:
- QSearch now uses a fail-soft framework
- No more crashes at low TC if the search did not have enough time to
find a bestmove
- TT entries store an `Option<Move>` instead of `Move`
- `Move` is now a `NonZeroU16`, so a nullmove is `Option::None`
- Fullmove counter is no longer a `u8` due to overflow issues

---
SPRT against `main`:
```
Elo   | 167.36 +- 11.75 (95%)
Conf  | 8.0+0.08s Threads=1 Hash=16MB
Games | N: 2500 W: 1421 L: 302 D: 777
Penta | [19, 74, 312, 459, 386]
```https://pyronomy.pythonanywhere.com/test/786/

And against `Stash v21.2` (~2714 CCRL Blitz):
```
Elo   | 9.88 +- 10.36 (95%)
Conf  | 8.0+0.08s Threads=1 Hash=16MB
Games | N: 3060 W: 1283 L: 1196 D: 581
Penta | [199, 226, 608, 283, 214]
```https://pyronomy.pythonanywhere.com/test/784/

This puts Toad at around ~2700.

---------

Co-authored-by: lily <[email protected]>
Co-authored-by: 87flowers <[email protected]>
  • Loading branch information
3 people authored Jan 2, 2025
1 parent 2309360 commit 6eb100f
Show file tree
Hide file tree
Showing 36 changed files with 2,800 additions and 1,630 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/target

pgo.sh

toad-*
37 changes: 29 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 9 additions & 17 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
[package]
name = "toad"
version = "2.0.0"
edition = "2021"
authors = ["Danny Hammer <[email protected]>"]
license = "MPL-2.0"
description = "A toy chess engine"
repository = "https://github.com/dannyhammer/toad"
homepage = "https://github.com/dannyhammer/toad"
keywords = ["chess", "chess-engine", "uci"]
[workspace]
resolver = "2"
members = ["toad"]

[dependencies]
anyhow = "1.0.89"
arrayvec = "0.7.6"
clap = { version = "4.5.18", features = ["derive", "string"] }
#uci-parser = { path = "../uci-parser", features = ["parse-go-perft", "parse-position-kiwipete", "clamp-negatives", "err-on-unused-input"] }
uci-parser = { git = "https://github.com/dannyhammer/uci-parser.git", features = ["parse-go-perft", "parse-position-kiwipete", "clamp-negatives", "err-on-unused-input"] }
#uci-parser = { version = "0.2.0", features = ["parse-go-perft", "parse-position-kiwipete", "clamp-negatives", "err-on-unused-input"] }
[profile.release]
panic = 'abort'
strip = true
lto = true
codegen-units = 1
overflow-checks = true
10 changes: 5 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
# If on Windows, add the .exe extension to the executable and use PowerShell instead of `sed`
ifeq ($(OS),Windows_NT)
EXT := .exe
NAME := $(shell powershell -Command "(Get-Content Cargo.toml | Select-String '^name =').Line -replace '.*= ', '' -replace '\"', ''")
VERSION := $(shell powershell -Command "(Get-Content Cargo.toml | Select-String '^version =').Line -replace '.*= ', '' -replace '\"', ''")
NAME := $(shell powershell -Command "(Get-Content toad/Cargo.toml | Select-String '^name =').Line -replace '.*= ', '' -replace '\"', ''")
VERSION := $(shell powershell -Command "(Get-Content toad/Cargo.toml | Select-String '^version =').Line -replace '.*= ', '' -replace '\"', ''")
else
EXT :=
NAME := $(shell sed -n 's/^name = "\(.*\)"/\1/p' Cargo.toml)
VERSION := $(shell sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml)
NAME := $(shell sed -n 's/^name = "\(.*\)"/\1/p' toad/Cargo.toml)
VERSION := $(shell sed -n 's/^version = "\(.*\)"/\1/p' toad/Cargo.toml)
endif

# OpenBench specifies that the binary name should be changeable with the EXE parameter
Expand All @@ -26,7 +26,7 @@ endif
# Compile an executable for use with OpenBench
openbench:
@echo Compiling $(EXE) for OpenBench
cargo rustc --release --bin toad -- -C target-cpu=native --emit link=$(EXE)
cargo rustc --manifest-path ./toad/Cargo.toml --release --bin toad -- -C target-cpu=native --emit link=$(EXE)

# Remove the EXE created
clean:
Expand Down
125 changes: 66 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# Toad 🐸 A UCI-compatible toy chess engine

Toad is a work-in-progress [chess engine](https://en.wikipedia.org/wiki/Chess_engine), and serves as my personal excuse to write fun code in Rust.
It was [originally](https://github.com/dannyhammer/toad/pull/73) built upon my [`chessie`](https://crates.io/crates/chessie) crate, which is a chess library that handles board representation, move generation and all other rules of chess.
Development progress is recorded automatically in the [changelog](./CHANGELOG.md).
All progression/non-regression testing is done through [OpenBench](https://github.com/AndyGrant/OpenBench) instance hosted [here](https://pyronomy.pythonanywhere.com/index/).
Strength of the latest version can be found on the [CCRL pages](https://computerchess.org.uk/ccrl/)- just search for `Toad`!
Strength of the latest version can be found on the [CCRL pages](https://computerchess.org.uk/ccrl/404/cgi/compare_engines.cgi?family=Toad&print=Rating+list&print=Results+table&print=LOS+table&print=Ponder+hit+table&print=Eval+difference+table&print=Comopp+gamenum+table&print=Overlap+table&print=Score+with+common+opponents).

Up for a game? Play against Toad on [Lichess](https://lichess.org/@/toad-bot)!

Expand All @@ -18,60 +17,6 @@ To run multiple commands on startup, pass them in with the `-c "<command>"` flag
You can pass in the `--no-exit` flag to continue execution after the command(s) have finished executing.
Run the engine and execute the `help` command to see a list of available commands, and `--help` to view all CLI flags and arguments.

### UCI Commands

Toad abides (mostly) by the [Universal Chess Interface](https://backscattering.de/chess/uci/) protocol, and communicates through `stdin` and `stdout`.
The parsing of UCI commands and responses is handled by my [`uci-parser`](https://crates.io/crates/uci-parser) crate.

The following UCI commands (and arguments) are supported:

- `uci`
- `debug [ on | off ]`
- `isready`
- `setoption name <x> [value <y>]`
- `ucinewgame`
- `position [fen <fenstring> | startpos] [moves <move_1> ... <move_i>]`
- Extended to include [`position kiwipete`](https://www.chessprogramming.org/Perft_Results#Position_2).
- `go wtime <x> btime <x> winc <x> binc <x> depth <x> nodes <x> movetime <x> infinite`
- Extended to include `go perft <x>`
- `stop`
- `quit`

### Custom Commands

In addition to the above UCI commands, Toad also supports the following custom commands:

```
Commands:
await Await the current search, blocking until it completes
bench Run a benchmark with the provided parameters
changevariant Change the variant of chess being played, or display the current variant
display Print a visual representation of the current board state
eval Print an evaluation of the current position
exit Quit the engine
fen Generate and print a FEN string for the current position
flip Flips the side-to-move. Equivalent to playing a nullmove
hashinfo Display information about the current hash table(s) in the engine
makemove Apply the provided move to the game, if possible
moves Shows all legal moves in the current position, or for a specific piece
option Display the current value of the specified option
perft Performs a perft on the current position at the supplied depth, printing total node count
psqt Outputs the Piece-Square table value for the provided piece at the provided square, scaled with the endgame weight
splitperft Performs a split perft on the current position at the supplied depth
help Print this message or the help of the given subcommand(s)
```

For specifics on how a command works, run `toad <COMMAND> --help`

### UCI Options

| Name | Values | Default | Description |
| -------------- | --------------- | ------- | --------------------------------------------------------------------------------- |
| `Clear Hash` | | | Clear the hash table(s) |
| `Hash` | `1..=1024` | `16` | Set the size (in MB) of the hash table(s) |
| `Threads` | `1..=1` | `1` | Only implemented for use with [OpenBench](https://github.com/AndyGrant/OpenBench) |
| `UCI_Chess960` | `true`, `false` | `false` | Enable support for [Chess960](https://en.wikipedia.org/wiki/Fischer_random_chess) |

## Running

To run Toad, head over to the [releases](https://github.com/dannyhammer/toad/releases) page to grab the latest pre-compiled release for your platform.
Expand Down Expand Up @@ -108,14 +53,19 @@ If you are willing to test the installation and execution of Toad on other opera
- [Time Management](https://www.chessprogramming.org/Time_Management) with soft and hard timeouts.
- [Quiescence Search](https://www.chessprogramming.org/Quiescence_Search) in a fail soft framework.
- [Draw detection](https://www.chessprogramming.org/Draw) through insufficient material, 2-fold repetition, and the 50-move rule.
- [Transposition Table](https://www.chessprogramming.org/Transposition_Table).
- [Transposition Table](https://www.chessprogramming.org/Transposition_Table) for move ordering and [cutoffs](https://www.chessprogramming.org/Transposition_Table#Transposition_Table_Cutoffs).
- [Principal Variation Search](https://www.chessprogramming.org/Principal_Variation_Search).
- [Aspiration Windows](https://www.chessprogramming.org/Aspiration_Windows) with [gradual widening](https://www.chessprogramming.org/Aspiration_Windows#Gradual_Widening).
- [Null Move Pruning](https://www.chessprogramming.org/Null_Move_Pruning).
- [Reverse Futility Pruning](https://www.chessprogramming.org/Reverse_Futility_Pruning).
- [Late Move Reductions](https://www.chessprogramming.org/Late_Move_Reductions).
- [Check Extensions](https://www.chessprogramming.org/Check_Extensions).
- [Transposition Table Cutoffs](https://www.chessprogramming.org/Transposition_Table#Transposition_Table_Cutoffs).
- [Razoring](https://www.chessprogramming.org/Razoring).
- [Internal Iterative Reductions (Transposition Table Reductions)](https://www.chessprogramming.org/Internal_Iterative_Reductions).
- [Internal Iterative Deepening](https://www.chessprogramming.org/Internal_Iterative_Deepening).
- [Late Move Pruning](https://www.chessprogramming.org/Futility_Pruning#MoveCountBasedPruning).
- [Mate Distance Pruning](https://www.chessprogramming.org/Mate_Distance_Pruning).
- Support for [fractional plies](https://www.chessprogramming.org/Depth#Fractional_Plies)
- Move Ordering:
- [MVV-LVA](https://www.chessprogramming.org/MVV-LVA) with relative piece values `K < P < N < B < R < Q`, so `KxR` is ordered before `PxR`.
- [Hash moves](https://www.chessprogramming.org/Hash_Move).
Expand All @@ -129,15 +79,72 @@ If you are willing to test the installation and execution of Toad on other opera

More features will be added as development continues! You can see most of my future plans in the [backlog](https://github.com/dannyhammer/toad/issues).

### UCI Commands

Toad abides (mostly) by the [Universal Chess Interface](https://backscattering.de/chess/uci/) protocol, and communicates through `stdin` and `stdout`.
The parsing of UCI commands and responses is handled by my [`uci-parser`](https://crates.io/crates/uci-parser) crate.

The following UCI commands (and arguments) are supported:

- `uci`
- `debug [ on | off ]`
- `isready`
- `setoption name <x> [value <y>]`
- `ucinewgame`
- `position [fen <fenstring> | startpos] [moves <move_1> ... <move_i>]`
- Extended to include [`position kiwipete`](https://www.chessprogramming.org/Perft_Results#Position_2).
- `go wtime <x> btime <x> winc <x> binc <x> depth <x> nodes <x> movetime <x> infinite`
- Extended to include `go perft <x>`
- `stop`
- `quit`

### Custom Commands

In addition to the above UCI commands, Toad also supports the following custom commands:

```
Commands:
bench Run a benchmark with the provided parameters
changevariant Change the variant of chess being played, or display the current variant
display Print a visual representation of the current board state
eval Print an evaluation of the current position
exit Quit the engine
fen Generate and print a FEN string for the current position
flip Flips the side-to-move. Equivalent to playing a nullmove
hashinfo Display information about the current hash table(s) in the engine
makemove Apply the provided move to the game, if possible
moves Shows all legal moves in the current position, or for a specific piece
option Display the current value of the specified option
perft Performs a perft on the current position at the supplied depth, printing total node count
place Place a piece on the provided square
psqt Outputs the Piece-Square table value for the provided piece at the provided square, scaled with the endgame weight
splitperft Performs a split perft on the current position at the supplied depth
take Remove the piece at the provided square
wait Await the current search, blocking until it completes
help Print this message or the help of the given subcommand(s)
```
For specifics on how a command works, run `toad <COMMAND> --help`
### UCI Options
| Name | Values | Default | Description |
| -------------- | --------------- | ------- | --------------------------------------------------------------------------------- |
| `Clear Hash` | | | Clear the hash table(s) |
| `Hash` | `1..=1024` | `16` | Set the size (in MB) of the hash table(s) |
| `Threads` | `1..=1` | `1` | Only implemented for use with [OpenBench](https://github.com/AndyGrant/OpenBench) |
| `UCI_Chess960` | `true`, `false` | `false` | Enable support for [Chess960](https://en.wikipedia.org/wiki/Fischer_random_chess) |
## Acknowledgements
More people have helped me on this journey than I can track, but I'll name a few notable resources/people here:
- [Sebastian Lague](https://www.youtube.com/@SebastianLague), for his [chess programming series](https://www.youtube.com/watch?v=_vqlIPDR2TU&list=PLFt_AvWsXl0cvHyu32ajwh2qU1i6hl77c) on YouTube that ultimate inspired me to do this project.
- [Sebastian Lague](https://www.youtube.com/@SebastianLague), for his [chess programming series](https://www.youtube.com/watch?v=_vqlIPDR2TU&list=PLFt_AvWsXl0cvHyu32ajwh2qU1i6hl77c) on YouTube, which was the original inspiration for this project.
- The [Chess Programming Wiki](https://www.chessprogramming.org/), and all those who contribute to free, open-source knowledge.
- The folks over at the [Engine Programming Discord](https://discord.com/invite/F6W6mMsTGN), for their patience with my silly questions and invaluable help overall.
- [Analog-Hors](https://github.com/analog-hors), for an excellent [article on magic bitboards](https://analog-hors.github.io/site/magic-bitboards/)
- The authors of [viridithas](https://github.com/cosmobobak/viridithas/) and [Stormphrax](https://github.com/Ciekce/Stormphrax), for allowing their engines to be open source and for answering all my silly questions.
- [Andrew Grant](https://github.com/AndyGrant/) for creating [OpenBench](https://github.com/AndyGrant/OpenBench) and being willing to help me with its setup and use.
- All those in the engine testing community, with special thanks for those who manage and host the [CCRL pages](https://computerchess.org.uk/ccrl/).
- The authors of [Yukari](https://github.com/yukarichess/yukari) for motivation through friendly competition.
- [Paul T](https://github.com/DeveloperPaul123), for feedback on my [`uci-parser`](https://crates.io/crates/uci-parser) crate.
Loading

0 comments on commit 6eb100f

Please sign in to comment.