Skip to content

Commit

Permalink
Merge pull request #17 from eboatwright/dev
Browse files Browse the repository at this point in the history
v3.1
  • Loading branch information
eboatwright authored Feb 13, 2024
2 parents 0b2370c + 2e5e75d commit 63ad318
Show file tree
Hide file tree
Showing 21 changed files with 1,259 additions and 648 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ Cargo.lock
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

sharpener/
sharpener
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "maxwell"
version = "3.0.8"
version = "3.1.0"
edition = "2021"

[dependencies]
Expand Down
89 changes: 62 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
![](/icon/Maxwell_316x316.png)
# Maxwell Chess Engine
A Chess engine written from scratch in Rust!<br>
A Chess engine written from scratch in Rust.<br>
If you use this code verbatim, or as a reference, please credit me!<br><br>

Rating Lists featuring Maxwell: [CCRL Blitz](https://computerchess.org.uk/ccrl/404/) | [MCERL](https://www.chessengeria.eu/mcerl)<br>
[Play against Maxwell on Lichess!](https://lichess.org/@/MaxwellOnLC) | [Some of Maxwell's Games](https://www.chess.com/library/collections/maxwells-games-my-chess-engine-2FFU82NM4)

## Rough Roadmap
- Tweak search values & thresholds
- Internal Iterative Deepening
- Static Exchange Evaluation
- Late Move Pruning
- Write an NNUE implementation! I've learned how Neural Networks work, so I'm really excited to get started on that
- Multithreading

## Features
#### Parameters
- fen=\<FEN STRING>: Sets up the board by a fen string (Doesn't work for UCI games) (default=STARTING_FEN)
- debug=\<BOOLEAN>: Toggle debug output that gets outputed per ply (default=true)
- opening_book=\<BOOLEAN>: Toggle opening book (default=true)
- opening_book=\<BOOLEAN>: Toggle built-in opening book (default=false)
- time_management=\<BOOLEAN>: Toggle time management, if false the bot will use all the remaining time (default=true)
- hash_size=\<INTEGER>: Sets the hash size in Megabytes, there's also a UCI option for this under the name "Hash" (default=256)
#### UCI Interface
- uci, isready, ucinewgame, position, go, stop, and quit commands
- "position" is only implemented for "position startpos", "position fen" is not yet implemented
- "Hash" UCI option, which sets the hash / transposition table size in Megabytes
#### Board Representation
- Purely bitboards
- Supports loading from FEN strings
Expand All @@ -21,43 +33,66 @@
- Magic bitboards for sliding pieces
- Hardcoded pawn movement
- Bitboard masks for other pieces calculated at startup
- Calculates pseudo-legal moves, then skips illegal moves in move loop
#### Evaluation
- Material count
- Piece square tables
- Separate middlegame and endgame tables for pawns and kings
- Passed, isolated and doubled pawns
- Attacked squares around kings
#### Move Ordering
- Hash move / best move from previous iteration
- Best move from the previous iteration, otherwise whatever move from the transposition table
- MVV-LVA
- 2 Killer moves
- History heuristic
- Castling
- Promotions
- Penalty for moving a piece to a square an opponent's piece attacks
- 2 Killer Moves
- History Heuristic
- Indexed by side to move, move start square, move end square
#### Search
- Iterative deepening
- Aspiration windows
- Starts at 40 and multiplies by 4 if out of alpha beta bounds
- Time management
- If less than 7 moves have been played, it uses 2.5% of it's remaining time, otherwise 7%
- This value is then also clamped between 0.25 and 20.0 seconds
- Exits search if a mate is found within search depth
- Alpha beta pruning
- Quiescence search with Delta Pruning
- Transposition table
- No set max size, but entries get removed after 10 moves without hits
- Null move pruning
- Single Threaded ~ For now ;)
- Negamax
- Iterative Deepening
- Alpha-Beta Pruning
- Late Move Reductions
- Principal Variation Search
- Reverse Futility Pruning (Static Null Move Pruning)
- Null Move Pruning
- Razoring
- Reverse futility pruning
- Late move reduction
- Search extensions
- Promotions
- Internal Iterative Reductions
- Quiescence Search
- Delta Pruning
- No TT Lookups
- Transposition Table
- UCI "Hash" option to change max size, default is 256 MB
- Replacement scheme prefers higher depth and exact evaluation bound
- Search Extensions
- Checks
- Pawn moves to the 2nd or 7th rank
- Time management
- If less than 7 moves have been played, it uses 2.5% of it's remaining time, otherwise 7%
- This value is then also clamped between 0.05 and 30.0 seconds

## Helpful Sources
## Helpful Sources & References
#### Thanks to Sebastian Lague for making his YouTube series, which inspired me to make my own engine!
- [Sebastian Lague's Chess Programming series](https://www.youtube.com/playlist?list=PLFt_AvWsXl0cvHyu32ajwh2qU1i6hl77c)

#### When I'm not sure what to do next, I like to read through other engine's code for ideas. <br> I try not to copy line for line, but in any case here are the engine's I've referenced:
- [Boychesser](https://github.com/analog-hors/Boychesser/)
- [Weiawaga](https://github.com/Heiaha/Weiawaga/)
- [Rustic (Engine and Book)](https://github.com/mvanthoor/rustic)
- [Lynx](https://github.com/lynx-chess/Lynx/)
- [Fruit 2.1](https://github.com/Warpten/Fruit-2.1/)
- [Tcheran](https://github.com/jgilchrist/tcheran/)
- [MadChess](https://github.com/ekmadsen/MadChess/)
- [Black Marlin](https://github.com/jnlt3/blackmarlin/)
- [Ethereal](https://github.com/AndyGrant/Ethereal/)

#### And some other helpful resources
- [The Chess Programming Wiki](https://www.chessprogramming.org/Main_Page)
- [BBC Engine Development](https://www.youtube.com/playlist?list=PLmN0neTso3Jxh8ZIylk74JpwfiWNI76Cs)
- [Lynx](https://github.com/lynx-chess/Lynx/)
- [Weiawaga](https://github.com/Heiaha/Weiawaga/)
- [Perfect 2021 Opening Book](https://sites.google.com/site/computerschess/perfect-2021-books)
- [Cute Chess](https://cutechess.com/)
- [PVS Implementation](https://web.archive.org/web/20071030220825/http://www.brucemo.com/compchess/programming/pvs.htm)
- [LMR Implementation](https://web.archive.org/web/20150212051846/http://www.glaurungchess.com/lmr.html)
- [Mediocre Chess](https://mediocrechess.blogspot.com/)
- [Chess Programming Reddit](https://www.reddit.com/r/chessprogramming/)
- [TalkChess Forum](https://talkchess.com/forum3/index.php)
- [Stockfish Features List](https://www.chessprogramming.org/Stockfish#Search)
207 changes: 207 additions & 0 deletions Test results.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
For X - Y - Z the order is: Wins, Losses, Draws
I used to use 30+0.5 and 10+0.2, but lately I've been using 8+0.08
================================================================


Re-write v1 vs v3.0.8: 17 - 14 - 19


Elo +/- Games Score Draw
1 Re-write v2 63 58 100 59.0% 30.0%
2 Re-write v1 -7 58 100 49.0% 28.0%
3 Maxwell v3.0.8 -56 56 100 42.0% 34.0%


This is for when to return a TT evaluation
Re-write v3 (Not root) vs Re-write v3 (Not PV): 131 - 60 - 109
Re-write v3 (Not root) vs Re-write v3 (All): 33 - 33 - 36
Re-write v3 (Depth replace) vs Re-write v3 (Always replace): 74 - 60 - 66


TT replacement schemes
Elo +/- Games Points Draw
1 Re-write v3 (Higher depth, exact bound) 37 37 200 110.5 40.5%
2 Re-write v3 (Higher depth, equals key) -7 37 200 98.0 40.0%
3 Re-write v3 (Higher depth) -30 38 200 91.5 39.5%


v4 contains fixes for Null Move Pruning
Elo +/- Games Points Draw
1 Re-write v4 33 45 150 82.0 34.7%
2 Re-write v3 12 46 150 77.5 32.7%
3 Re-write v2 -44 43 150 65.5 40.7%


Promotion extension tests
Elo +/- Games Points Draw
Pawn one step from promotion 26 45 150 80.5 35.3%
Promotion flag -7 45 150 73.5 35.3%
Neither -19 45 150 71.0 34.7%


Re-write v4 vs Aspiration window drops after out of bounds: 275 - 263 - 262


Re-write v5 (Uses evaluation from the last iteration instead from the last search) vs Re-write v4: 106 - 93 - 101


Removed capture check before doing pruning techniques vs Re-write v5: 126 - 98 - 76


Capture check only before NMP vs Re-write v5 (Capture check before any pruning) 54 - 37 - 35


Elo +/- Games Points Draw
Re-write v6 (Capture check before NMP) 17 20 800 419.0 31.3%
Removed capture check 7 20 800 408.0 31.5%
Re-write v5 (Capture check before pruning) -23 20 800 373.0 29.8%


Re-write v7 (RFP before NMP) vs Re-write v6: 136 - 120 - 144


Re-write v8 (New PVS) vs Re-write v7: 161 - 117 - 122


Re-write v9 (LMR & PVS changes) vs Re-write v8 (New PVS): 418 - 241 - 341


Re-write v10 vs Re-write v9: 138 - 135 - 127


Re-write v10 vs Maxwell v3.0.8: 754 - 171 - 331 (1256 games!)


Re-write v10 vs Added extension to PVS searches: 99 - 85 - 116 (?)


Re-write v11 vs Re-write v10: 376 - 343 - 481


With alpha TT stores vs Without alpha TT stores (Re-write v11): 239 - 256 - 355


Don't allow more than one null move in the search tree at once vs Re-write v11: 175 - 167 - 268
Techincally a win, but the scores were fluctuating a good bit, and I'm not so sure about it


v12 vs v11 was equal, because v12 was just a bit of code cleanup


Strelka razoring vs Re-write v12: 234 - 304 - 368


NMP changes (> 1, and 3 + (depth - 2) / 5) vs Re-write v12: 255 - 254 - 391


Re-write v13 (NMP changes: > 1, and 3 + (depth - 2) / 3) vs Re-write v12: 171 - 122 - 207


History indexed by [from][to] vs Re-write v13: 76 - 77 - 113


Re-write v14 (History indexed by [side to move][from][to]) vs Re-write v13: 152 - 125 - 223


The move order penalty when the target square is attacked by the enemy
Elo +/- Games Points Wins Draws
Re-write v15 (Removed) 27 16 1000 539.0 320 438
Re-write v14 -13 16 1000 481.0 259 444
Re-write v15 (Quiets only) -14 16 1000 480.0 262 436


Countermoves vs Re-write v15: 122 - 177 - 201


v16 changed TT to use a Vec instead of a HashMap, so there should be no strength difference


Re-write v17 (Re-wrote killer moves) vs Re-write v16: 173 - 122 - 205


Re-write v17 vs Maxwell v3.0.8 (Patch 2): 968 - 136 - 476 (1580 games, and an estimated +190 Elo!!)


Count plies since null moves in is_repetition vs Re-write v17: 140 - 149 - 211


Re-write v18 (Add null moves to repetition lookback) vs Re-write v17: 350 - 327 - 523


Re-write v19 (FINALLY FIXED REPETITION DETECTION) vs Re-write v18: 509 - 429 - 662


Incremental move sorter like Rustic vs Re-write v19: 153 - 171 - 176


Extensions outside the move loop vs Re-write v19: 576 - 581 - 593


Only check extension, outside the move loop vs Re-write v19: 272 - 276 - 252


Re-write v20 (Rewrote extension code) vs Re-write v19: 171 - 163 - 166


(Re-write v20's RFP Threshold is 60 centipawns per ply)
RFP = 80 vs Re-write v20: 165 - 172 - 173
RFP = 90 vs Re-write v20: 168 - 181 - 151
RFP = 100 vs Re-write v20: 423 - 519 - 508

At this point I changed RFP to return beta (because of fail hard)

RFP = 16 * (depth - 1).pow(2) + 50 vs Re-write v20: 196 - 217 - 191
RFP = 30 * (depth - 1).pow(2) + 60 vs Re-write v20: 133 - 150 - 176
RFP = 20 * (depth - 1).pow(2) + 65 vs Re-write v20: 140 - 163 - 147
RFP = 40 * (depth - 1).pow(2) + 70 vs Re-write v20: 486 - 498 - 516

FINALLY
Re-write v21 (RFP = 50 * (depth - 1).pow(2) + 55) vs Re-write v20: 276 - 252 - 272


Hmm
Store Alpha bound TT entries vs Re-write v21: 283 - 308 - 311


No TT Cutoff on PV-nodes vs Re-write v21: 514 - 521 - 573


Internal Iterative Reductions
Only on PV, Depth > 2, Depth -= 1 vs Re-write v21: 254 - 255 - 253
Re-write v22 (All nodes, Depth > 1, Depth -= 1) vs Re-write v21: 285 - 234 - 247
All nodes, Depth > 2, Depth -= 2 vs Re-write v22: It lost horribly :P


Only return TT eval if not PV, or depth == 0 vs Re-write v22: 498 - 554 - 618


RFP = 55 * (depth - 1).pow(2) + 60 vs Re-write v22: 142 - 145 - 153


Razoring change (depth -= 2) vs Re-write v22: 533 - 556 - 687
Razoring change (threshold <= alpha) vs Re-write v22: 122 - 132 - 146
Razoring change (285 + 200 * (depth - 1)) vs Re-write v22: 205 - 198 - 207
Razoring change (300 + 150 * (depth - 1)) vs Re-write v22: 265 - 225 - 266


Only NMP if no hash move vs Re-write v23: 485 - 520 - 597


Aspiration Windows
Elo +/- Games Points Wins Draws Draw
AW=30 12 24 500 258.5 165 187 37.4%
AW=60 -2 25 500 248.5 167 163 32.6%
Re-write v23 (40) -5 25 500 246.5 164 165 33.0%
AW=50 -5 24 500 246.5 148 197 39.4%


Hmm
AW=30 vs Re-write v32: 147 - 148 - 145


Maxwell v3.1 (Re-write v23) vs Maxwell v3.0.8 (Patch 2): 1533 - 155 - 312
Elo difference: 293.9 +/- 17.6, LOS: 100.0 %


Tests TODO:
TT enabled vs TT disabled
44 changes: 44 additions & 0 deletions To-do list.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
thoughts on NNUE:
I've wanted to learn how to write neural net for a long time, so I want to implement NNUE eventually.
But what I'm not going to do is just find a SF NNUE library and stick it in there because that's lame

Update:
I've learned how to write neural networks, and trained one to evaluate Tic-Tac-Toe positions!
So for either v3.2 or v3.3 I'm gonna work on writing my own version of NNUE from scratch

figure out some sort of multithreading:
to implement pondering I think I'll have to add multithreading

maybe one thread that waits for UCI commands like "stop"
and all the rest of the threads search

https://www.chessprogramming.org/Lazy_SMP

transposition table:
buckets
aging
make it multithreading safe
prefetching

try removing all the attacked squares bitboards stuff, and just make a function that detects whether one square is attacked?
try giving a small boost in evaluation for the current side to move
try lower pawn evaluation values
experiment with more than 2 killer moves per ply
calculate my own magic numbers; currently "borrowing" Sebastian Lague's ^^
check out pin detection to speed up check detection
try writing a struct that sorts moves incrementally
I tried this a couple times, but haven't got it faster than my current solution
re-implement PV table with a different approach; I don't like the 2d array

History reductions / pruning
https://www.chessprogramming.org/Internal_Iterative_Deepening
https://www.chessprogramming.org/Static_Exchange_Evaluation
https://www.chessprogramming.org/Futility_Pruning#MoveCountBasedPruning (Late move pruning)
https://www.chessprogramming.org/History_Leaf_Pruning
https://www.chessprogramming.org/ProbCut
https://www.chessprogramming.org/Razoring#Strelka
https://www.chessprogramming.org/Texel's_Tuning_Method

Some random resources I found: (Not using them right now but they could be useful)
https://analog-hors.github.io/site/magic-bitboards/
https://mediocrechess.blogspot.com/2006/12/guide-attacked-squares.html
Loading

0 comments on commit 63ad318

Please sign in to comment.