Merge branch 'master' into clusterMergeMaster15

fix small merge conflicts, lightly tested:
Score of cluster vs master: 1 - 0 - 59  [0.508] 60
Elo difference: 5.8 +/- 11.2, LOS: 84.1 %, DrawRatio: 98.3 %
This commit is contained in:
Joost VandeVondele
2022-10-30 15:34:59 +01:00
35 changed files with 924 additions and 641 deletions
+65
View File
@@ -0,0 +1,65 @@
name: Report issue
description: Create a report to help us fix issues with the engine
body:
- type: textarea
attributes:
label: Describe the issue
description: A clear and concise description of what you're experiencing.
validations:
required: true
- type: textarea
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: |
Steps to reproduce the behavior.
You can also use this section to paste the command line output.
placeholder: |
```
position startpos moves g2g4 e7e5 f2f3
go mate 1
info string NNUE evaluation using nn-6877cd24400e.nnue enabled
info depth 1 seldepth 1 multipv 1 score mate 1 nodes 33 nps 11000 tbhits 0 time 3 pv d8h4
bestmove d8h4
```
validations:
required: true
- type: textarea
attributes:
label: Anything else?
description: |
Anything that will give us more context about the issue you are encountering.
You can also use this section to propose ideas on how to solve the issue.
validations:
required: false
- type: dropdown
attributes:
label: Operating system
options:
- All
- Windows
- Linux
- MacOS
- Android
- Other or N/A
validations:
required: true
- type: input
attributes:
label: Stockfish version
description: |
This can be found by running the engine.
You can also use the commit ID.
placeholder: Stockfish 15 / e6e324e
validations:
required: true
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Discord server
url: https://discord.gg/GWDRS3kU6R
about: Feel free to ask for support or have a chat with us in our Discord server!
- name: Discussions, Q&A, ideas, show us something...
url: https://github.com/official-stockfish/Stockfish/discussions/new
about: Do you have an idea for Stockfish? Do you want to show something that you made? Please open a discussion about it!
+20 -3
View File
@@ -5,7 +5,6 @@ on:
- master - master
- tools - tools
- github_ci - github_ci
- github_ci_armv7
pull_request: pull_request:
branches: branches:
- master - master
@@ -51,7 +50,7 @@ jobs:
os: ubuntu-20.04, os: ubuntu-20.04,
compiler: aarch64-linux-android21-clang++, compiler: aarch64-linux-android21-clang++,
comp: ndk, comp: ndk,
run_armv8_tests: true, run_armv8_tests: false,
shell: 'bash {0}' shell: 'bash {0}'
} }
- { - {
@@ -59,7 +58,7 @@ jobs:
os: ubuntu-20.04, os: ubuntu-20.04,
compiler: armv7a-linux-androideabi21-clang++, compiler: armv7a-linux-androideabi21-clang++,
comp: ndk, comp: ndk,
run_armv7_tests: true, run_armv7_tests: false,
shell: 'bash {0}' shell: 'bash {0}'
} }
- { - {
@@ -269,6 +268,12 @@ jobs:
- name: Test armv8 build - name: Test armv8 build
if: ${{ matrix.config.run_armv8_tests }} if: ${{ matrix.config.run_armv8_tests }}
run: | run: |
ANDROID_ROOT=/usr/local/lib/android
ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk
SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager
echo "y" | $SDKMANAGER "ndk;21.4.7075529"
ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle
ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument" export LDFLAGS="-static -Wno-unused-command-line-argument"
make clean make clean
@@ -280,6 +285,12 @@ jobs:
- name: Test armv7 build - name: Test armv7 build
if: ${{ matrix.config.run_armv7_tests }} if: ${{ matrix.config.run_armv7_tests }}
run: | run: |
ANDROID_ROOT=/usr/local/lib/android
ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk
SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager
echo "y" | $SDKMANAGER "ndk;21.4.7075529"
ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle
ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument" export LDFLAGS="-static -Wno-unused-command-line-argument"
make clean make clean
@@ -289,6 +300,12 @@ jobs:
- name: Test armv7-neon build - name: Test armv7-neon build
if: ${{ matrix.config.run_armv7_tests }} if: ${{ matrix.config.run_armv7_tests }}
run: | run: |
ANDROID_ROOT=/usr/local/lib/android
ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk
SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager
echo "y" | $SDKMANAGER "ndk;21.4.7075529"
ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle
ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument" export LDFLAGS="-static -Wno-unused-command-line-argument"
make clean make clean
+11 -3
View File
@@ -35,19 +35,23 @@ Ben Chaney (Chaneybenjamini)
Ben Koshy (BKSpurgeon) Ben Koshy (BKSpurgeon)
Bill Henry (VoyagerOne) Bill Henry (VoyagerOne)
Bojun Guo (noobpwnftw, Nooby) Bojun Guo (noobpwnftw, Nooby)
Boštjan Mejak (PedanticHacker)
braich braich
Brian Sheppard (SapphireBrand, briansheppard-toast) Brian Sheppard (SapphireBrand, briansheppard-toast)
Bruno de Melo Costa (BM123499) Bruno de Melo Costa (BM123499)
Bruno Pellanda (pellanda)
Bryan Cross (crossbr) Bryan Cross (crossbr)
candirufish candirufish
Chess13234 Chess13234
Chris Cain (ceebo) Chris Cain (ceebo)
clefrks
Dale Weiler (graphitemaster) Dale Weiler (graphitemaster)
Dan Schmidt (dfannius) Dan Schmidt (dfannius)
Daniel Axtens (daxtens) Daniel Axtens (daxtens)
Daniel Dugovic (ddugovic) Daniel Dugovic (ddugovic)
Dariusz Orzechowski (dorzechowski) Dariusz Orzechowski (dorzechowski)
David Zar David Zar
David (dav1312)
Daylen Yang (daylen) Daylen Yang (daylen)
Deshawn Mohan-Smith (GoldenRare) Deshawn Mohan-Smith (GoldenRare)
Dieter Dobbelaere (ddobbelaere) Dieter Dobbelaere (ddobbelaere)
@@ -55,6 +59,7 @@ DiscanX
Dominik Schlösser (domschl) Dominik Schlösser (domschl)
double-beep double-beep
Douglas Matos Gomes (dsmsgms) Douglas Matos Gomes (dsmsgms)
Dubslow
Eduardo Cáceres (eduherminio) Eduardo Cáceres (eduherminio)
Eelco de Groot (KingDefender) Eelco de Groot (KingDefender)
Elvin Liu (solarlight2) Elvin Liu (solarlight2)
@@ -104,6 +109,7 @@ jundery
Justin Blanchard (UncombedCoconut) Justin Blanchard (UncombedCoconut)
Kelly Wilson Kelly Wilson
Ken Takusagawa Ken Takusagawa
Kian E (KJE-98)
kinderchocolate kinderchocolate
Kiran Panditrao (Krgp) Kiran Panditrao (Krgp)
Kojirion Kojirion
@@ -124,6 +130,7 @@ marotear
Matt Ginsberg (mattginsberg) Matt Ginsberg (mattginsberg)
Matthew Lai (matthewlai) Matthew Lai (matthewlai)
Matthew Sullivan (Matt14916) Matthew Sullivan (Matt14916)
Max A. (Disservin)
Maxim Molchanov (Maxim) Maxim Molchanov (Maxim)
Michael An (man) Michael An (man)
Michael Byrne (MichaelB7) Michael Byrne (MichaelB7)
@@ -154,7 +161,6 @@ Panthee
Pascal Romaret Pascal Romaret
Pasquale Pigazzini (ppigazzini) Pasquale Pigazzini (ppigazzini)
Patrick Jansen (mibere) Patrick Jansen (mibere)
pellanda
Peter Schneider (pschneider1968) Peter Schneider (pschneider1968)
Peter Zsifkovits (CoffeeOne) Peter Zsifkovits (CoffeeOne)
Praveen Kumar Tummala (praveentml) Praveen Kumar Tummala (praveentml)
@@ -162,9 +168,10 @@ Rahul Dsilva (silversolver1)
Ralph Stößer (Ralph Stoesser) Ralph Stößer (Ralph Stoesser)
Raminder Singh Raminder Singh
renouve renouve
Reuven Peleg Reuven Peleg (R-Peleg)
Richard Lloyd Richard Lloyd (Richard-Lloyd)
Rodrigo Exterckötter Tjäder Rodrigo Exterckötter Tjäder
Rodrigo Roim (roim)
Ron Britvich (Britvich) Ron Britvich (Britvich)
Ronald de Man (syzygy1, syzygy) Ronald de Man (syzygy1, syzygy)
rqs rqs
@@ -184,6 +191,7 @@ Stefan Geschwentner (locutus2)
Stefano Cardanobile (Stefano80) Stefano Cardanobile (Stefano80)
Steinar Gunderson (sesse) Steinar Gunderson (sesse)
Stéphane Nicolet (snicolet) Stéphane Nicolet (snicolet)
Syine Mineta (MinetaS)
Prokop Randáček (ProkopRandacek) Prokop Randáček (ProkopRandacek)
Thanar2 Thanar2
thaspel thaspel
+79 -38
View File
@@ -1,14 +1,27 @@
<div align="center">
[![Stockfish][stockfish128-logo]][website-link]
[![Build][build-badge]][build-link]
[![License][license-badge]][license-link]
<br>
[![Release][release-badge]][release-link]
[![Commits][commits-badge]][commits-link]
<br>
[![Website][website-badge]][website-link]
[![Fishtest][fishtest-badge]][fishtest-link]
[![Discord][discord-badge]][discord-link]
</div>
## Overview ## Overview
[![Build Status](https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml/badge.svg)](https://github.com/official-stockfish/Stockfish/actions) [Stockfish][website-link] is a free, powerful UCI chess engine derived from
[![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?branch=master&svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master) Glaurung 2.1. Stockfish is not a complete chess program and requires a UCI-compatible
graphical user interface (GUI) (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard,
[Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably.
derived from Glaurung 2.1. Stockfish is not a complete chess program and requires a Read the documentation for your GUI of choice for information about how to use
UCI-compatible graphical user interface (GUI) (e.g. XBoard with PolyGlot, Scid, Stockfish with it.
Cute Chess, eboard, Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in order
to be used comfortably. Read the documentation for your GUI of choice for information
about how to use Stockfish with it.
The Stockfish engine features two evaluation functions for chess. The efficiently The Stockfish engine features two evaluation functions for chess. The efficiently
updatable neural network (NNUE) based evaluation is the default and by far the strongest. updatable neural network (NNUE) based evaluation is the default and by far the strongest.
@@ -21,28 +34,25 @@ avx2, neon, or similar).
This distribution of Stockfish consists of the following files: This distribution of Stockfish consists of the following files:
* [Readme.md](https://github.com/official-stockfish/Stockfish/blob/master/README.md), * [README.md][readme-link], the file you are currently reading.
the file you are currently reading.
* [Copying.txt](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt), * [Copying.txt][license-link], a text file containing the GNU General Public License
a text file containing the GNU General Public License version 3. version 3.
* [AUTHORS](https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS), * [AUTHORS][authors-link], a text file with the list of authors for the project.
a text file with the list of authors for the project
* [src](https://github.com/official-stockfish/Stockfish/tree/master/src), * [src][src-link], a subdirectory containing the full source code, including a Makefile
a subdirectory containing the full source code, including a Makefile
that can be used to compile Stockfish on Unix-like systems. that can be used to compile Stockfish on Unix-like systems.
* a file with the .nnue extension, storing the neural network for the NNUE * a file with the .nnue extension, storing the neural network for the NNUE evaluation.
evaluation. Binary distributions will have this file embedded. Binary distributions will have this file embedded.
## The UCI protocol and available options ## The UCI protocol and available options
The Universal Chess Interface (UCI) is a standard protocol used to communicate with The Universal Chess Interface (UCI) is a standard protocol used to communicate with
a chess engine, and is the recommended way to do so for typical graphical user interfaces a chess engine, and is the recommended way to do so for typical graphical user interfaces
(GUI) or chess tools. Stockfish implements the majority of its options as described (GUI) or chess tools. Stockfish implements the majority of its options as described
in [the UCI protocol](https://www.shredderchess.com/download/div/uci.zip). in [the UCI protocol][uci-link].
Developers can see the default values for UCI options available in Stockfish by typing Developers can see the default values for UCI options available in Stockfish by typing
`./stockfish uci` in a terminal, but the majority of users will typically see them and `./stockfish uci` in a terminal, but the majority of users will typically see them and
@@ -180,12 +190,10 @@ on the evaluations of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward. The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move. of the neural network need to be updated after a typical chess move.
[The nodchip repository](https://github.com/nodchip/Stockfish) provided the first [The nodchip repository][nodchip-link] provided the first version of the needed tools
version of the needed tools to train and develop the NNUE networks. Today, more to train and develop the NNUE networks. Today, more advanced training tools are
advanced training tools are available in available in [the nnue-pytorch repository][pytorch-link], while data generation tools
[the nnue-pytorch repository](https://github.com/glinscott/nnue-pytorch/), are available in [a dedicated branch][tools-link].
while data generation tools are available in
[a dedicated branch](https://github.com/official-stockfish/Stockfish/tree/tools).
On CPUs supporting modern vector instructions (avx2 and similar), the NNUE evaluation On CPUs supporting modern vector instructions (avx2 and similar), the NNUE evaluation
results in much stronger playing strength, even if the nodes per second computed by results in much stronger playing strength, even if the nodes per second computed by
@@ -278,8 +286,8 @@ are already enabled, and no configuration is needed.
### Support on Windows ### Support on Windows
The use of large pages requires "Lock Pages in Memory" privilege. See The use of large pages requires "Lock Pages in Memory" privilege. See
[Enable the Lock Pages in Memory Option (Windows)](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows) [Enable the Lock Pages in Memory Option (Windows)][lockpages-link]
on how to enable this privilege, then run [RAMMap](https://docs.microsoft.com/en-us/sysinternals/downloads/rammap) on how to enable this privilege, then run [RAMMap][rammap-link]
to double-check that large pages are used. We suggest that you reboot to double-check that large pages are used. We suggest that you reboot
your computer after you have enabled large pages, because long Windows your computer after you have enabled large pages, because long Windows
sessions suffer from memory fragmentation, which may prevent Stockfish sessions suffer from memory fragmentation, which may prevent Stockfish
@@ -322,26 +330,26 @@ effort. There are a few ways to help contribute to its growth.
### Donating hardware ### Donating hardware
Improving Stockfish requires a massive amount of testing. You can donate Improving Stockfish requires a massive amount of testing. You can donate
your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker:-overview) your hardware resources by installing the [Fishtest Worker][worker-link]
and view the current tests on [Fishtest](https://tests.stockfishchess.org/tests). and view the current tests on [Fishtest][fishtest-link].
### Improving the code ### Improving the code
If you want to help improve the code, there are several valuable resources: If you want to help improve the code, there are several valuable resources:
* [In this wiki,](https://www.chessprogramming.org) many techniques used in * [In this wiki,][programming-link] many techniques used in
Stockfish are explained with a lot of background information. Stockfish are explained with a lot of background information.
* [The section on Stockfish](https://www.chessprogramming.org/Stockfish) * [The section on Stockfish][programmingsf-link]
describes many features and techniques used by Stockfish. However, it is describes many features and techniques used by Stockfish. However, it is
generic rather than being focused on Stockfish's precise implementation. generic rather than being focused on Stockfish's precise implementation.
Nevertheless, a helpful resource. Nevertheless, a helpful resource.
* The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish). * The latest source can always be found on [GitHub][github-link].
Discussions about Stockfish take place these days mainly in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking) Discussions about Stockfish take place these days mainly in the [FishCooking][fishcooking-link]
group and on the [Stockfish Discord channel](https://discord.gg/nv8gDtt). group and on the [Stockfish Discord channel][discord-link].
The engine testing is done on [Fishtest](https://tests.stockfishchess.org/tests). The engine testing is done on [Fishtest][fishtest-link].
If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test) If you want to help improve Stockfish, please read this [guideline][guideline-link]
first, where the basics of Stockfish development are explained. first, where the basics of Stockfish development are explained.
@@ -361,4 +369,37 @@ exact binary you are distributing. If you make any changes to the
source code, these changes must also be made available under the GPL v3. source code, these changes must also be made available under the GPL v3.
For full details, read the copy of the GPL v3 found in the file named For full details, read the copy of the GPL v3 found in the file named
[*Copying.txt*](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt). [*Copying.txt*][license-link].
[authors-link]: https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS
[build-link]: https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml
[commits-link]: https://github.com/official-stockfish/Stockfish/commits/master
[discord-link]: https://discord.gg/GWDRS3kU6R
[fishcooking-link]: https://groups.google.com/g/fishcooking
[fishtest-link]: https://tests.stockfishchess.org/tests
[github-link]: https://github.com/official-stockfish/Stockfish
[guideline-link]: https://github.com/glinscott/fishtest/wiki/Creating-my-first-test
[license-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt
[lockpages-link]: https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows
[nodchip-link]: https://github.com/nodchip/Stockfish
[programming-link]: https://www.chessprogramming.org/Main_Page
[programmingsf-link]: https://www.chessprogramming.org/Stockfish
[pytorch-link]: https://github.com/glinscott/nnue-pytorch
[rammap-link]: https://docs.microsoft.com/en-us/sysinternals/downloads/rammap
[readme-link]: https://github.com/official-stockfish/Stockfish/blob/master/README.md
[release-link]: https://github.com/official-stockfish/Stockfish/releases/latest
[src-link]: https://github.com/official-stockfish/Stockfish/tree/master/src
[stockfish128-logo]: https://stockfishchess.org/images/logo/icon_128x128.png
[tools-link]: https://github.com/official-stockfish/Stockfish/tree/tools
[uci-link]: https://www.shredderchess.com/download/div/uci.zip
[website-link]: https://stockfishchess.org
[worker-link]: https://github.com/glinscott/fishtest/wiki/Running-the-worker:-overview
[build-badge]: https://img.shields.io/github/workflow/status/official-stockfish/Stockfish/Stockfish?style=for-the-badge&label=stockfish&logo=github
[commits-badge]: https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge
[discord-badge]: https://img.shields.io/discord/435943710472011776?style=for-the-badge&label=discord&logo=Discord
[fishtest-badge]: https://img.shields.io/website?style=for-the-badge&down_color=red&down_message=Offline&label=Fishtest&up_color=success&up_message=Online&url=https%3A%2F%2Ftests.stockfishchess.org%2Ftests%2Ffinished
[license-badge]: https://img.shields.io/github/license/official-stockfish/Stockfish?style=for-the-badge&label=license&color=success
[release-badge]: https://img.shields.io/github/v/release/official-stockfish/Stockfish?style=for-the-badge&label=official%20release
[website-badge]: https://img.shields.io/website?style=for-the-badge&down_color=red&down_message=Offline&label=website&up_color=success&up_message=Online&url=https%3A%2F%2Fstockfishchess.org
+82 -67
View File
@@ -1,99 +1,105 @@
Contributors to Fishtest with >10,000 CPU hours, as of 2022-02-05. Contributors to Fishtest with >10,000 CPU hours, as of 2022-07-31.
Thank you! Thank you!
Username CPU Hours Games played Username CPU Hours Games played
------------------------------------------------------------------ ------------------------------------------------------------------
noobpwnftw 30730952 2158431735 noobpwnftw 33202707 2423743815
mlang 2729669 187335452 technologov 5064327 270208248
technologov 1696847 74478658 mlang 2963357 198937430
dew 1635640 97483012 dew 1677196 99717674
grandphish2 1062754 64955639 grandphish2 1231326 74551309
okrout 1102747 98977462
TueRens 925904 57404676
pemo 911980 35581261
tvijlbrief 795993 51894442 tvijlbrief 795993 51894442
okrout 773704 63465204 JojoM 774270 47311084
TueRens 766198 47770388
mibere 703840 46867607 mibere 703840 46867607
JojoM 703005 42689868 linrock 697283 18804969
pemo 634102 29868807 gvreuls 564284 36392236
linrock 626939 17408017 cw 515739 34775505
gvreuls 517442 33605006 fastgm 500949 30101898
cw 503905 33850487 oz 439015 31794460
fastgm 482847 29004732 CSU_Dynasty 438017 29369136
crunchy 427035 27344275 crunchy 427035 27344275
CSU_Dynasty 415864 28116776 ctoks 422671 27812261
ctoks 403102 26737127 bcross 363335 25108521
oz 357710 26490208 leszek 360149 22674005
bcross 331095 23165889 velislav 333325 21444360
Fisherman 327231 21829379 Fisherman 327231 21829379
velislav 321708 20729264 Dantist 292327 17951982
leszek 303654 19063973 mgrabiak 247220 16137378
Dantist 251015 15843226 nordlandia 226543 14601042
mgrabiak 231973 15162494 robal 224740 14314972
glinscott 217799 13780820 glinscott 217799 13780820
robal 213960 13665726 ncfish1 207751 13909639
nordlandia 211692 13484886 drabel 203884 13922680
drabel 200914 13755384 mhoram 200022 12533963
bking_US 198894 11876016 bking_US 198894 11876016
mhoram 180229 11610075 rpngn 191764 12236583
Thanar 179852 12365359 Thanar 179852 12365359
vdv 175544 9904472 vdv 175544 9904472
spams 157128 10319326 spams 157128 10319326
marrco 150300 9402229 marrco 150300 9402229
sqrt2 147963 9724586 sqrt2 147963 9724586
vdbergh 137429 8955089 vdbergh 137480 8958795
CoffeeOne 137100 5024116 CoffeeOne 137100 5024116
malala 136182 8002293 malala 136182 8002293
xoto 133759 9159372 xoto 133759 9159372
rpngn 131285 8657757 davar 128645 8367253
davar 122661 7996937 DesolatedDodo 124877 8056482
dsmith 122059 7570238 dsmith 122059 7570238
amicic 119659 7937885 amicic 119661 7938029
Data 113305 8220352 Data 113305 8220352
BrunoBanani 112960 7436849 BrunoBanani 112960 7436849
CypressChess 108321 7759588 CypressChess 108321 7759588
MaZePallas 102823 6633619 MaZePallas 102823 6633619
skiminki 102168 6778440
sterni1971 100532 5880772 sterni1971 100532 5880772
sunu 100167 7040199 sunu 100167 7040199
DesolatedDodo 99038 6414626
ElbertoOne 99028 7023771 ElbertoOne 99028 7023771
skiminki 98123 6478402 zeryl 96984 6162287
brabos 92118 6186135 brabos 92118 6186135
cuistot 90358 5351004 cuistot 91738 5447070
psk 89957 5984901 psk 89957 5984901
racerschmacer 85712 6119648 racerschmacer 85712 6119648
Vizvezdenec 83761 5344740 Vizvezdenec 83761 5344740
sschnee 83003 4840890 sschnee 83003 4840890
0x3C33 82614 5271253 0x3C33 82614 5271253
armo9494 82501 5806056
BRAVONE 81239 5054681 BRAVONE 81239 5054681
nssy 76497 5259388 nssy 76497 5259388
thirdlife 76478 1544524
Calis007 76457 4281018
jromang 75885 5230523
teddybaer 75125 5407666 teddybaer 75125 5407666
Pking_cda 73776 5293873 Pking_cda 73776 5293873
zeryl 73335 4774257 Wolfgang 72750 4538670
jromang 72192 5057715 sebastronomy 70784 1329428
solarlight 70517 5028306 solarlight 70517 5028306
dv8silencer 70287 3883992 dv8silencer 70287 3883992
Bobo1239 68515 4652287 Bobo1239 68515 4652287
yurikvelo 67651 4578970
manap 66273 4121774 manap 66273 4121774
tinker 64333 4268790 tinker 64333 4268790
yurikvelo 63371 4335060
qurashee 61208 3429862 qurashee 61208 3429862
robnjr 57262 4053117 robnjr 57262 4053117
Wolfgang 57014 3561352 megaman7de 57023 3525850
Freja 56938 3733019 Freja 56938 3733019
MaxKlaxxMiner 56279 3410158
ttruscott 56010 3680085 ttruscott 56010 3680085
rkl 55132 4164467 rkl 55132 4164467
renouve 53811 3501516 renouve 53811 3501516
tolkki963 53294 3354682
DMBK 52963 3933332
finfish 51360 3370515 finfish 51360 3370515
eva42 51272 3599691 eva42 51272 3599691
Calis007 51182 3131552 Spprtr 51139 3299983
eastorwest 51058 3451555 eastorwest 51058 3451555
rap 49985 3219146 rap 49985 3219146
pb00067 49727 3298270 pb00067 49727 3298270
Spprtr 48260 3141959
bigpen0r 47667 3336927 bigpen0r 47667 3336927
ronaldjerum 47654 3240695 ronaldjerum 47654 3240695
MaxKlaxxMiner 47584 2972142
biffhero 46564 3111352 biffhero 46564 3111352
megaman7de 45992 2952006
Fifis 45843 3088497 Fifis 45843 3088497
VoyagerOne 45476 3452465 VoyagerOne 45476 3452465
speedycpu 43842 3003273 speedycpu 43842 3003273
@@ -102,25 +108,27 @@ Antihistamine 41788 2761312
mhunt 41735 2691355 mhunt 41735 2691355
homyur 39893 2850481 homyur 39893 2850481
gri 39871 2515779 gri 39871 2515779
oryx 38867 2976992 oryx 39602 3024830
SC 37299 2731694 SC 37299 2731694
Garf 37213 2986270 Garf 37213 2986270
Dubslow 36714 2409254
csnodgrass 36207 2688994 csnodgrass 36207 2688994
jmdana 36157 2210661 jmdana 36157 2210661
markkulix 35994 2226860
strelock 34716 2074055 strelock 34716 2074055
EthanOConnor 33370 2090311 EthanOConnor 33370 2090311
slakovv 32915 2021889 slakovv 32915 2021889
armo9494 32129 2551682 gopeto 31078 2033362
tolkki963 32114 1932256
manapbk 30987 1810399 manapbk 30987 1810399
DMBK 30675 2383552
Prcuvu 30377 2170122 Prcuvu 30377 2170122
anst 30301 2190091 anst 30301 2190091
jkiiski 30136 1904470 jkiiski 30136 1904470
gopeto 29886 1979118
hyperbolic.tom 29840 2017394 hyperbolic.tom 29840 2017394
chuckstablers 29659 2093438 chuckstablers 29659 2093438
Pyafue 29650 1902349 Pyafue 29650 1902349
MarcusTullius 28611 1646671
spcc 28241 1821198
belzedar94 27935 1789106
OuaisBla 27636 1578800 OuaisBla 27636 1578800
chriswk 26902 1868317 chriswk 26902 1868317
achambord 26582 1767323 achambord 26582 1767323
@@ -129,15 +137,16 @@ yorkman 26193 1992080
SFTUser 25182 1675689 SFTUser 25182 1675689
nabildanial 24942 1519409 nabildanial 24942 1519409
Sharaf_DG 24765 1786697 Sharaf_DG 24765 1786697
ncfish1 24411 1520927 rodneyc 24375 1416258
rodneyc 24275 1410450 Ulysses 24017 1626140
agg177 23890 1395014 agg177 23890 1395014
belzedar94 23707 1593860
JanErik 23408 1703875 JanErik 23408 1703875
Ente 23403 1660988
kdave 23392 1630462
Isidor 23388 1680691 Isidor 23388 1680691
Norabor 23339 1602636 Norabor 23339 1602636
Ente 23093 1642458
cisco2015 22897 1762669 cisco2015 22897 1762669
Wencey 22573 1121406
Zirie 22542 1472937 Zirie 22542 1472937
team-oh 22272 1636708 team-oh 22272 1636708
MazeOfGalious 21978 1629593 MazeOfGalious 21978 1629593
@@ -146,17 +155,19 @@ ianh2105 21725 1632562
xor12 21628 1680365 xor12 21628 1680365
dex 21612 1467203 dex 21612 1467203
nesoneg 21494 1463031 nesoneg 21494 1463031
Roady 21323 1433822
sphinx 21211 1384728 sphinx 21211 1384728
user213718 21196 1397710
jjoshua2 21001 1423089 jjoshua2 21001 1423089
horst.prack 20878 1465656 horst.prack 20878 1465656
user213718 20783 1379584
0xB00B1ES 20590 1208666 0xB00B1ES 20590 1208666
j3corre 20405 941444 j3corre 20405 941444
Adrian.Schmidt123 20316 1281436 Adrian.Schmidt123 20316 1281436
jcAEie 20221 1504162
wei 19973 1745989 wei 19973 1745989
Roady 19848 1335928
rstoesser 19569 1293588 rstoesser 19569 1293588
eudhan 19274 1283717 eudhan 19274 1283717
fishtester 19145 1242668
vulcan 18871 1729392 vulcan 18871 1729392
jundery 18445 1115855 jundery 18445 1115855
iisiraider 18247 1101015 iisiraider 18247 1101015
@@ -164,24 +175,25 @@ ville 17883 1384026
chris 17698 1487385 chris 17698 1487385
purplefishies 17595 1092533 purplefishies 17595 1092533
dju 17353 978595 dju 17353 978595
kdave 17183 1242754 AndreasKrug 17191 1317997
DragonLord 17014 1162790 DragonLord 17014 1162790
thirdlife 16996 447356 Jopo12321 16966 944924
spcc 16932 1130940 GPUex 16744 1077826
fishtester 16644 1123000 xwziegtm 16608 1276372
Ulysses 16490 1184400
IgorLeMasson 16064 1147232 IgorLeMasson 16064 1147232
ako027ako 15671 1173203 ako027ako 15671 1173203
jsys14 15474 917092
Nikolay.IT 15154 1068349 Nikolay.IT 15154 1068349
Andrew Grant 15114 895539 Andrew Grant 15114 895539
scuzzi 15112 960373
OssumOpossum 14857 1007129 OssumOpossum 14857 1007129
Karby 14808 867120 Karby 14808 867120
AndreasKrug 14608 1152093
enedene 14476 905279 enedene 14476 905279
jsys14 14340 844792
bpfliegel 14298 884523 bpfliegel 14298 884523
mpx86 14019 759568 mpx86 14019 759568
jpulman 13982 870599 jpulman 13982 870599
Naven94 13879 811552
Karpovbot 13808 734276
crocogoat 13803 1117422 crocogoat 13803 1117422
joster 13794 950160 joster 13794 950160
Nesa92 13786 1114691 Nesa92 13786 1114691
@@ -189,42 +201,45 @@ mbeier 13650 1044928
Hjax 13535 915487 Hjax 13535 915487
Dark_wizzie 13422 1007152 Dark_wizzie 13422 1007152
Rudolphous 13244 883140 Rudolphous 13244 883140
MarcusTullius 13221 843169
Machariel 13010 863104 Machariel 13010 863104
mabichito 12903 749391 mabichito 12903 749391
thijsk 12886 722107 thijsk 12886 722107
AdrianSA 12860 804972 AdrianSA 12860 804972
infinigon 12807 937332 infinigon 12807 937332
Flopzee 12698 894821 Flopzee 12698 894821
pirt 12551 965597
fatmurphy 12547 853210 fatmurphy 12547 853210
scuzzi 12511 845761
SapphireBrand 12416 969604 SapphireBrand 12416 969604
modolief 12386 896470 modolief 12386 896470
Farseer 12249 694108 Farseer 12249 694108
pgontarz 12151 848794 pgontarz 12151 848794
stocky 11954 699440 stocky 11954 699440
mschmidt 11941 803401 mschmidt 11941 803401
Oakwen 11925 818865
MooTheCow 11851 772628
deflectooor 11642 565132
dbernier 11609 818636 dbernier 11609 818636
Skiff84 11604 602786
Maxim 11543 836024 Maxim 11543 836024
pirt 11516 894513
infinity 11470 727027 infinity 11470 727027
FormazChar 11430 856559
aga 11409 695071 aga 11409 695071
Jackfish 11403 750526
torbjo 11395 729145 torbjo 11395 729145
Thomas A. Anderson 11372 732094 Thomas A. Anderson 11372 732094
savage84 11358 670860 savage84 11358 670860
markkulix 11331 739098
FormazChar 11308 847735
d64 11263 789184 d64 11263 789184
MooTheCow 11237 720174 qoo_charly_cai 11127 671959
snicolet 11106 869170 snicolet 11106 869170
ali-al-zhrani 11098 768494 ali-al-zhrani 11098 768494
whelanh 11067 235676 whelanh 11067 235676
basepi 10637 744851 basepi 10637 744851
Cubox 10621 826448 Cubox 10621 826448
Alb11747 10558 689794
michaelrpg 10509 739239 michaelrpg 10509 739239
OIVAS7572 10420 995586 OIVAS7572 10420 995586
Garruk 10343 704723
dzjp 10343 732529 dzjp 10343 732529
Garruk 10332 703905
ols 10259 570669 ols 10259 570669
lbraesch 10252 647825 lbraesch 10252 647825
Jackfish 10098 682338 Karmatron 10195 661432
+53 -28
View File
@@ -117,7 +117,7 @@ ifeq ($(ARCH), $(filter $(ARCH), \
x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \ x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \
x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \
x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \ x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \
armv7 armv7-neon armv8 apple-silicon general-64 general-32)) armv7 armv7-neon armv8 apple-silicon general-64 general-32 riscv64))
SUPPORTED_ARCH=true SUPPORTED_ARCH=true
else else
SUPPORTED_ARCH=false SUPPORTED_ARCH=false
@@ -340,16 +340,26 @@ ifeq ($(findstring e2k,$(ARCH)),e2k)
popcnt = yes popcnt = yes
endif endif
ifeq ($(ARCH),riscv64)
arch = riscv64
endif endif
endif
### ========================================================================== ### ==========================================================================
### Section 3. Low-level Configuration ### Section 3. Low-level Configuration
### ========================================================================== ### ==========================================================================
### 3.1 Selecting compiler (default = gcc) ### 3.1 Selecting compiler (default = gcc)
CXXFLAGS += -Wall -Wcast-qual -fno-exceptions -std=c++17 $(EXTRACXXFLAGS) ifeq ($(MAKELEVEL),0)
DEPENDFLAGS += -std=c++17 export ENV_CXXFLAGS := $(CXXFLAGS)
LDFLAGS += $(EXTRALDFLAGS) export ENV_DEPENDFLAGS := $(DEPENDFLAGS)
export ENV_LDFLAGS := $(LDFLAGS)
endif
CXXFLAGS = $(ENV_CXXFLAGS) -Wall -Wcast-qual -fno-exceptions -std=c++17 $(EXTRACXXFLAGS)
DEPENDFLAGS = $(ENV_DEPENDFLAGS) -std=c++17
LDFLAGS = $(ENV_LDFLAGS) $(EXTRALDFLAGS)
ifeq ($(COMP),) ifeq ($(COMP),)
COMP=gcc COMP=gcc
@@ -360,11 +370,14 @@ ifeq ($(COMP),gcc)
CXX=g++ CXX=g++
CXXFLAGS += -pedantic -Wextra -Wshadow CXXFLAGS += -pedantic -Wextra -Wshadow
ifeq ($(arch),$(filter $(arch),armv7 armv8)) ifeq ($(arch),$(filter $(arch),armv7 armv8 riscv64))
ifeq ($(OS),Android) ifeq ($(OS),Android)
CXXFLAGS += -m$(bits) CXXFLAGS += -m$(bits)
LDFLAGS += -m$(bits) LDFLAGS += -m$(bits)
endif endif
ifeq ($(ARCH),riscv64)
CXXFLAGS += -latomic
endif
else else
CXXFLAGS += -m$(bits) CXXFLAGS += -m$(bits)
LDFLAGS += -m$(bits) LDFLAGS += -m$(bits)
@@ -425,11 +438,14 @@ ifeq ($(COMP),clang)
endif endif
endif endif
ifeq ($(arch),$(filter $(arch),armv7 armv8)) ifeq ($(arch),$(filter $(arch),armv7 armv8 riscv64))
ifeq ($(OS),Android) ifeq ($(OS),Android)
CXXFLAGS += -m$(bits) CXXFLAGS += -m$(bits)
LDFLAGS += -m$(bits) LDFLAGS += -m$(bits)
endif endif
ifeq ($(ARCH),riscv64)
CXXFLAGS += -latomic
endif
else else
CXXFLAGS += -m$(bits) CXXFLAGS += -m$(bits)
LDFLAGS += -m$(bits) LDFLAGS += -m$(bits)
@@ -589,7 +605,7 @@ endif
ifeq ($(avx2),yes) ifeq ($(avx2),yes)
CXXFLAGS += -DUSE_AVX2 CXXFLAGS += -DUSE_AVX2
ifeq ($(comp),$(filter $(comp),gcc clang mingw)) ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -mavx2 CXXFLAGS += -mavx2 -mbmi
endif endif
endif endif
@@ -674,7 +690,7 @@ endif
ifeq ($(optimize),yes) ifeq ($(optimize),yes)
ifeq ($(debug), no) ifeq ($(debug), no)
ifeq ($(comp),clang) ifeq ($(comp),clang)
CXXFLAGS += -flto CXXFLAGS += -flto=full
ifeq ($(target_windows),yes) ifeq ($(target_windows),yes)
CXXFLAGS += -fuse-ld=lld CXXFLAGS += -fuse-ld=lld
endif endif
@@ -684,21 +700,19 @@ ifeq ($(debug), no)
# GCC on some systems. # GCC on some systems.
else ifeq ($(comp),gcc) else ifeq ($(comp),gcc)
ifeq ($(gccisclang),) ifeq ($(gccisclang),)
CXXFLAGS += -flto CXXFLAGS += -flto -flto-partition=one
LDFLAGS += $(CXXFLAGS) -flto=jobserver LDFLAGS += $(CXXFLAGS) -flto=jobserver
else else
CXXFLAGS += -flto CXXFLAGS += -flto=full
LDFLAGS += $(CXXFLAGS) LDFLAGS += $(CXXFLAGS)
endif endif
# To use LTO and static linking on Windows, # To use LTO and static linking on Windows,
# the tool chain requires gcc version 10.1 or later. # the tool chain requires gcc version 10.1 or later.
else ifeq ($(comp),mingw) else ifeq ($(comp),mingw)
ifneq ($(arch),i386) CXXFLAGS += -flto -flto-partition=one
CXXFLAGS += -flto
LDFLAGS += $(CXXFLAGS) -save-temps LDFLAGS += $(CXXFLAGS) -save-temps
endif endif
endif
endif endif
endif endif
@@ -764,6 +778,7 @@ help:
@echo "apple-silicon > Apple silicon ARM64" @echo "apple-silicon > Apple silicon ARM64"
@echo "general-64 > unspecified 64-bit" @echo "general-64 > unspecified 64-bit"
@echo "general-32 > unspecified 32-bit" @echo "general-32 > unspecified 32-bit"
@echo "riscv64 > RISC-V 64-bit"
@echo "" @echo ""
@echo "Supported compilers:" @echo "Supported compilers:"
@echo "" @echo ""
@@ -832,25 +847,35 @@ clean: objclean profileclean
net: net:
$(eval nnuenet := $(shell grep EvalFileDefaultName evaluate.h | grep define | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/')) $(eval nnuenet := $(shell grep EvalFileDefaultName evaluate.h | grep define | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/'))
@echo "Default net: $(nnuenet)" @echo "Default net: $(nnuenet)"
$(eval nnuedownloadurl := https://tests.stockfishchess.org/api/nn/$(nnuenet)) $(eval nnuedownloadurl1 := https://tests.stockfishchess.org/api/nn/$(nnuenet))
$(eval nnuedownloadurl2 := https://github.com/official-stockfish/networks/raw/master/$(nnuenet))
$(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi)) $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi))
@if test -f "$(nnuenet)"; then \ @if [ "x$(curl_or_wget)" = "x" ]; then \
echo "Already available."; \
else \
if [ "x$(curl_or_wget)" = "x" ]; then \
echo "Automatic download failed: neither curl nor wget is installed. Install one of these tools or download the net manually"; exit 1; \ echo "Automatic download failed: neither curl nor wget is installed. Install one of these tools or download the net manually"; exit 1; \
else \ fi
echo "Downloading $(nnuedownloadurl)"; $(curl_or_wget) $(nnuedownloadurl) > $(nnuenet);\
fi; \
fi;
$(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi)) $(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi))
@if [ "x$(shasum_command)" != "x" ]; then \ @if [ "x$(shasum_command)" = "x" ]; then \
if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \
echo "Failed download or $(nnuenet) corrupted, please delete!"; exit 1; \
fi \
else \
echo "shasum / sha256sum not found, skipping net validation"; \ echo "shasum / sha256sum not found, skipping net validation"; \
fi fi
@for nnuedownloadurl in "$(nnuedownloadurl1)" "$(nnuedownloadurl2)"; do \
if test -f "$(nnuenet)"; then \
echo "$(nnuenet) available."; \
else \
if [ "x$(curl_or_wget)" != "x" ]; then \
echo "Downloading $${nnuedownloadurl}"; $(curl_or_wget) $${nnuedownloadurl} > $(nnuenet);\
fi; \
fi; \
if [ "x$(shasum_command)" != "x" ]; then \
if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \
echo "Removing failed download"; rm -f $(nnuenet); \
else \
echo "Network validated"; break; \
fi; \
fi; \
done
@if ! test -f "$(nnuenet)"; then \
echo "Failed to download $(nnuenet)."; \
fi
# clean binaries and objects # clean binaries and objects
objclean: objclean:
@@ -914,7 +939,7 @@ config-sanity: net
@test "$(SUPPORTED_ARCH)" = "true" @test "$(SUPPORTED_ARCH)" = "true"
@test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "e2k" || \ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "e2k" || \
test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" || test "$(arch)" = "riscv64"
@test "$(bits)" = "32" || test "$(bits)" = "64" @test "$(bits)" = "32" || test "$(bits)" = "64"
@test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
@test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no"
+32 -57
View File
@@ -202,12 +202,12 @@ namespace {
constexpr Value SpaceThreshold = Value(11551); constexpr Value SpaceThreshold = Value(11551);
// KingAttackWeights[PieceType] contains king attack weights by piece type // KingAttackWeights[PieceType] contains king attack weights by piece type
constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 76, 46, 45, 14 };
// SafeCheck[PieceType][single/multiple] contains safe check bonus by piece type, // SafeCheck[PieceType][single/multiple] contains safe check bonus by piece type,
// higher if multiple safe checks are possible for that piece type. // higher if multiple safe checks are possible for that piece type.
constexpr int SafeCheck[][2] = { constexpr int SafeCheck[][2] = {
{}, {}, {803, 1292}, {639, 974}, {1087, 1878}, {759, 1132} {}, {}, {805, 1292}, {650, 984}, {1071, 1886}, {730, 1128}
}; };
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
@@ -1046,82 +1046,58 @@ make_v:
return v; return v;
} }
/// Fisher Random Chess: correction for cornered bishops, to fix chess960 play with NNUE
Value fix_FRC(const Position& pos) {
constexpr Bitboard Corners = 1ULL << SQ_A1 | 1ULL << SQ_H1 | 1ULL << SQ_A8 | 1ULL << SQ_H8;
if (!(pos.pieces(BISHOP) & Corners))
return VALUE_ZERO;
int correction = 0;
if ( pos.piece_on(SQ_A1) == W_BISHOP
&& pos.piece_on(SQ_B2) == W_PAWN)
correction -= CorneredBishop;
if ( pos.piece_on(SQ_H1) == W_BISHOP
&& pos.piece_on(SQ_G2) == W_PAWN)
correction -= CorneredBishop;
if ( pos.piece_on(SQ_A8) == B_BISHOP
&& pos.piece_on(SQ_B7) == B_PAWN)
correction += CorneredBishop;
if ( pos.piece_on(SQ_H8) == B_BISHOP
&& pos.piece_on(SQ_G7) == B_PAWN)
correction += CorneredBishop;
return pos.side_to_move() == WHITE ? Value(3 * correction)
: -Value(3 * correction);
}
} // namespace Eval } // namespace Eval
/// evaluate() is the evaluator for the outer world. It returns a static /// evaluate() is the evaluator for the outer world. It returns a static
/// evaluation of the position from the point of view of the side to move. /// evaluation of the position from the point of view of the side to move.
Value Eval::evaluate(const Position& pos) { Value Eval::evaluate(const Position& pos, int* complexity) {
Value v; Value v;
bool useClassical = false; Value psq = pos.psq_eg_stm();
// Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, // We use the much less accurate but faster Classical eval when the NNUE
// but we switch to NNUE during long shuffling or with high material on the board. // option is set to false. Otherwise we use the NNUE eval unless the
if ( !useNNUE // PSQ advantage is decisive and several pieces remain. (~3 Elo)
|| ((pos.this_thread()->depth > 9 || pos.count<ALL_PIECES>() > 7) && bool useClassical = !useNNUE || (pos.count<ALL_PIECES>() > 7 && abs(psq) > 1760);
abs(eg_value(pos.psq_score())) * 5 > (856 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count())))
{
v = Evaluation<NO_TRACE>(pos).value(); // classical
useClassical = abs(v) >= 297;
}
// If result of a classical evaluation is much lower than threshold fall back to NNUE if (useClassical)
if (useNNUE && !useClassical) v = Evaluation<NO_TRACE>(pos).value();
else
{ {
Value nnue = NNUE::evaluate(pos, true); // NNUE int nnueComplexity;
int scale = 1036 + 20 * pos.non_pawn_material() / 1024; int scale = 1064 + 106 * pos.non_pawn_material() / 5120;
Color stm = pos.side_to_move(); Color stm = pos.side_to_move();
Value optimism = pos.this_thread()->optimism[stm]; Value optimism = pos.this_thread()->optimism[stm];
Value psq = (stm == WHITE ? 1 : -1) * eg_value(pos.psq_score());
int complexity = 35 * abs(nnue - psq) / 256;
optimism = optimism * (44 + complexity) / 31; Value nnue = NNUE::evaluate(pos, true, &nnueComplexity);
v = (nnue + optimism) * scale / 1024 - optimism;
if (pos.is_chess960()) // Blend nnue complexity with (semi)classical complexity
v += fix_FRC(pos); nnueComplexity = ( 416 * nnueComplexity
+ 424 * abs(psq - nnue)
+ (optimism > 0 ? int(optimism) * int(psq - nnue) : 0)
) / 1024;
// Return hybrid NNUE complexity to caller
if (complexity)
*complexity = nnueComplexity;
optimism = optimism * (269 + nnueComplexity) / 256;
v = (nnue * scale + optimism * (scale - 754)) / 1024;
} }
// Damp down the evaluation linearly when shuffling // Damp down the evaluation linearly when shuffling
v = v * (207 - pos.rule50_count()) / 207; v = v * (195 - pos.rule50_count()) / 211;
// Guarantee evaluation does not hit the tablebase range // Guarantee evaluation does not hit the tablebase range
v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
// When not using NNUE, return classical complexity to caller
if (complexity && (!useNNUE || useClassical))
*complexity = abs(v - psq);
return v; return v;
} }
@@ -1143,7 +1119,6 @@ std::string Eval::trace(Position& pos) {
std::memset(scores, 0, sizeof(scores)); std::memset(scores, 0, sizeof(scores));
// Reset any global variable used in eval // Reset any global variable used in eval
pos.this_thread()->depth = 0;
pos.this_thread()->trend = SCORE_ZERO; pos.this_thread()->trend = SCORE_ZERO;
pos.this_thread()->bestValue = VALUE_ZERO; pos.this_thread()->bestValue = VALUE_ZERO;
pos.this_thread()->optimism[WHITE] = VALUE_ZERO; pos.this_thread()->optimism[WHITE] = VALUE_ZERO;
+3 -3
View File
@@ -31,7 +31,7 @@ class Position;
namespace Eval { namespace Eval {
std::string trace(Position& pos); std::string trace(Position& pos);
Value evaluate(const Position& pos); Value evaluate(const Position& pos, int* complexity = nullptr);
extern bool useNNUE; extern bool useNNUE;
extern std::string currentEvalFileName; extern std::string currentEvalFileName;
@@ -39,12 +39,12 @@ namespace Eval {
// The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue
// for the build process (profile-build and fishtest) to work. Do not change the // for the build process (profile-build and fishtest) to work. Do not change the
// name of the macro, as it is used in the Makefile. // name of the macro, as it is used in the Makefile.
#define EvalFileDefaultName "nn-6877cd24400e.nnue" #define EvalFileDefaultName "nn-ad9b42354671.nnue"
namespace NNUE { namespace NNUE {
std::string trace(Position& pos); std::string trace(Position& pos);
Value evaluate(const Position& pos, bool adjusted = false); Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr);
void init(); void init();
void verify(); void verify();
+2 -4
View File
@@ -378,10 +378,9 @@ void std_aligned_free(void* ptr) {
#if defined(_WIN32) #if defined(_WIN32)
static void* aligned_large_pages_alloc_windows(size_t allocSize) { static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize) {
#if !defined(_WIN64) #if !defined(_WIN64)
(void)allocSize; // suppress unused-parameter compiler warning
return nullptr; return nullptr;
#else #else
@@ -626,8 +625,7 @@ string argv0; // path+name of the executable binary, as given by argv
string binaryDirectory; // path of the executable directory string binaryDirectory; // path of the executable directory
string workingDirectory; // path of the working directory string workingDirectory; // path of the working directory
void init(int argc, char* argv[]) { void init([[maybe_unused]] int argc, char* argv[]) {
(void)argc;
string pathSeparator; string pathSeparator;
// extract the path+name of the executable binary // extract the path+name of the executable binary
-40
View File
@@ -116,56 +116,16 @@ class ValueList {
public: public:
std::size_t size() const { return size_; } std::size_t size() const { return size_; }
void resize(std::size_t newSize) { size_ = newSize; }
void push_back(const T& value) { values_[size_++] = value; } void push_back(const T& value) { values_[size_++] = value; }
T& operator[](std::size_t index) { return values_[index]; }
T* begin() { return values_; }
T* end() { return values_ + size_; }
const T& operator[](std::size_t index) const { return values_[index]; }
const T* begin() const { return values_; } const T* begin() const { return values_; }
const T* end() const { return values_ + size_; } const T* end() const { return values_ + size_; }
void swap(ValueList& other) {
const std::size_t maxSize = std::max(size_, other.size_);
for (std::size_t i = 0; i < maxSize; ++i) {
std::swap(values_[i], other.values_[i]);
}
std::swap(size_, other.size_);
}
private: private:
T values_[MaxSize]; T values_[MaxSize];
std::size_t size_ = 0; std::size_t size_ = 0;
}; };
/// sigmoid(t, x0, y0, C, P, Q) implements a sigmoid-like function using only integers,
/// with the following properties:
///
/// - sigmoid is centered in (x0, y0)
/// - sigmoid has amplitude [-P/Q , P/Q] instead of [-1 , +1]
/// - limit is (y0 - P/Q) when t tends to -infinity
/// - limit is (y0 + P/Q) when t tends to +infinity
/// - the slope can be adjusted using C > 0, smaller C giving a steeper sigmoid
/// - the slope of the sigmoid when t = x0 is P/(Q*C)
/// - sigmoid is increasing with t when P > 0 and Q > 0
/// - to get a decreasing sigmoid, change sign of P
/// - mean value of the sigmoid is y0
///
/// Use <https://www.desmos.com/calculator/jhh83sqq92> to draw the sigmoid
inline int64_t sigmoid(int64_t t, int64_t x0,
int64_t y0,
int64_t C,
int64_t P,
int64_t Q)
{
assert(C > 0);
assert(Q != 0);
return y0 + P * (t-x0) / (Q * (std::abs(t-x0) + C)) ;
}
/// xorshift64star Pseudo-Random Number Generator /// xorshift64star Pseudo-Random Number Generator
/// This class is based on original code written and dedicated /// This class is based on original code written and dedicated
/// to the public domain by Sebastiano Vigna (2014). /// to the public domain by Sebastiano Vigna (2014).
+12 -22
View File
@@ -69,6 +69,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
stage = (pos.checkers() ? EVASION_TT : MAIN_TT) + stage = (pos.checkers() ? EVASION_TT : MAIN_TT) +
!(ttm && pos.pseudo_legal(ttm)); !(ttm && pos.pseudo_legal(ttm));
threatenedPieces = 0;
} }
/// MovePicker constructor for quiescence search /// MovePicker constructor for quiescence search
@@ -82,7 +83,6 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) + stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) +
!( ttm !( ttm
&& (pos.checkers() || depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare)
&& pos.pseudo_legal(ttm)); && pos.pseudo_legal(ttm));
} }
@@ -106,30 +106,20 @@ void MovePicker::score() {
static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
Bitboard threatened, threatenedByPawn, threatenedByMinor, threatenedByRook; [[maybe_unused]] Bitboard threatenedByPawn, threatenedByMinor, threatenedByRook;
if constexpr (Type == QUIETS) if constexpr (Type == QUIETS)
{ {
Color us = pos.side_to_move(); Color us = pos.side_to_move();
// squares threatened by pawns
threatenedByPawn = pos.attacks_by<PAWN>(~us); threatenedByPawn = pos.attacks_by<PAWN>(~us);
// squares threatened by minors or pawns
threatenedByMinor = pos.attacks_by<KNIGHT>(~us) | pos.attacks_by<BISHOP>(~us) | threatenedByPawn; threatenedByMinor = pos.attacks_by<KNIGHT>(~us) | pos.attacks_by<BISHOP>(~us) | threatenedByPawn;
// squares threatened by rooks, minors or pawns
threatenedByRook = pos.attacks_by<ROOK>(~us) | threatenedByMinor; threatenedByRook = pos.attacks_by<ROOK>(~us) | threatenedByMinor;
// pieces threatened by pieces of lesser material value // Pieces threatened by pieces of lesser material value
threatened = (pos.pieces(us, QUEEN) & threatenedByRook) threatenedPieces = (pos.pieces(us, QUEEN) & threatenedByRook)
| (pos.pieces(us, ROOK) & threatenedByMinor) | (pos.pieces(us, ROOK) & threatenedByMinor)
| (pos.pieces(us, KNIGHT, BISHOP) & threatenedByPawn); | (pos.pieces(us, KNIGHT, BISHOP) & threatenedByPawn);
} }
else
{
// Silence unused variable warnings
(void) threatened;
(void) threatenedByPawn;
(void) threatenedByMinor;
(void) threatenedByRook;
}
for (auto& m : *this) for (auto& m : *this)
if constexpr (Type == CAPTURES) if constexpr (Type == CAPTURES)
@@ -137,27 +127,27 @@ void MovePicker::score() {
+ (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]; + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))];
else if constexpr (Type == QUIETS) else if constexpr (Type == QUIETS)
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)]
+ 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
+ (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
+ (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]
+ (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)]
+ (threatened & from_sq(m) ? + (threatenedPieces & from_sq(m) ?
(type_of(pos.moved_piece(m)) == QUEEN && !(to_sq(m) & threatenedByRook) ? 50000 (type_of(pos.moved_piece(m)) == QUEEN && !(to_sq(m) & threatenedByRook) ? 50000
: type_of(pos.moved_piece(m)) == ROOK && !(to_sq(m) & threatenedByMinor) ? 25000 : type_of(pos.moved_piece(m)) == ROOK && !(to_sq(m) & threatenedByMinor) ? 25000
: !(to_sq(m) & threatenedByPawn) ? 15000 : !(to_sq(m) & threatenedByPawn) ? 15000
: 0) : 0)
: 0); : 0)
+ bool(pos.check_squares(type_of(pos.moved_piece(m))) & to_sq(m)) * 16384;
else // Type == EVASIONS else // Type == EVASIONS
{ {
if (pos.capture(m)) if (pos.capture(m))
m.value = PieceValue[MG][pos.piece_on(to_sq(m))] m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
- Value(type_of(pos.moved_piece(m))); - Value(type_of(pos.moved_piece(m)))
+ (1 << 28);
else else
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
+ 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)];
- (1 << 28);
} }
} }
+5 -1
View File
@@ -86,7 +86,8 @@ enum StatsType { NoCaptures, Captures };
/// unsuccessful during the current search, and is used for reduction and move /// unsuccessful during the current search, and is used for reduction and move
/// ordering decisions. It uses 2 tables (one for each color) indexed by /// ordering decisions. It uses 2 tables (one for each color) indexed by
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
typedef Stats<int16_t, 14365, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory; /// (~11 elo)
typedef Stats<int16_t, 7183, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
/// move, see www.chessprogramming.org/Countermove_Heuristic /// move, see www.chessprogramming.org/Countermove_Heuristic
@@ -101,6 +102,7 @@ typedef Stats<int16_t, 29952, PIECE_NB, SQUARE_NB> PieceToHistory;
/// ContinuationHistory is the combined history of a given pair of moves, usually /// ContinuationHistory is the combined history of a given pair of moves, usually
/// the current one given a previous one. The nested history table is based on /// the current one given a previous one. The nested history table is based on
/// PieceToHistory instead of ButterflyBoards. /// PieceToHistory instead of ButterflyBoards.
/// (~63 elo)
typedef Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB> ContinuationHistory; typedef Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB> ContinuationHistory;
@@ -129,6 +131,8 @@ public:
MovePicker(const Position&, Move, Value, Depth, const CapturePieceToHistory*); MovePicker(const Position&, Move, Value, Depth, const CapturePieceToHistory*);
Move next_move(bool skipQuiets = false); Move next_move(bool skipQuiets = false);
Bitboard threatenedPieces;
private: private:
template<PickType T, typename Pred> Move select(Pred); template<PickType T, typename Pred> Move select(Pred);
template<GenType> void score(); template<GenType> void score();
+6 -3
View File
@@ -137,13 +137,13 @@ namespace Stockfish::Eval::NNUE {
} }
// Evaluation function. Perform differential calculation. // Evaluation function. Perform differential calculation.
Value evaluate(const Position& pos, bool adjusted) { Value evaluate(const Position& pos, bool adjusted, int* complexity) {
// We manually align the arrays on the stack because with gcc < 9.3 // We manually align the arrays on the stack because with gcc < 9.3
// overaligning stack variables with alignas() doesn't work correctly. // overaligning stack variables with alignas() doesn't work correctly.
constexpr uint64_t alignment = CacheLineSize; constexpr uint64_t alignment = CacheLineSize;
int delta = 10 - pos.non_pawn_material() / 1515; int delta = 24 - pos.non_pawn_material() / 9560;
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
TransformedFeatureType transformedFeaturesUnaligned[ TransformedFeatureType transformedFeaturesUnaligned[
@@ -161,9 +161,12 @@ namespace Stockfish::Eval::NNUE {
const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket);
const auto positional = network[bucket]->propagate(transformedFeatures); const auto positional = network[bucket]->propagate(transformedFeatures);
if (complexity)
*complexity = abs(psqt - positional) / OutputScale;
// Give more value to positional evaluation when adjusted flag is set // Give more value to positional evaluation when adjusted flag is set
if (adjusted) if (adjusted)
return static_cast<Value>(((128 - delta) * psqt + (128 + delta) * positional) / 128 / OutputScale); return static_cast<Value>(((1024 - delta) * psqt + (1024 + delta) * positional) / (1024 * OutputScale));
else else
return static_cast<Value>((psqt + positional) / OutputScale); return static_cast<Value>((psqt + positional) / OutputScale);
} }
+16 -15
View File
@@ -24,50 +24,51 @@
namespace Stockfish::Eval::NNUE::Features { namespace Stockfish::Eval::NNUE::Features {
// Orient a square according to perspective (rotates by 180 for black)
inline Square HalfKAv2_hm::orient(Color perspective, Square s, Square ksq) {
return Square(int(s) ^ (bool(perspective) * SQ_A8) ^ ((file_of(ksq) < FILE_E) * SQ_H1));
}
// Index of a feature for a given king position and another piece on some square // Index of a feature for a given king position and another piece on some square
inline IndexType HalfKAv2_hm::make_index(Color perspective, Square s, Piece pc, Square ksq) { template<Color Perspective>
Square o_ksq = orient(perspective, ksq, ksq); inline IndexType HalfKAv2_hm::make_index(Square s, Piece pc, Square ksq) {
return IndexType(orient(perspective, s, ksq) + PieceSquareIndex[perspective][pc] + PS_NB * KingBuckets[o_ksq]); return IndexType((int(s) ^ OrientTBL[Perspective][ksq]) + PieceSquareIndex[Perspective][pc] + KingBuckets[Perspective][ksq]);
} }
// Get a list of indices for active features // Get a list of indices for active features
template<Color Perspective>
void HalfKAv2_hm::append_active_indices( void HalfKAv2_hm::append_active_indices(
const Position& pos, const Position& pos,
Color perspective,
IndexList& active IndexList& active
) { ) {
Square ksq = pos.square<KING>(perspective); Square ksq = pos.square<KING>(Perspective);
Bitboard bb = pos.pieces(); Bitboard bb = pos.pieces();
while (bb) while (bb)
{ {
Square s = pop_lsb(bb); Square s = pop_lsb(bb);
active.push_back(make_index(perspective, s, pos.piece_on(s), ksq)); active.push_back(make_index<Perspective>(s, pos.piece_on(s), ksq));
} }
} }
// Explicit template instantiations
template void HalfKAv2_hm::append_active_indices<WHITE>(const Position& pos, IndexList& active);
template void HalfKAv2_hm::append_active_indices<BLACK>(const Position& pos, IndexList& active);
// append_changed_indices() : get a list of indices for recently changed features // append_changed_indices() : get a list of indices for recently changed features
template<Color Perspective>
void HalfKAv2_hm::append_changed_indices( void HalfKAv2_hm::append_changed_indices(
Square ksq, Square ksq,
const DirtyPiece& dp, const DirtyPiece& dp,
Color perspective,
IndexList& removed, IndexList& removed,
IndexList& added IndexList& added
) { ) {
for (int i = 0; i < dp.dirty_num; ++i) { for (int i = 0; i < dp.dirty_num; ++i) {
if (dp.from[i] != SQ_NONE) if (dp.from[i] != SQ_NONE)
removed.push_back(make_index(perspective, dp.from[i], dp.piece[i], ksq)); removed.push_back(make_index<Perspective>(dp.from[i], dp.piece[i], ksq));
if (dp.to[i] != SQ_NONE) if (dp.to[i] != SQ_NONE)
added.push_back(make_index(perspective, dp.to[i], dp.piece[i], ksq)); added.push_back(make_index<Perspective>(dp.to[i], dp.piece[i], ksq));
} }
} }
// Explicit template instantiations
template void HalfKAv2_hm::append_changed_indices<WHITE>(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added);
template void HalfKAv2_hm::append_changed_indices<BLACK>(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added);
int HalfKAv2_hm::update_cost(const StateInfo* st) { int HalfKAv2_hm::update_cost(const StateInfo* st) {
return st->dirtyPiece.dirty_num; return st->dirtyPiece.dirty_num;
} }
+43 -15
View File
@@ -62,11 +62,9 @@ namespace Stockfish::Eval::NNUE::Features {
PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE } PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE }
}; };
// Orient a square according to perspective (rotates by 180 for black)
static Square orient(Color perspective, Square s, Square ksq);
// Index of a feature for a given king position and another piece on some square // Index of a feature for a given king position and another piece on some square
static IndexType make_index(Color perspective, Square s, Piece pc, Square ksq); template<Color Perspective>
static IndexType make_index(Square s, Piece pc, Square ksq);
public: public:
// Feature name // Feature name
@@ -79,15 +77,45 @@ namespace Stockfish::Eval::NNUE::Features {
static constexpr IndexType Dimensions = static constexpr IndexType Dimensions =
static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_NB) / 2; static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_NB) / 2;
static constexpr int KingBuckets[64] = { #define B(v) (v * PS_NB)
-1, -1, -1, -1, 31, 30, 29, 28, static constexpr int KingBuckets[COLOR_NB][SQUARE_NB] = {
-1, -1, -1, -1, 27, 26, 25, 24, { B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28),
-1, -1, -1, -1, 23, 22, 21, 20, B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24),
-1, -1, -1, -1, 19, 18, 17, 16, B(20), B(21), B(22), B(23), B(23), B(22), B(21), B(20),
-1, -1, -1, -1, 15, 14, 13, 12, B(16), B(17), B(18), B(19), B(19), B(18), B(17), B(16),
-1, -1, -1, -1, 11, 10, 9, 8, B(12), B(13), B(14), B(15), B(15), B(14), B(13), B(12),
-1, -1, -1, -1, 7, 6, 5, 4, B( 8), B( 9), B(10), B(11), B(11), B(10), B( 9), B( 8),
-1, -1, -1, -1, 3, 2, 1, 0 B( 4), B( 5), B( 6), B( 7), B( 7), B( 6), B( 5), B( 4),
B( 0), B( 1), B( 2), B( 3), B( 3), B( 2), B( 1), B( 0) },
{ B( 0), B( 1), B( 2), B( 3), B( 3), B( 2), B( 1), B( 0),
B( 4), B( 5), B( 6), B( 7), B( 7), B( 6), B( 5), B( 4),
B( 8), B( 9), B(10), B(11), B(11), B(10), B( 9), B( 8),
B(12), B(13), B(14), B(15), B(15), B(14), B(13), B(12),
B(16), B(17), B(18), B(19), B(19), B(18), B(17), B(16),
B(20), B(21), B(22), B(23), B(23), B(22), B(21), B(20),
B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24),
B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28) }
};
#undef B
// Orient a square according to perspective (rotates by 180 for black)
static constexpr int OrientTBL[COLOR_NB][SQUARE_NB] = {
{ SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1 },
{ SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8 }
}; };
// Maximum number of simultaneously active features. // Maximum number of simultaneously active features.
@@ -95,16 +123,16 @@ namespace Stockfish::Eval::NNUE::Features {
using IndexList = ValueList<IndexType, MaxActiveDimensions>; using IndexList = ValueList<IndexType, MaxActiveDimensions>;
// Get a list of indices for active features // Get a list of indices for active features
template<Color Perspective>
static void append_active_indices( static void append_active_indices(
const Position& pos, const Position& pos,
Color perspective,
IndexList& active); IndexList& active);
// Get a list of indices for recently changed features // Get a list of indices for recently changed features
template<Color Perspective>
static void append_changed_indices( static void append_changed_indices(
Square ksq, Square ksq,
const DirtyPiece& dp, const DirtyPiece& dp,
Color perspective,
IndexList& removed, IndexList& removed,
IndexList& added IndexList& added
); );
+11 -5
View File
@@ -25,7 +25,7 @@
#include <algorithm> #include <algorithm>
#include <type_traits> #include <type_traits>
#include "../nnue_common.h" #include "../nnue_common.h"
#include "../../simd.h" #include "simd.h"
/* /*
This file contains the definition for a fully connected layer (aka affine transform). This file contains the definition for a fully connected layer (aka affine transform).
@@ -151,9 +151,15 @@ namespace Stockfish::Eval::NNUE::Layers {
template <IndexType InDims, IndexType OutDims, typename Enabled = void> template <IndexType InDims, IndexType OutDims, typename Enabled = void>
class AffineTransform; class AffineTransform;
#if defined (USE_AVX512)
constexpr IndexType LargeInputSize = 2 * 64;
#else
constexpr IndexType LargeInputSize = std::numeric_limits<IndexType>::max();
#endif
// A specialization for large inputs. // A specialization for large inputs.
template <IndexType InDims, IndexType OutDims> template <IndexType InDims, IndexType OutDims>
class AffineTransform<InDims, OutDims, std::enable_if_t<(ceil_to_multiple<IndexType>(InDims, MaxSimdWidth) >= 2*64)>> { class AffineTransform<InDims, OutDims, std::enable_if_t<(ceil_to_multiple<IndexType>(InDims, MaxSimdWidth) >= LargeInputSize)>> {
public: public:
// Input/output type // Input/output type
using InputType = std::uint8_t; using InputType = std::uint8_t;
@@ -170,7 +176,7 @@ namespace Stockfish::Eval::NNUE::Layers {
using OutputBuffer = OutputType[PaddedOutputDimensions]; using OutputBuffer = OutputType[PaddedOutputDimensions];
static_assert(PaddedInputDimensions >= 128, "Something went wrong. This specialization should not have been chosen."); static_assert(PaddedInputDimensions >= LargeInputSize, "Something went wrong. This specialization should not have been chosen.");
#if defined (USE_AVX512) #if defined (USE_AVX512)
static constexpr const IndexType InputSimdWidth = 64; static constexpr const IndexType InputSimdWidth = 64;
@@ -369,7 +375,7 @@ namespace Stockfish::Eval::NNUE::Layers {
}; };
template <IndexType InDims, IndexType OutDims> template <IndexType InDims, IndexType OutDims>
class AffineTransform<InDims, OutDims, std::enable_if_t<(ceil_to_multiple<IndexType>(InDims, MaxSimdWidth) < 2*64)>> { class AffineTransform<InDims, OutDims, std::enable_if_t<(ceil_to_multiple<IndexType>(InDims, MaxSimdWidth) < LargeInputSize)>> {
public: public:
// Input/output type // Input/output type
// Input/output type // Input/output type
@@ -387,7 +393,7 @@ namespace Stockfish::Eval::NNUE::Layers {
using OutputBuffer = OutputType[PaddedOutputDimensions]; using OutputBuffer = OutputType[PaddedOutputDimensions];
static_assert(PaddedInputDimensions < 128, "Something went wrong. This specialization should not have been chosen."); static_assert(PaddedInputDimensions < LargeInputSize, "Something went wrong. This specialization should not have been chosen.");
#if defined (USE_SSSE3) #if defined (USE_SSSE3)
static constexpr const IndexType OutputSimdWidth = SimdWidth / 4; static constexpr const IndexType OutputSimdWidth = SimdWidth / 4;
+120
View File
@@ -0,0 +1,120 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Definition of layer ClippedReLU of NNUE evaluation function
#ifndef NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED
#define NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED
#include "../nnue_common.h"
namespace Stockfish::Eval::NNUE::Layers {
// Clipped ReLU
template <IndexType InDims>
class SqrClippedReLU {
public:
// Input/output type
using InputType = std::int32_t;
using OutputType = std::uint8_t;
// Number of input/output dimensions
static constexpr IndexType InputDimensions = InDims;
static constexpr IndexType OutputDimensions = InputDimensions;
static constexpr IndexType PaddedOutputDimensions =
ceil_to_multiple<IndexType>(OutputDimensions, 32);
using OutputBuffer = OutputType[PaddedOutputDimensions];
// Hash value embedded in the evaluation file
static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) {
std::uint32_t hashValue = 0x538D24C7u;
hashValue += prevHash;
return hashValue;
}
// Read network parameters
bool read_parameters(std::istream&) {
return true;
}
// Write network parameters
bool write_parameters(std::ostream&) const {
return true;
}
// Forward propagation
const OutputType* propagate(
const InputType* input, OutputType* output) const {
#if defined(USE_SSE2)
constexpr IndexType NumChunks = InputDimensions / 16;
#ifdef USE_SSE41
const __m128i Zero = _mm_setzero_si128();
#else
const __m128i k0x80s = _mm_set1_epi8(-128);
#endif
static_assert(WeightScaleBits == 6);
const auto in = reinterpret_cast<const __m128i*>(input);
const auto out = reinterpret_cast<__m128i*>(output);
for (IndexType i = 0; i < NumChunks; ++i) {
__m128i words0 = _mm_packs_epi32(
_mm_load_si128(&in[i * 4 + 0]),
_mm_load_si128(&in[i * 4 + 1]));
__m128i words1 = _mm_packs_epi32(
_mm_load_si128(&in[i * 4 + 2]),
_mm_load_si128(&in[i * 4 + 3]));
// Not sure if
words0 = _mm_srli_epi16(_mm_mulhi_epi16(words0, words0), 3);
words1 = _mm_srli_epi16(_mm_mulhi_epi16(words1, words1), 3);
const __m128i packedbytes = _mm_packs_epi16(words0, words1);
_mm_store_si128(&out[i],
#ifdef USE_SSE41
_mm_max_epi8(packedbytes, Zero)
#else
_mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)
#endif
);
}
constexpr IndexType Start = NumChunks * 16;
#else
constexpr IndexType Start = 0;
#endif
for (IndexType i = Start; i < InputDimensions; ++i) {
output[i] = static_cast<OutputType>(
// realy should be /127 but we need to make it fast
// needs to be accounted for in the trainer
std::max(0ll, std::min(127ll, (((long long)input[i] * input[i]) >> (2 * WeightScaleBits)) / 128)));
}
return output;
}
};
} // namespace Stockfish::Eval::NNUE::Layers
#endif // NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED
+7 -2
View File
@@ -29,6 +29,7 @@
#include "layers/affine_transform.h" #include "layers/affine_transform.h"
#include "layers/clipped_relu.h" #include "layers/clipped_relu.h"
#include "layers/sqr_clipped_relu.h"
#include "../misc.h" #include "../misc.h"
@@ -48,8 +49,9 @@ struct Network
static constexpr int FC_1_OUTPUTS = 32; static constexpr int FC_1_OUTPUTS = 32;
Layers::AffineTransform<TransformedFeatureDimensions, FC_0_OUTPUTS + 1> fc_0; Layers::AffineTransform<TransformedFeatureDimensions, FC_0_OUTPUTS + 1> fc_0;
Layers::SqrClippedReLU<FC_0_OUTPUTS + 1> ac_sqr_0;
Layers::ClippedReLU<FC_0_OUTPUTS + 1> ac_0; Layers::ClippedReLU<FC_0_OUTPUTS + 1> ac_0;
Layers::AffineTransform<FC_0_OUTPUTS, FC_1_OUTPUTS> fc_1; Layers::AffineTransform<FC_0_OUTPUTS * 2, FC_1_OUTPUTS> fc_1;
Layers::ClippedReLU<FC_1_OUTPUTS> ac_1; Layers::ClippedReLU<FC_1_OUTPUTS> ac_1;
Layers::AffineTransform<FC_1_OUTPUTS, 1> fc_2; Layers::AffineTransform<FC_1_OUTPUTS, 1> fc_2;
@@ -93,6 +95,7 @@ struct Network
struct alignas(CacheLineSize) Buffer struct alignas(CacheLineSize) Buffer
{ {
alignas(CacheLineSize) decltype(fc_0)::OutputBuffer fc_0_out; alignas(CacheLineSize) decltype(fc_0)::OutputBuffer fc_0_out;
alignas(CacheLineSize) decltype(ac_sqr_0)::OutputType ac_sqr_0_out[ceil_to_multiple<IndexType>(FC_0_OUTPUTS * 2, 32)];
alignas(CacheLineSize) decltype(ac_0)::OutputBuffer ac_0_out; alignas(CacheLineSize) decltype(ac_0)::OutputBuffer ac_0_out;
alignas(CacheLineSize) decltype(fc_1)::OutputBuffer fc_1_out; alignas(CacheLineSize) decltype(fc_1)::OutputBuffer fc_1_out;
alignas(CacheLineSize) decltype(ac_1)::OutputBuffer ac_1_out; alignas(CacheLineSize) decltype(ac_1)::OutputBuffer ac_1_out;
@@ -114,8 +117,10 @@ struct Network
#endif #endif
fc_0.propagate(transformedFeatures, buffer.fc_0_out); fc_0.propagate(transformedFeatures, buffer.fc_0_out);
ac_sqr_0.propagate(buffer.fc_0_out, buffer.ac_sqr_0_out);
ac_0.propagate(buffer.fc_0_out, buffer.ac_0_out); ac_0.propagate(buffer.fc_0_out, buffer.ac_0_out);
fc_1.propagate(buffer.ac_0_out, buffer.fc_1_out); std::memcpy(buffer.ac_sqr_0_out + FC_0_OUTPUTS, buffer.ac_0_out, FC_0_OUTPUTS * sizeof(decltype(ac_0)::OutputType));
fc_1.propagate(buffer.ac_sqr_0_out, buffer.fc_1_out);
ac_1.propagate(buffer.fc_1_out, buffer.ac_1_out); ac_1.propagate(buffer.fc_1_out, buffer.ac_1_out);
fc_2.propagate(buffer.ac_1_out, buffer.fc_2_out); fc_2.propagate(buffer.ac_1_out, buffer.fc_2_out);
+33 -32
View File
@@ -271,8 +271,8 @@ namespace Stockfish::Eval::NNUE {
// Convert input features // Convert input features
std::int32_t transform(const Position& pos, OutputType* output, int bucket) const { std::int32_t transform(const Position& pos, OutputType* output, int bucket) const {
update_accumulator(pos, WHITE); update_accumulator<WHITE>(pos);
update_accumulator(pos, BLACK); update_accumulator<BLACK>(pos);
const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()};
const auto& accumulation = pos.state()->accumulator.accumulation; const auto& accumulation = pos.state()->accumulator.accumulation;
@@ -338,7 +338,8 @@ namespace Stockfish::Eval::NNUE {
private: private:
void update_accumulator(const Position& pos, const Color perspective) const { template<Color Perspective>
void update_accumulator(const Position& pos) const {
// The size must be enough to contain the largest possible update. // The size must be enough to contain the largest possible update.
// That might depend on the feature set and generally relies on the // That might depend on the feature set and generally relies on the
@@ -356,18 +357,18 @@ namespace Stockfish::Eval::NNUE {
// of the estimated gain in terms of features to be added/subtracted. // of the estimated gain in terms of features to be added/subtracted.
StateInfo *st = pos.state(), *next = nullptr; StateInfo *st = pos.state(), *next = nullptr;
int gain = FeatureSet::refresh_cost(pos); int gain = FeatureSet::refresh_cost(pos);
while (st->previous && !st->accumulator.computed[perspective]) while (st->previous && !st->accumulator.computed[Perspective])
{ {
// This governs when a full feature refresh is needed and how many // This governs when a full feature refresh is needed and how many
// updates are better than just one full refresh. // updates are better than just one full refresh.
if ( FeatureSet::requires_refresh(st, perspective) if ( FeatureSet::requires_refresh(st, Perspective)
|| (gain -= FeatureSet::update_cost(st) + 1) < 0) || (gain -= FeatureSet::update_cost(st) + 1) < 0)
break; break;
next = st; next = st;
st = st->previous; st = st->previous;
} }
if (st->accumulator.computed[perspective]) if (st->accumulator.computed[Perspective])
{ {
if (next == nullptr) if (next == nullptr)
return; return;
@@ -376,17 +377,17 @@ namespace Stockfish::Eval::NNUE {
// accumulator. Then, we update the current accumulator (pos.state()). // accumulator. Then, we update the current accumulator (pos.state()).
// Gather all features to be updated. // Gather all features to be updated.
const Square ksq = pos.square<KING>(perspective); const Square ksq = pos.square<KING>(Perspective);
FeatureSet::IndexList removed[2], added[2]; FeatureSet::IndexList removed[2], added[2];
FeatureSet::append_changed_indices( FeatureSet::append_changed_indices<Perspective>(
ksq, next->dirtyPiece, perspective, removed[0], added[0]); ksq, next->dirtyPiece, removed[0], added[0]);
for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous) for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous)
FeatureSet::append_changed_indices( FeatureSet::append_changed_indices<Perspective>(
ksq, st2->dirtyPiece, perspective, removed[1], added[1]); ksq, st2->dirtyPiece, removed[1], added[1]);
// Mark the accumulators as computed. // Mark the accumulators as computed.
next->accumulator.computed[perspective] = true; next->accumulator.computed[Perspective] = true;
pos.state()->accumulator.computed[perspective] = true; pos.state()->accumulator.computed[Perspective] = true;
// Now update the accumulators listed in states_to_update[], where the last element is a sentinel. // Now update the accumulators listed in states_to_update[], where the last element is a sentinel.
StateInfo *states_to_update[3] = StateInfo *states_to_update[3] =
@@ -396,7 +397,7 @@ namespace Stockfish::Eval::NNUE {
{ {
// Load accumulator // Load accumulator
auto accTile = reinterpret_cast<vec_t*>( auto accTile = reinterpret_cast<vec_t*>(
&st->accumulator.accumulation[perspective][j * TileHeight]); &st->accumulator.accumulation[Perspective][j * TileHeight]);
for (IndexType k = 0; k < NumRegs; ++k) for (IndexType k = 0; k < NumRegs; ++k)
acc[k] = vec_load(&accTile[k]); acc[k] = vec_load(&accTile[k]);
@@ -422,7 +423,7 @@ namespace Stockfish::Eval::NNUE {
// Store accumulator // Store accumulator
accTile = reinterpret_cast<vec_t*>( accTile = reinterpret_cast<vec_t*>(
&states_to_update[i]->accumulator.accumulation[perspective][j * TileHeight]); &states_to_update[i]->accumulator.accumulation[Perspective][j * TileHeight]);
for (IndexType k = 0; k < NumRegs; ++k) for (IndexType k = 0; k < NumRegs; ++k)
vec_store(&accTile[k], acc[k]); vec_store(&accTile[k], acc[k]);
} }
@@ -432,7 +433,7 @@ namespace Stockfish::Eval::NNUE {
{ {
// Load accumulator // Load accumulator
auto accTilePsqt = reinterpret_cast<psqt_vec_t*>( auto accTilePsqt = reinterpret_cast<psqt_vec_t*>(
&st->accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]); &st->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k) for (std::size_t k = 0; k < NumPsqtRegs; ++k)
psqt[k] = vec_load_psqt(&accTilePsqt[k]); psqt[k] = vec_load_psqt(&accTilePsqt[k]);
@@ -458,7 +459,7 @@ namespace Stockfish::Eval::NNUE {
// Store accumulator // Store accumulator
accTilePsqt = reinterpret_cast<psqt_vec_t*>( accTilePsqt = reinterpret_cast<psqt_vec_t*>(
&states_to_update[i]->accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]); &states_to_update[i]->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k) for (std::size_t k = 0; k < NumPsqtRegs; ++k)
vec_store_psqt(&accTilePsqt[k], psqt[k]); vec_store_psqt(&accTilePsqt[k], psqt[k]);
} }
@@ -467,12 +468,12 @@ namespace Stockfish::Eval::NNUE {
#else #else
for (IndexType i = 0; states_to_update[i]; ++i) for (IndexType i = 0; states_to_update[i]; ++i)
{ {
std::memcpy(states_to_update[i]->accumulator.accumulation[perspective], std::memcpy(states_to_update[i]->accumulator.accumulation[Perspective],
st->accumulator.accumulation[perspective], st->accumulator.accumulation[Perspective],
HalfDimensions * sizeof(BiasType)); HalfDimensions * sizeof(BiasType));
for (std::size_t k = 0; k < PSQTBuckets; ++k) for (std::size_t k = 0; k < PSQTBuckets; ++k)
states_to_update[i]->accumulator.psqtAccumulation[perspective][k] = st->accumulator.psqtAccumulation[perspective][k]; states_to_update[i]->accumulator.psqtAccumulation[Perspective][k] = st->accumulator.psqtAccumulation[Perspective][k];
st = states_to_update[i]; st = states_to_update[i];
@@ -482,10 +483,10 @@ namespace Stockfish::Eval::NNUE {
const IndexType offset = HalfDimensions * index; const IndexType offset = HalfDimensions * index;
for (IndexType j = 0; j < HalfDimensions; ++j) for (IndexType j = 0; j < HalfDimensions; ++j)
st->accumulator.accumulation[perspective][j] -= weights[offset + j]; st->accumulator.accumulation[Perspective][j] -= weights[offset + j];
for (std::size_t k = 0; k < PSQTBuckets; ++k) for (std::size_t k = 0; k < PSQTBuckets; ++k)
st->accumulator.psqtAccumulation[perspective][k] -= psqtWeights[index * PSQTBuckets + k]; st->accumulator.psqtAccumulation[Perspective][k] -= psqtWeights[index * PSQTBuckets + k];
} }
// Difference calculation for the activated features // Difference calculation for the activated features
@@ -494,10 +495,10 @@ namespace Stockfish::Eval::NNUE {
const IndexType offset = HalfDimensions * index; const IndexType offset = HalfDimensions * index;
for (IndexType j = 0; j < HalfDimensions; ++j) for (IndexType j = 0; j < HalfDimensions; ++j)
st->accumulator.accumulation[perspective][j] += weights[offset + j]; st->accumulator.accumulation[Perspective][j] += weights[offset + j];
for (std::size_t k = 0; k < PSQTBuckets; ++k) for (std::size_t k = 0; k < PSQTBuckets; ++k)
st->accumulator.psqtAccumulation[perspective][k] += psqtWeights[index * PSQTBuckets + k]; st->accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k];
} }
} }
#endif #endif
@@ -506,9 +507,9 @@ namespace Stockfish::Eval::NNUE {
{ {
// Refresh the accumulator // Refresh the accumulator
auto& accumulator = pos.state()->accumulator; auto& accumulator = pos.state()->accumulator;
accumulator.computed[perspective] = true; accumulator.computed[Perspective] = true;
FeatureSet::IndexList active; FeatureSet::IndexList active;
FeatureSet::append_active_indices(pos, perspective, active); FeatureSet::append_active_indices<Perspective>(pos, active);
#ifdef VECTOR #ifdef VECTOR
for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
@@ -528,7 +529,7 @@ namespace Stockfish::Eval::NNUE {
} }
auto accTile = reinterpret_cast<vec_t*>( auto accTile = reinterpret_cast<vec_t*>(
&accumulator.accumulation[perspective][j * TileHeight]); &accumulator.accumulation[Perspective][j * TileHeight]);
for (unsigned k = 0; k < NumRegs; k++) for (unsigned k = 0; k < NumRegs; k++)
vec_store(&accTile[k], acc[k]); vec_store(&accTile[k], acc[k]);
} }
@@ -548,27 +549,27 @@ namespace Stockfish::Eval::NNUE {
} }
auto accTilePsqt = reinterpret_cast<psqt_vec_t*>( auto accTilePsqt = reinterpret_cast<psqt_vec_t*>(
&accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]); &accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k) for (std::size_t k = 0; k < NumPsqtRegs; ++k)
vec_store_psqt(&accTilePsqt[k], psqt[k]); vec_store_psqt(&accTilePsqt[k], psqt[k]);
} }
#else #else
std::memcpy(accumulator.accumulation[perspective], biases, std::memcpy(accumulator.accumulation[Perspective], biases,
HalfDimensions * sizeof(BiasType)); HalfDimensions * sizeof(BiasType));
for (std::size_t k = 0; k < PSQTBuckets; ++k) for (std::size_t k = 0; k < PSQTBuckets; ++k)
accumulator.psqtAccumulation[perspective][k] = 0; accumulator.psqtAccumulation[Perspective][k] = 0;
for (const auto index : active) for (const auto index : active)
{ {
const IndexType offset = HalfDimensions * index; const IndexType offset = HalfDimensions * index;
for (IndexType j = 0; j < HalfDimensions; ++j) for (IndexType j = 0; j < HalfDimensions; ++j)
accumulator.accumulation[perspective][j] += weights[offset + j]; accumulator.accumulation[Perspective][j] += weights[offset + j];
for (std::size_t k = 0; k < PSQTBuckets; ++k) for (std::size_t k = 0; k < PSQTBuckets; ++k)
accumulator.psqtAccumulation[perspective][k] += psqtWeights[index * PSQTBuckets + k]; accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k];
} }
#endif #endif
} }
+19 -19
View File
@@ -32,30 +32,30 @@ namespace {
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// Pawn penalties // Pawn penalties
constexpr Score Backward = S( 9, 22); constexpr Score Backward = S( 6, 19);
constexpr Score Doubled = S(13, 51); constexpr Score Doubled = S(11, 51);
constexpr Score DoubledEarly = S(20, 7); constexpr Score DoubledEarly = S(17, 7);
constexpr Score Isolated = S( 3, 15); constexpr Score Isolated = S( 1, 20);
constexpr Score WeakLever = S( 4, 58); constexpr Score WeakLever = S( 2, 57);
constexpr Score WeakUnopposed = S(13, 24); constexpr Score WeakUnopposed = S(15, 18);
// Bonus for blocked pawns at 5th or 6th rank // Bonus for blocked pawns at 5th or 6th rank
constexpr Score BlockedPawn[2] = { S(-17, -6), S(-9, 2) }; constexpr Score BlockedPawn[2] = { S(-19, -8), S(-7, 3) };
constexpr Score BlockedStorm[RANK_NB] = { constexpr Score BlockedStorm[RANK_NB] = {
S(0, 0), S(0, 0), S(75, 78), S(-8, 16), S(-6, 10), S(-6, 6), S(0, 2) S(0, 0), S(0, 0), S(64, 75), S(-3, 14), S(-12, 19), S(-7, 4), S(-10, 5)
}; };
// Connected pawn bonus // Connected pawn bonus
constexpr int Connected[RANK_NB] = { 0, 5, 7, 11, 23, 48, 87 }; constexpr int Connected[RANK_NB] = { 0, 3, 7, 7, 15, 54, 86 };
// Strength of pawn shelter for our king by [distance from edge][rank]. // Strength of pawn shelter for our king by [distance from edge][rank].
// RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king.
constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = { constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = {
{ V( -5), V( 82), V( 92), V( 54), V( 36), V( 22), V( 28) }, { V(-2), V(85), V(95), V(53), V(39), V(23), V(25) },
{ V(-44), V( 63), V( 33), V(-50), V(-30), V(-12), V( -62) }, { V(-55), V(64), V(32), V(-55), V(-30), V(-11), V(-61) },
{ V(-11), V( 77), V( 22), V( -6), V( 31), V( 8), V( -45) }, { V(-11), V(75), V(19), V(-6), V(26), V(9), V(-47) },
{ V(-39), V(-12), V(-29), V(-50), V(-43), V(-68), V(-164) } { V(-41), V(-11), V(-27), V(-58), V(-42), V(-66), V(-163) }
}; };
// Danger of enemy pawns moving toward our king by [distance from edge][rank]. // Danger of enemy pawns moving toward our king by [distance from edge][rank].
@@ -63,17 +63,17 @@ namespace {
// is behind our king. Note that UnblockedStorm[0][1-2] accommodate opponent pawn // is behind our king. Note that UnblockedStorm[0][1-2] accommodate opponent pawn
// on edge, likely blocked by our king. // on edge, likely blocked by our king.
constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = { constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = {
{ V( 87), V(-288), V(-168), V( 96), V( 47), V( 44), V( 46) }, { V(94), V(-280), V(-170), V(90), V(59), V(47), V(53) },
{ V( 42), V( -25), V( 120), V( 45), V( 34), V( -9), V( 24) }, { V(43), V(-17), V(128), V(39), V(26), V(-17), V(15) },
{ V( -8), V( 51), V( 167), V( 35), V( -4), V(-16), V(-12) }, { V(-9), V(62), V(170), V(34), V(-5), V(-20), V(-11) },
{ V(-17), V( -13), V( 100), V( 4), V( 9), V(-16), V(-31) } { V(-27), V(-19), V(106), V(10), V(2), V(-13), V(-24) }
}; };
// KingOnFile[semi-open Us][semi-open Them] contains bonuses/penalties // KingOnFile[semi-open Us][semi-open Them] contains bonuses/penalties
// for king when the king is on a semi-open or open file. // for king when the king is on a semi-open or open file.
constexpr Score KingOnFile[2][2] = {{ S(-21,10), S(-7, 1) }, constexpr Score KingOnFile[2][2] = {{ S(-18,11), S(-6,-3) },
{ S( 0,-3), S( 9,-4) }}; { S( 0, 0), S( 5,-4) }};
#undef S #undef S
#undef V #undef V
+6 -1
View File
@@ -1054,7 +1054,10 @@ Key Position::key_after(Move m) const {
if (captured) if (captured)
k ^= Zobrist::psq[captured][to]; k ^= Zobrist::psq[captured][to];
return k ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from]; k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from];
return (captured || type_of(pc) == PAWN)
? k : adjust_key50<true>(k);
} }
@@ -1099,10 +1102,12 @@ bool Position::see_ge(Move m, Value threshold) const {
// Don't allow pinned pieces to attack as long as there are // Don't allow pinned pieces to attack as long as there are
// pinners on their original square. // pinners on their original square.
if (pinners(~stm) & occupied) if (pinners(~stm) & occupied)
{
stmAttackers &= ~blockers_for_king(stm); stmAttackers &= ~blockers_for_king(stm);
if (!stmAttackers) if (!stmAttackers)
break; break;
}
res ^= 1; res ^= 1;
+15 -2
View File
@@ -161,6 +161,7 @@ public:
bool has_repeated() const; bool has_repeated() const;
int rule50_count() const; int rule50_count() const;
Score psq_score() const; Score psq_score() const;
Value psq_eg_stm() const;
Value non_pawn_material(Color c) const; Value non_pawn_material(Color c) const;
Value non_pawn_material() const; Value non_pawn_material() const;
@@ -184,6 +185,8 @@ private:
void move_piece(Square from, Square to); void move_piece(Square from, Square to);
template<bool Do> template<bool Do>
void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto);
template<bool AfterMove>
Key adjust_key50(Key k) const;
// Data members // Data members
Piece board[SQUARE_NB]; Piece board[SQUARE_NB];
@@ -326,8 +329,14 @@ inline int Position::pawns_on_same_color_squares(Color c, Square s) const {
} }
inline Key Position::key() const { inline Key Position::key() const {
return st->rule50 < 14 ? st->key return adjust_key50<false>(st->key);
: st->key ^ make_key((st->rule50 - 14) / 8); }
template<bool AfterMove>
inline Key Position::adjust_key50(Key k) const
{
return st->rule50 < 14 - AfterMove
? k : k ^ make_key((st->rule50 - (14 - AfterMove)) / 8);
} }
inline Key Position::pawn_key() const { inline Key Position::pawn_key() const {
@@ -342,6 +351,10 @@ inline Score Position::psq_score() const {
return psq; return psq;
} }
inline Value Position::psq_eg_stm() const {
return (sideToMove == WHITE ? 1 : -1) * eg_value(psq);
}
inline Value Position::non_pawn_material(Color c) const { inline Value Position::non_pawn_material(Color c) const {
return st->nonPawnMaterial[c]; return st->nonPawnMaterial[c];
} }
+142 -149
View File
@@ -64,7 +64,7 @@ namespace {
// Futility margin // Futility margin
Value futility_margin(Depth d, bool improving) { Value futility_margin(Depth d, bool improving) {
return Value(168 * (d - improving)); return Value(165 * (d - improving));
} }
// Reductions lookup table, initialized at startup // Reductions lookup table, initialized at startup
@@ -72,21 +72,22 @@ namespace {
Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) {
int r = Reductions[d] * Reductions[mn]; int r = Reductions[d] * Reductions[mn];
return (r + 1463 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 1010); return (r + 1642 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 916);
} }
constexpr int futility_move_count(bool improving, Depth depth) { constexpr int futility_move_count(bool improving, Depth depth) {
return (3 + depth * depth) / (2 - improving); return improving ? (3 + depth * depth)
: (3 + depth * depth) / 2;
} }
// History and stats update bonus, based on depth // History and stats update bonus, based on depth
int stat_bonus(Depth d) { int stat_bonus(Depth d) {
return std::min((9 * d + 270) * d - 311 , 2145); return std::min((12 * d + 282) * d - 349 , 1594);
} }
// Add a small random component to draw evaluations to avoid 3-fold blindness // Add a small random component to draw evaluations to avoid 3-fold blindness
Value value_draw(Thread* thisThread) { Value value_draw(const Thread* thisThread) {
return VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1); return VALUE_DRAW - 1 + Value(thisThread->nodes & 0x2);
} }
// Skill structure is used to implement strength limit. If we have an uci_elo then // Skill structure is used to implement strength limit. If we have an uci_elo then
@@ -116,7 +117,7 @@ namespace {
Value value_to_tt(Value v, int ply); Value value_to_tt(Value v, int ply);
Value value_from_tt(Value v, int ply, int r50c); Value value_from_tt(Value v, int ply, int r50c);
void update_pv(Move* pv, Move move, Move* childPv); void update_pv(Move* pv, Move move, const Move* childPv);
void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus);
void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus); void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus);
void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq,
@@ -158,7 +159,7 @@ namespace {
void Search::init() { void Search::init() {
for (int i = 1; i < MAX_MOVES; ++i) for (int i = 1; i < MAX_MOVES; ++i)
Reductions[i] = int((20.81 + std::log(Threads.size()) / 2) * std::log(i)); Reductions[i] = int((20.26 + std::log(Threads.size()) / 2) * std::log(i));
} }
@@ -242,6 +243,9 @@ void MainThread::search() {
&& rootMoves[0].pv[0] != MOVE_NONE) && rootMoves[0].pv[0] != MOVE_NONE)
bestThread = Threads.get_best_thread(); bestThread = Threads.get_best_thread();
for (Thread* th : Threads)
th->previousDepth = bestThread->completedDepth;
// Prepare PVLine and ponder move // Prepare PVLine and ponder move
std::string PVLine = UCI::pv(bestThread->rootPos, bestThread->completedDepth, -VALUE_INFINITE, VALUE_INFINITE); std::string PVLine = UCI::pv(bestThread->rootPos, bestThread->completedDepth, -VALUE_INFINITE, VALUE_INFINITE);
bestPreviousScore = bestThread->rootMoves[0].score; bestPreviousScore = bestThread->rootMoves[0].score;
@@ -333,11 +337,10 @@ void Thread::search() {
multiPV = std::min(multiPV, rootMoves.size()); multiPV = std::min(multiPV, rootMoves.size());
complexityAverage.set(202, 1); complexityAverage.set(155, 1);
trend = SCORE_ZERO; trend = SCORE_ZERO;
optimism[ us] = Value(39); optimism[us] = optimism[~us] = VALUE_ZERO;
optimism[~us] = -optimism[us];
int searchAgainCounter = 0; int searchAgainCounter = 0;
@@ -380,16 +383,16 @@ void Thread::search() {
if (rootDepth >= 4) if (rootDepth >= 4)
{ {
Value prev = rootMoves[pvIdx].averageScore; Value prev = rootMoves[pvIdx].averageScore;
delta = Value(16) + int(prev) * prev / 19178; delta = Value(10) + int(prev) * prev / 15620;
alpha = std::max(prev - delta,-VALUE_INFINITE); alpha = std::max(prev - delta,-VALUE_INFINITE);
beta = std::min(prev + delta, VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE);
// Adjust trend and optimism based on root move's previousScore // Adjust trend and optimism based on root move's previousScore
int tr = sigmoid(prev, 3, 8, 90, 125, 1); int tr = 116 * prev / (std::abs(prev) + 89);
trend = (us == WHITE ? make_score(tr, tr / 2) trend = (us == WHITE ? make_score(tr, tr / 2)
: -make_score(tr, tr / 2)); : -make_score(tr, tr / 2));
int opt = sigmoid(prev, 8, 17, 144, 13966, 183); int opt = 118 * prev / (std::abs(prev) + 169);
optimism[ us] = Value(opt); optimism[ us] = Value(opt);
optimism[~us] = -optimism[us]; optimism[~us] = -optimism[us];
} }
@@ -400,7 +403,9 @@ void Thread::search() {
int failedHighCnt = 0; int failedHighCnt = 0;
while (true) while (true)
{ {
Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - searchAgainCounter); // Adjust the effective depth searched, but ensuring at least one effective increment for every
// four searchAgain steps (see issue #2717).
Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - 3 * (searchAgainCounter + 1) / 4);
bestValue = Stockfish::search<Root>(rootPos, ss, alpha, beta, adjustedDepth, false); bestValue = Stockfish::search<Root>(rootPos, ss, alpha, beta, adjustedDepth, false);
// Bring the best move to the front. It is critical that sorting // Bring the best move to the front. It is critical that sorting
@@ -497,17 +502,16 @@ void Thread::search() {
&& !Threads.stop && !Threads.stop
&& !mainThread->stopOnPonderhit) && !mainThread->stopOnPonderhit)
{ {
double fallingEval = (69 + 12 * (mainThread->bestPreviousAverageScore - bestValue) double fallingEval = (71 + 12 * (mainThread->bestPreviousAverageScore - bestValue)
+ 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 781.4; + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 656.7;
fallingEval = std::clamp(fallingEval, 0.5, 1.5); fallingEval = std::clamp(fallingEval, 0.5, 1.5);
// If the bestMove is stable over several iterations, reduce time accordingly // If the bestMove is stable over several iterations, reduce time accordingly
timeReduction = lastBestMoveDepth + 10 < completedDepth ? 1.63 : 0.73; timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.37 : 0.65;
double reduction = (1.56 + mainThread->previousTimeReduction) / (2.20 * timeReduction); double reduction = (1.4 + mainThread->previousTimeReduction) / (2.15 * timeReduction);
double bestMoveInstability = 1.073 + std::max(1.0, 2.25 - 9.9 / rootDepth) double bestMoveInstability = 1 + 1.7 * totBestMoveChanges / Threads.size();
* totBestMoveChanges / Threads.size();
int complexity = mainThread->complexityAverage.value(); int complexity = mainThread->complexityAverage.value();
double complexPosition = std::clamp(1.0 + (complexity - 326) / 1618.1, 0.5, 1.5); double complexPosition = std::min(1.0 + (complexity - 261) / 1738.7, 1.5);
double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition; double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition;
@@ -528,7 +532,7 @@ void Thread::search() {
} }
else if ( Threads.increaseDepth else if ( Threads.increaseDepth
&& !mainThread->ponder && !mainThread->ponder
&& Time.elapsed() > totalTime * 0.43) && Time.elapsed() > totalTime * 0.53)
Threads.increaseDepth = false; Threads.increaseDepth = false;
else else
Threads.increaseDepth = true; Threads.increaseDepth = true;
@@ -591,18 +595,17 @@ namespace {
Move ttMove, move, excludedMove, bestMove; Move ttMove, move, excludedMove, bestMove;
Depth extension, newDepth; Depth extension, newDepth;
Value bestValue, value, ttValue, eval, maxValue, probCutBeta; Value bestValue, value, ttValue, eval, maxValue, probCutBeta;
bool givesCheck, improving, didLMR, priorCapture; bool givesCheck, improving, priorCapture, singularQuietLMR;
bool capture, doFullDepthSearch, moveCountPruning, ttCapture; bool capture, moveCountPruning, ttCapture;
Piece movedPiece; Piece movedPiece;
int moveCount, captureCount, quietCount, bestMoveCount, improvement, complexity; int moveCount, captureCount, quietCount, improvement, complexity;
// Step 1. Initialize node // Step 1. Initialize node
Thread* thisThread = pos.this_thread(); Thread* thisThread = pos.this_thread();
thisThread->depth = depth;
ss->inCheck = pos.checkers(); ss->inCheck = pos.checkers();
priorCapture = pos.captured_piece(); priorCapture = pos.captured_piece();
Color us = pos.side_to_move(); Color us = pos.side_to_move();
moveCount = bestMoveCount = captureCount = quietCount = ss->moveCount = 0; moveCount = captureCount = quietCount = ss->moveCount = 0;
bestValue = -VALUE_INFINITE; bestValue = -VALUE_INFINITE;
maxValue = VALUE_INFINITE; maxValue = VALUE_INFINITE;
@@ -642,8 +645,8 @@ namespace {
(ss+1)->ttPv = false; (ss+1)->ttPv = false;
(ss+1)->excludedMove = bestMove = MOVE_NONE; (ss+1)->excludedMove = bestMove = MOVE_NONE;
(ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
(ss+2)->cutoffCnt = 0;
ss->doubleExtensions = (ss-1)->doubleExtensions; ss->doubleExtensions = (ss-1)->doubleExtensions;
ss->depth = depth;
Square prevSq = to_sq((ss-1)->currentMove); Square prevSq = to_sq((ss-1)->currentMove);
// Initialize statScore to zero for the grandchildren of the current position. // Initialize statScore to zero for the grandchildren of the current position.
@@ -670,10 +673,9 @@ namespace {
// At non-PV nodes we check for an early TT cutoff // At non-PV nodes we check for an early TT cutoff
if ( !PvNode if ( !PvNode
&& ss->ttHit && ss->ttHit
&& tte->depth() > depth - (thisThread->id() % 2 == 1) && tte->depth() > depth - (tte->bound() == BOUND_EXACT)
&& ttValue != VALUE_NONE // Possible in case of TT access race && ttValue != VALUE_NONE // Possible in case of TT access race
&& (ttValue >= beta ? (tte->bound() & BOUND_LOWER) && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER)))
: (tte->bound() & BOUND_UPPER)))
{ {
// If ttMove is quiet, update move sorting heuristics on TT hit (~1 Elo) // If ttMove is quiet, update move sorting heuristics on TT hit (~1 Elo)
if (ttMove) if (ttMove)
@@ -773,11 +775,9 @@ namespace {
// Never assume anything about values stored in TT // Never assume anything about values stored in TT
ss->staticEval = eval = tte->eval(); ss->staticEval = eval = tte->eval();
if (eval == VALUE_NONE) if (eval == VALUE_NONE)
ss->staticEval = eval = evaluate(pos); ss->staticEval = eval = evaluate(pos, &complexity);
else // Fall back to (semi)classical complexity for TT hits, the NNUE complexity is lost
// Randomize draw evaluation complexity = abs(ss->staticEval - pos.psq_eg_stm());
if (eval == VALUE_DRAW)
eval = value_draw(thisThread);
// ttValue can be used as a better position evaluation (~4 Elo) // ttValue can be used as a better position evaluation (~4 Elo)
if ( ttValue != VALUE_NONE if ( ttValue != VALUE_NONE
@@ -786,7 +786,7 @@ namespace {
} }
else else
{ {
ss->staticEval = eval = evaluate(pos); ss->staticEval = eval = evaluate(pos, &complexity);
// Save static evaluation into transposition table // Save static evaluation into transposition table
if (!excludedMove) if (!excludedMove)
@@ -795,10 +795,12 @@ namespace {
eval); eval);
} }
thisThread->complexityAverage.update(complexity);
// Use static evaluation difference to improve quiet move ordering (~3 Elo) // Use static evaluation difference to improve quiet move ordering (~3 Elo)
if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture)
{ {
int bonus = std::clamp(-16 * int((ss-1)->staticEval + ss->staticEval), -2000, 2000); int bonus = std::clamp(-19 * int((ss-1)->staticEval + ss->staticEval), -1914, 1914);
thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus;
} }
@@ -808,19 +810,13 @@ namespace {
// margin and the improving flag are used in various pruning heuristics. // margin and the improving flag are used in various pruning heuristics.
improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval
: (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval
: 175; : 168;
improving = improvement > 0; improving = improvement > 0;
complexity = abs(ss->staticEval - (us == WHITE ? eg_value(pos.psq_score()) : -eg_value(pos.psq_score())));
thisThread->complexityAverage.update(complexity);
// Step 7. Razoring. // Step 7. Razoring.
// If eval is really low check with qsearch if it can exceed alpha, if it can't, // If eval is really low check with qsearch if it can exceed alpha, if it can't,
// return a fail low. // return a fail low.
if ( !PvNode if (eval < alpha - 369 - 254 * depth * depth)
&& depth <= 7
&& eval < alpha - 348 - 258 * depth * depth)
{ {
value = qsearch<NonPV>(pos, ss, alpha - 1, alpha); value = qsearch<NonPV>(pos, ss, alpha - 1, alpha);
if (value < alpha) if (value < alpha)
@@ -831,18 +827,18 @@ namespace {
// The depth condition is important for mate finding. // The depth condition is important for mate finding.
if ( !ss->ttPv if ( !ss->ttPv
&& depth < 8 && depth < 8
&& eval - futility_margin(depth, improving) - (ss-1)->statScore / 256 >= beta && eval - futility_margin(depth, improving) - (ss-1)->statScore / 303 >= beta
&& eval >= beta && eval >= beta
&& eval < 26305) // larger than VALUE_KNOWN_WIN, but smaller than TB wins. && eval < 28031) // larger than VALUE_KNOWN_WIN, but smaller than TB wins
return eval; return eval;
// Step 9. Null move search with verification search (~22 Elo) // Step 9. Null move search with verification search (~22 Elo)
if ( !PvNode if ( !PvNode
&& (ss-1)->currentMove != MOVE_NULL && (ss-1)->currentMove != MOVE_NULL
&& (ss-1)->statScore < 14695 && (ss-1)->statScore < 17139
&& eval >= beta && eval >= beta
&& eval >= ss->staticEval && eval >= ss->staticEval
&& ss->staticEval >= beta - 15 * depth - improvement / 15 + 198 + complexity / 28 && ss->staticEval >= beta - 20 * depth - improvement / 13 + 233 + complexity / 25
&& !excludedMove && !excludedMove
&& pos.non_pawn_material(us) && pos.non_pawn_material(us)
&& (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
@@ -850,7 +846,7 @@ namespace {
assert(eval - beta >= 0); assert(eval - beta >= 0);
// Null move dynamic reduction based on depth, eval and complexity of position // Null move dynamic reduction based on depth, eval and complexity of position
Depth R = std::min(int(eval - beta) / 147, 5) + depth / 3 + 4 - (complexity > 753); Depth R = std::min(int(eval - beta) / 168, 7) + depth / 3 + 4 - (complexity > 861);
ss->currentMove = MOVE_NULL; ss->currentMove = MOVE_NULL;
ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0];
@@ -886,7 +882,7 @@ namespace {
} }
} }
probCutBeta = beta + 179 - 46 * improving; probCutBeta = beta + 191 - 54 * improving;
// Step 10. ProbCut (~4 Elo) // Step 10. ProbCut (~4 Elo)
// If we have a good enough capture and a reduced search returns a value // If we have a good enough capture and a reduced search returns a value
@@ -906,20 +902,15 @@ namespace {
assert(probCutBeta < VALUE_INFINITE); assert(probCutBeta < VALUE_INFINITE);
MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, depth - 3, &captureHistory); MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, depth - 3, &captureHistory);
bool ttPv = ss->ttPv;
bool captureOrPromotion;
ss->ttPv = false;
while ((move = mp.next_move()) != MOVE_NONE) while ((move = mp.next_move()) != MOVE_NONE)
if (move != excludedMove && pos.legal(move)) if (move != excludedMove && pos.legal(move))
{ {
assert(pos.capture(move) || promotion_type(move) == QUEEN); assert(pos.capture(move) || promotion_type(move) == QUEEN);
captureOrPromotion = true;
ss->currentMove = move; ss->currentMove = move;
ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck]
[captureOrPromotion] [true]
[pos.moved_piece(move)] [pos.moved_piece(move)]
[to_sq(move)]; [to_sq(move)];
@@ -936,34 +927,32 @@ namespace {
if (value >= probCutBeta) if (value >= probCutBeta)
{ {
// if transposition table doesn't have equal or more deep info write probCut data into it // Save ProbCut data into transposition table
if ( !(ss->ttHit Cluster::save(thisThread, tte, posKey, value_to_tt(value, ss->ply), ss->ttPv,
&& tte->depth() >= depth - 3 BOUND_LOWER, depth - 3, move, ss->staticEval);
&& ttValue != VALUE_NONE))
Cluster::save(thisThread, tte, posKey, value_to_tt(value, ss->ply), ttPv,
BOUND_LOWER,
depth - 3, move, ss->staticEval);
return value; return value;
} }
} }
ss->ttPv = ttPv;
} }
// Step 11. If the position is not in TT, decrease depth by 2 or 1 depending on node type (~3 Elo) // Step 11. If the position is not in TT, decrease depth by 3.
// Use qsearch if depth is equal or below zero (~4 Elo)
if ( PvNode if ( PvNode
&& depth >= 3
&& !ttMove) && !ttMove)
depth -= 2; depth -= 3;
if (depth <= 0)
return qsearch<PV>(pos, ss, alpha, beta);
if ( cutNode if ( cutNode
&& depth >= 8 && depth >= 9
&& !ttMove) && !ttMove)
depth--; depth -= 2;
moves_loop: // When in check, search starts here moves_loop: // When in check, search starts here
// Step 12. A small Probcut idea, when we are in check (~0 Elo) // Step 12. A small Probcut idea, when we are in check (~0 Elo)
probCutBeta = beta + 481; probCutBeta = beta + 417;
if ( ss->inCheck if ( ss->inCheck
&& !PvNode && !PvNode
&& depth >= 2 && depth >= 2
@@ -990,7 +979,7 @@ moves_loop: // When in check, search starts here
ss->killers); ss->killers);
value = bestValue; value = bestValue;
moveCountPruning = false; moveCountPruning = singularQuietLMR = false;
// Indicate PvNodes that will probably fail low if the node was searched // Indicate PvNodes that will probably fail low if the node was searched
// at a depth equal or greater than the current depth, and the result of this search was a fail low. // at a depth equal or greater than the current depth, and the result of this search was a fail low.
@@ -1054,17 +1043,16 @@ moves_loop: // When in check, search starts here
|| givesCheck) || givesCheck)
{ {
// Futility pruning for captures (~0 Elo) // Futility pruning for captures (~0 Elo)
if ( !pos.empty(to_sq(move)) if ( !givesCheck
&& !givesCheck
&& !PvNode && !PvNode
&& lmrDepth < 6 && lmrDepth < 7
&& !ss->inCheck && !ss->inCheck
&& ss->staticEval + 281 + 179 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] && ss->staticEval + 180 + 201 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))]
+ captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 6 < alpha) + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 6 < alpha)
continue; continue;
// SEE based pruning (~9 Elo) // SEE based pruning (~9 Elo)
if (!pos.see_ge(move, Value(-203) * depth)) if (!pos.see_ge(move, Value(-222) * depth))
continue; continue;
} }
else else
@@ -1078,16 +1066,16 @@ moves_loop: // When in check, search starts here
&& history < -3875 * (depth - 1)) && history < -3875 * (depth - 1))
continue; continue;
history += thisThread->mainHistory[us][from_to(move)]; history += 2 * thisThread->mainHistory[us][from_to(move)];
// Futility pruning: parent node (~9 Elo) // Futility pruning: parent node (~9 Elo)
if ( !ss->inCheck if ( !ss->inCheck
&& lmrDepth < 11 && lmrDepth < 13
&& ss->staticEval + 122 + 138 * lmrDepth + history / 60 <= alpha) && ss->staticEval + 106 + 145 * lmrDepth + history / 52 <= alpha)
continue; continue;
// Prune moves with negative SEE (~3 Elo) // Prune moves with negative SEE (~3 Elo)
if (!pos.see_ge(move, Value(-25 * lmrDepth * lmrDepth - 20 * lmrDepth))) if (!pos.see_ge(move, Value(-24 * lmrDepth * lmrDepth - 15 * lmrDepth)))
continue; continue;
} }
} }
@@ -1102,7 +1090,7 @@ moves_loop: // When in check, search starts here
// a reduced search on all the other moves but the ttMove and if the // a reduced search on all the other moves but the ttMove and if the
// result is lower than ttValue minus a margin, then we will extend the ttMove. // result is lower than ttValue minus a margin, then we will extend the ttMove.
if ( !rootNode if ( !rootNode
&& depth >= 4 + 2 * (PvNode && tte->is_pv()) && depth >= 4 - (thisThread->previousDepth > 24) + 2 * (PvNode && tte->is_pv())
&& move == ttMove && move == ttMove
&& !excludedMove // Avoid recursive singular search && !excludedMove // Avoid recursive singular search
/* && ttValue != VALUE_NONE Already implicit in the next condition */ /* && ttValue != VALUE_NONE Already implicit in the next condition */
@@ -1110,7 +1098,7 @@ moves_loop: // When in check, search starts here
&& (tte->bound() & BOUND_LOWER) && (tte->bound() & BOUND_LOWER)
&& tte->depth() >= depth - 3) && tte->depth() >= depth - 3)
{ {
Value singularBeta = ttValue - 3 * depth; Value singularBeta = ttValue - (3 + (ss->ttPv && !PvNode)) * depth;
Depth singularDepth = (depth - 1) / 2; Depth singularDepth = (depth - 1) / 2;
ss->excludedMove = move; ss->excludedMove = move;
@@ -1120,11 +1108,12 @@ moves_loop: // When in check, search starts here
if (value < singularBeta) if (value < singularBeta)
{ {
extension = 1; extension = 1;
singularQuietLMR = !ttCapture;
// Avoid search explosion by limiting the number of double extensions // Avoid search explosion by limiting the number of double extensions
if ( !PvNode if ( !PvNode
&& value < singularBeta - 26 && value < singularBeta - 25
&& ss->doubleExtensions <= 8) && ss->doubleExtensions <= 9)
extension = 2; extension = 2;
} }
@@ -1139,19 +1128,23 @@ moves_loop: // When in check, search starts here
// If the eval of ttMove is greater than beta, we reduce it (negative extension) // If the eval of ttMove is greater than beta, we reduce it (negative extension)
else if (ttValue >= beta) else if (ttValue >= beta)
extension = -2; extension = -2;
// If the eval of ttMove is less than alpha and value, we reduce it (negative extension)
else if (ttValue <= alpha && ttValue <= value)
extension = -1;
} }
// Check extensions (~1 Elo) // Check extensions (~1 Elo)
else if ( givesCheck else if ( givesCheck
&& depth > 9 && depth > 9
&& abs(ss->staticEval) > 71) && abs(ss->staticEval) > 82)
extension = 1; extension = 1;
// Quiet ttMove extensions (~0 Elo) // Quiet ttMove extensions (~0 Elo)
else if ( PvNode else if ( PvNode
&& move == ttMove && move == ttMove
&& move == ss->killers[0] && move == ss->killers[0]
&& (*contHist[0])[movedPiece][to_sq(move)] >= 5491) && (*contHist[0])[movedPiece][to_sq(move)] >= 5177)
extension = 1; extension = 1;
} }
@@ -1172,8 +1165,6 @@ moves_loop: // When in check, search starts here
// Step 16. Make the move // Step 16. Make the move
pos.do_move(move, st, givesCheck); pos.do_move(move, st, givesCheck);
bool doDeeperSearch = false;
// Step 17. Late moves reduction / extension (LMR, ~98 Elo) // Step 17. Late moves reduction / extension (LMR, ~98 Elo)
// We use various heuristics for the sons of a node after the first son has // We use various heuristics for the sons of a node after the first son has
// been searched. In general we would like to reduce them, but there are many // been searched. In general we would like to reduce them, but there are many
@@ -1186,11 +1177,6 @@ moves_loop: // When in check, search starts here
{ {
Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta); Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta);
// Decrease reduction at some PvNodes (~2 Elo)
if ( PvNode
&& bestMoveCount <= 3)
r--;
// Decrease reduction if position is or has been on the PV // Decrease reduction if position is or has been on the PV
// and node is not likely to fail low. (~3 Elo) // and node is not likely to fail low. (~3 Elo)
if ( ss->ttPv if ( ss->ttPv
@@ -1202,59 +1188,52 @@ moves_loop: // When in check, search starts here
r--; r--;
// Increase reduction for cut nodes (~3 Elo) // Increase reduction for cut nodes (~3 Elo)
if (cutNode && move != ss->killers[0]) if (cutNode)
r += 2; r += 2;
// Increase reduction if ttMove is a capture (~3 Elo) // Increase reduction if ttMove is a capture (~3 Elo)
if (ttCapture) if (ttCapture)
r++; r++;
// Decrease reduction at PvNodes if bestvalue // Decrease reduction for PvNodes based on depth
// is vastly different from static evaluation if (PvNode)
if (PvNode && !ss->inCheck && abs(ss->staticEval - bestValue) > 250) r -= 1 + 11 / (3 + depth);
// Decrease reduction if ttMove has been singularly extended (~1 Elo)
if (singularQuietLMR)
r--; r--;
ss->statScore = thisThread->mainHistory[us][from_to(move)] // Dicrease reduction if we move a threatened piece (~1 Elo)
if ( depth > 9
&& (mp.threatenedPieces & from_sq(move)))
r--;
// Increase reduction if next ply has a lot of fail high
if ((ss+1)->cutoffCnt > 3 && !PvNode)
r++;
ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)]
+ (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[0])[movedPiece][to_sq(move)]
+ (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)]
+ (*contHist[3])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)]
- 4334; - 4433;
// Decrease/increase reduction for moves with a good/bad history (~30 Elo) // Decrease/increase reduction for moves with a good/bad history (~30 Elo)
r -= ss->statScore / 15914; r -= ss->statScore / 13628;
// In general we want to cap the LMR depth search at newDepth. But if reductions // In general we want to cap the LMR depth search at newDepth, but when
// are really negative and movecount is low, we allow this move to be searched // reduction is negative, we allow this move a limited search extension
// deeper than the first move (this may lead to hidden double extensions). // beyond the first move depth. This may lead to hidden double extensions.
int deeper = r >= -1 ? 0 Depth d = std::clamp(newDepth - r, 1, newDepth + 1);
: moveCount <= 4 ? 2
: PvNode && depth > 4 ? 1
: cutNode && moveCount <= 8 ? 1
: 0;
Depth d = std::clamp(newDepth - r, 1, newDepth + deeper);
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true); value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true);
// If the son is reduced and fails high it will be re-searched at full depth // Do full depth search when reduced LMR search fails high
doFullDepthSearch = value > alpha && d < newDepth; if (value > alpha && d < newDepth)
doDeeperSearch = value > (alpha + 78 + 11 * (newDepth - d));
didLMR = true;
}
else
{
doFullDepthSearch = !PvNode || moveCount > 1;
didLMR = false;
}
// Step 18. Full depth search when LMR is skipped or fails high
if (doFullDepthSearch)
{ {
const bool doDeeperSearch = value > (alpha + 64 + 11 * (newDepth - d));
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth + doDeeperSearch, !cutNode); value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth + doDeeperSearch, !cutNode);
// If the move passed LMR update its stats
if (didLMR)
{
int bonus = value > alpha ? stat_bonus(newDepth) int bonus = value > alpha ? stat_bonus(newDepth)
: -stat_bonus(newDepth); : -stat_bonus(newDepth);
@@ -1265,6 +1244,12 @@ moves_loop: // When in check, search starts here
} }
} }
// Step 18. Full depth search when LMR is skipped
else if (!PvNode || moveCount > 1)
{
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode);
}
// For PV nodes only, do a full PV search on the first move or after a fail // For PV nodes only, do a full PV search on the first move or after a fail
// high (in the latter case search only if value < beta), otherwise let the // high (in the latter case search only if value < beta), otherwise let the
// parent node fail low with value <= alpha and try another move. // parent node fail low with value <= alpha and try another move.
@@ -1336,15 +1321,27 @@ moves_loop: // When in check, search starts here
if (PvNode && value < beta) // Update alpha! Always alpha < beta if (PvNode && value < beta) // Update alpha! Always alpha < beta
{ {
alpha = value; alpha = value;
bestMoveCount++;
// Reduce other moves if we have found at least one score improvement
if ( depth > 1
&& depth < 6
&& beta < VALUE_KNOWN_WIN
&& alpha > -VALUE_KNOWN_WIN)
depth -= 1;
assert(depth > 0);
} }
else else
{ {
ss->cutoffCnt++;
assert(value >= beta); // Fail high assert(value >= beta); // Fail high
break; break;
} }
} }
} }
else
ss->cutoffCnt = 0;
// If the move is worse than some previously searched move, remember it to update its stats later // If the move is worse than some previously searched move, remember it to update its stats later
if (move != bestMove) if (move != bestMove)
@@ -1383,14 +1380,14 @@ moves_loop: // When in check, search starts here
quietsSearched, quietCount, capturesSearched, captureCount, depth); quietsSearched, quietCount, capturesSearched, captureCount, depth);
// Bonus for prior countermove that caused the fail low // Bonus for prior countermove that caused the fail low
else if ( (depth >= 4 || PvNode) else if ( (depth >= 5 || PvNode)
&& !priorCapture) && !priorCapture)
{ {
//Assign extra bonus if current node is PvNode or cutNode //Assign extra bonus if current node is PvNode or cutNode
//or fail low was really bad //or fail low was really bad
bool extraBonus = PvNode bool extraBonus = PvNode
|| cutNode || cutNode
|| bestValue < alpha - 70 * depth; || bestValue < alpha - 62 * depth;
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus)); update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus));
} }
@@ -1419,6 +1416,7 @@ moves_loop: // When in check, search starts here
// qsearch() is the quiescence search function, which is called by the main search // qsearch() is the quiescence search function, which is called by the main search
// function with zero depth, or recursively with further decreasing depth per call. // function with zero depth, or recursively with further decreasing depth per call.
// (~155 elo)
template <NodeType nodeType> template <NodeType nodeType>
Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
@@ -1475,8 +1473,7 @@ moves_loop: // When in check, search starts here
&& ss->ttHit && ss->ttHit
&& tte->depth() >= ttDepth && tte->depth() >= ttDepth
&& ttValue != VALUE_NONE // Only in case of TT access race && ttValue != VALUE_NONE // Only in case of TT access race
&& (ttValue >= beta ? (tte->bound() & BOUND_LOWER) && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER)))
: (tte->bound() & BOUND_UPPER)))
return ttValue; return ttValue;
// Evaluate the position statically // Evaluate the position statically
@@ -1519,7 +1516,7 @@ moves_loop: // When in check, search starts here
if (PvNode && bestValue > alpha) if (PvNode && bestValue > alpha)
alpha = bestValue; alpha = bestValue;
futilityBase = bestValue + 118; futilityBase = bestValue + 153;
} }
const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory,
@@ -1595,8 +1592,8 @@ moves_loop: // When in check, search starts here
// Continuation history based pruning (~2 Elo) // Continuation history based pruning (~2 Elo)
if ( !capture if ( !capture
&& bestValue > VALUE_TB_LOSS_IN_MAX_PLY && bestValue > VALUE_TB_LOSS_IN_MAX_PLY
&& (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0
&& (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold) && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0)
continue; continue;
// movecount pruning for quiet check evasions // movecount pruning for quiet check evasions
@@ -1702,7 +1699,7 @@ moves_loop: // When in check, search starts here
// update_pv() adds current move and appends child pv[] // update_pv() adds current move and appends child pv[]
void update_pv(Move* pv, Move move, Move* childPv) { void update_pv(Move* pv, Move move, const Move* childPv) {
for (*pv++ = move; childPv && *childPv != MOVE_NONE; ) for (*pv++ = move; childPv && *childPv != MOVE_NONE; )
*pv++ = *childPv++; *pv++ = *childPv++;
@@ -1715,19 +1712,18 @@ moves_loop: // When in check, search starts here
void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq,
Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth) { Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth) {
int bonus1, bonus2;
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Thread* thisThread = pos.this_thread(); Thread* thisThread = pos.this_thread();
CapturePieceToHistory& captureHistory = thisThread->captureHistory; CapturePieceToHistory& captureHistory = thisThread->captureHistory;
Piece moved_piece = pos.moved_piece(bestMove); Piece moved_piece = pos.moved_piece(bestMove);
PieceType captured = type_of(pos.piece_on(to_sq(bestMove))); PieceType captured = type_of(pos.piece_on(to_sq(bestMove)));
int bonus1 = stat_bonus(depth + 1);
bonus1 = stat_bonus(depth + 1);
bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus
: stat_bonus(depth); // smaller bonus
if (!pos.capture(bestMove)) if (!pos.capture(bestMove))
{ {
int bonus2 = bestValue > beta + 137 ? bonus1 // larger bonus
: stat_bonus(depth); // smaller bonus
// Increase stats for the best move in case it was a quiet move // Increase stats for the best move in case it was a quiet move
update_quiet_stats(pos, ss, bestMove, bonus2); update_quiet_stats(pos, ss, bestMove, bonus2);
@@ -1915,12 +1911,9 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) {
ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
ss << " nodes " << nodesSearched ss << " nodes " << nodesSearched
<< " nps " << nodesSearched * 1000 / elapsed; << " nps " << nodesSearched * 1000 / elapsed
<< " hashfull " << TT.hashfull()
if (elapsed > 1000) // Earlier makes little sense << " tbhits " << tbHits
ss << " hashfull " << TT.hashfull();
ss << " tbhits " << tbHits
<< " time " << elapsed << " time " << elapsed
<< " pv"; << " pv";
+1 -4
View File
@@ -32,9 +32,6 @@ class Position;
namespace Search { namespace Search {
/// Threshold used for countermoves based pruning
constexpr int CounterMovePruneThreshold = 0;
/// Stack struct keeps track of the information we need to remember from nodes /// Stack struct keeps track of the information we need to remember from nodes
/// shallower and deeper in the tree during the search. Each search thread has /// shallower and deeper in the tree during the search. Each search thread has
@@ -48,13 +45,13 @@ struct Stack {
Move excludedMove; Move excludedMove;
Move killers[2]; Move killers[2];
Value staticEval; Value staticEval;
Depth depth;
int statScore; int statScore;
int moveCount; int moveCount;
bool inCheck; bool inCheck;
bool ttPv; bool ttPv;
bool ttHit; bool ttHit;
int doubleExtensions; int doubleExtensions;
int cutoffCnt;
}; };
+8 -7
View File
@@ -60,6 +60,7 @@ namespace Stockfish {
namespace { namespace {
constexpr int TBPIECES = 7; // Max number of supported pieces constexpr int TBPIECES = 7; // Max number of supported pieces
constexpr int MAX_DTZ = 1 << 18; // Max DTZ supported, large enough to deal with the syzygy TB limit.
enum { BigEndian, LittleEndian }; enum { BigEndian, LittleEndian };
enum TBType { WDL, DTZ }; // Used as template parameter enum TBType { WDL, DTZ }; // Used as template parameter
@@ -1291,7 +1292,7 @@ void Tablebases::init(const std::string& paths) {
for (auto s : diagonal) for (auto s : diagonal)
MapA1D1D4[s] = code++; MapA1D1D4[s] = code++;
// MapKK[] encodes all the 461 possible legal positions of two kings where // MapKK[] encodes all the 462 possible legal positions of two kings where
// the first is in the a1-d1-d4 triangle. If the first king is on the a1-d4 // the first is in the a1-d1-d4 triangle. If the first king is on the a1-d4
// diagonal, the other one shall not to be above the a1-h8 diagonal. // diagonal, the other one shall not to be above the a1-h8 diagonal.
std::vector<std::pair<int, Square>> bothOnDiagonal; std::vector<std::pair<int, Square>> bothOnDiagonal;
@@ -1524,7 +1525,7 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) {
// Check whether a position was repeated since the last zeroing move. // Check whether a position was repeated since the last zeroing move.
bool rep = pos.has_repeated(); bool rep = pos.has_repeated();
int dtz, bound = Options["Syzygy50MoveRule"] ? 900 : 1; int dtz, bound = Options["Syzygy50MoveRule"] ? (MAX_DTZ - 100) : 1;
// Probe and rank each move // Probe and rank each move
for (auto& m : rootMoves) for (auto& m : rootMoves)
@@ -1567,8 +1568,8 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) {
// Better moves are ranked higher. Certain wins are ranked equally. // Better moves are ranked higher. Certain wins are ranked equally.
// Losing moves are ranked equally unless a 50-move draw is in sight. // Losing moves are ranked equally unless a 50-move draw is in sight.
int r = dtz > 0 ? (dtz + cnt50 <= 99 && !rep ? 1000 : 1000 - (dtz + cnt50)) int r = dtz > 0 ? (dtz + cnt50 <= 99 && !rep ? MAX_DTZ : MAX_DTZ - (dtz + cnt50))
: dtz < 0 ? (-dtz * 2 + cnt50 < 100 ? -1000 : -1000 + (-dtz + cnt50)) : dtz < 0 ? (-dtz * 2 + cnt50 < 100 ? -MAX_DTZ : -MAX_DTZ + (-dtz + cnt50))
: 0; : 0;
m.tbRank = r; m.tbRank = r;
@@ -1576,9 +1577,9 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) {
// 1 cp to cursed wins and let it grow to 49 cp as the positions gets // 1 cp to cursed wins and let it grow to 49 cp as the positions gets
// closer to a real win. // closer to a real win.
m.tbScore = r >= bound ? VALUE_MATE - MAX_PLY - 1 m.tbScore = r >= bound ? VALUE_MATE - MAX_PLY - 1
: r > 0 ? Value((std::max( 3, r - 800) * int(PawnValueEg)) / 200) : r > 0 ? Value((std::max( 3, r - (MAX_DTZ - 200)) * int(PawnValueEg)) / 200)
: r == 0 ? VALUE_DRAW : r == 0 ? VALUE_DRAW
: r > -bound ? Value((std::min(-3, r + 800) * int(PawnValueEg)) / 200) : r > -bound ? Value((std::min(-3, r + (MAX_DTZ - 200)) * int(PawnValueEg)) / 200)
: -VALUE_MATE + MAX_PLY + 1; : -VALUE_MATE + MAX_PLY + 1;
} }
@@ -1592,7 +1593,7 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) {
// A return value false indicates that not all probes were successful. // A return value false indicates that not all probes were successful.
bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) { bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) {
static const int WDL_to_rank[] = { -1000, -899, 0, 899, 1000 }; static const int WDL_to_rank[] = { -MAX_DTZ, -MAX_DTZ + 101, 0, MAX_DTZ - 101, MAX_DTZ };
ProbeState result; ProbeState result;
StateInfo st; StateInfo st;
-2
View File
@@ -31,8 +31,6 @@ enum WDLScore {
WDLDraw = 0, // Draw WDLDraw = 0, // Draw
WDLCursedWin = 1, // Win, but draw under 50-move rule WDLCursedWin = 1, // Win, but draw under 50-move rule
WDLWin = 2, // Win WDLWin = 2, // Win
WDLScoreNone = -1000
}; };
// Possible states after a probing operation // Possible states after a probing operation
+4 -4
View File
@@ -60,15 +60,13 @@ void Thread::clear() {
counterMoves.fill(MOVE_NONE); counterMoves.fill(MOVE_NONE);
mainHistory.fill(0); mainHistory.fill(0);
captureHistory.fill(0); captureHistory.fill(0);
previousDepth = 0;
for (bool inCheck : { false, true }) for (bool inCheck : { false, true })
for (StatsType c : { NoCaptures, Captures }) for (StatsType c : { NoCaptures, Captures })
{
for (auto& to : continuationHistory[inCheck][c]) for (auto& to : continuationHistory[inCheck][c])
for (auto& h : to) for (auto& h : to)
h->fill(-71); h->fill(-71);
continuationHistory[inCheck][c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1);
}
} }
@@ -241,7 +239,9 @@ Thread* ThreadPool::get_best_thread() const {
} }
else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY
|| ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY
&& votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]])) && ( votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]
|| ( votes[th->rootMoves[0].pv[0]] == votes[bestThread->rootMoves[0].pv[0]]
&& th->rootMoves[0].pv.size() > bestThread->rootMoves[0].pv.size()))))
bestThread = th; bestThread = th;
} }
+1 -1
View File
@@ -70,7 +70,7 @@ public:
Position rootPos; Position rootPos;
StateInfo rootState; StateInfo rootState;
Search::RootMoves rootMoves; Search::RootMoves rootMoves;
Depth rootDepth, completedDepth, depth; Depth rootDepth, completedDepth, previousDepth;
Value rootDelta; Value rootDelta;
CounterMoveHistory counterMoves; CounterMoveHistory counterMoves;
ButterflyHistory mainHistory; ButterflyHistory mainHistory;
+1 -1
View File
@@ -80,7 +80,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
// game time for the current move, so also cap to 20% of available game time. // game time for the current move, so also cap to 20% of available game time.
if (limits.movestogo == 0) if (limits.movestogo == 0)
{ {
optScale = std::min(0.0084 + std::pow(ply + 3.0, 0.5) * 0.0042, optScale = std::min(0.0120 + std::pow(ply + 3.0, 0.45) * 0.0039,
0.2 * limits.time[us] / double(timeLeft)) 0.2 * limits.time[us] / double(timeLeft))
* optExtra; * optExtra;
maxScale = std::min(7.0, 4.0 + ply / 12.0); maxScale = std::min(7.0, 4.0 + ply / 12.0);
+67 -61
View File
@@ -41,14 +41,14 @@ extern vector<string> setup_bench(const Position&, istream&);
namespace { namespace {
// FEN string of the initial position, normal chess // FEN string for the initial position in standard chess
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
// position() is called when engine receives the "position" UCI command. // position() is called when the engine receives the "position" UCI command.
// The function sets up the position described in the given FEN string ("fen") // It sets up the position that is described in the given FEN string ("fen") or
// or the starting position ("startpos") and then makes the moves given in the // the initial position ("startpos") and then makes the moves given in the following
// following move list ("moves"). // move list ("moves").
void position(Position& pos, istringstream& is, StateListPtr& states) { void position(Position& pos, istringstream& is, StateListPtr& states) {
@@ -60,7 +60,7 @@ namespace {
if (token == "startpos") if (token == "startpos")
{ {
fen = StartFEN; fen = StartFEN;
is >> token; // Consume "moves" token if any is >> token; // Consume the "moves" token, if any
} }
else if (token == "fen") else if (token == "fen")
while (is >> token && token != "moves") while (is >> token && token != "moves")
@@ -68,10 +68,10 @@ namespace {
else else
return; return;
states = StateListPtr(new std::deque<StateInfo>(1)); // Drop old and create a new one states = StateListPtr(new std::deque<StateInfo>(1)); // Drop the old state and create a new one
pos.set(fen, Options["UCI_Chess960"], &states->back(), Threads.main()); pos.set(fen, Options["UCI_Chess960"], &states->back(), Threads.main());
// Parse move list (if any) // Parse the move list, if any
while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE)
{ {
states->emplace_back(); states->emplace_back();
@@ -79,8 +79,8 @@ namespace {
} }
} }
// trace_eval() prints the evaluation for the current position, consistent with the UCI // trace_eval() prints the evaluation of the current position, consistent with
// options set so far. // the UCI options set so far.
void trace_eval(Position& pos) { void trace_eval(Position& pos) {
@@ -94,20 +94,20 @@ namespace {
} }
// setoption() is called when engine receives the "setoption" UCI command. The // setoption() is called when the engine receives the "setoption" UCI command.
// function updates the UCI option ("name") to the given value ("value"). // The function updates the UCI option ("name") to the given value ("value").
void setoption(istringstream& is) { void setoption(istringstream& is) {
string token, name, value; string token, name, value;
is >> token; // Consume "name" token is >> token; // Consume the "name" token
// Read option name (can contain spaces) // Read the option name (can contain spaces)
while (is >> token && token != "value") while (is >> token && token != "value")
name += (name.empty() ? "" : " ") + token; name += (name.empty() ? "" : " ") + token;
// Read option value (can contain spaces) // Read the option value (can contain spaces)
while (is >> token) while (is >> token)
value += (value.empty() ? "" : " ") + token; value += (value.empty() ? "" : " ") + token;
@@ -118,9 +118,9 @@ namespace {
} }
// go() is called when engine receives the "go" UCI command. The function sets // go() is called when the engine receives the "go" UCI command. The function
// the thinking time and other parameters from the input string, then starts // sets the thinking time and other parameters from the input string, then starts
// the search. // with a search.
void go(Position& pos, istringstream& is, StateListPtr& states) { void go(Position& pos, istringstream& is, StateListPtr& states) {
@@ -128,7 +128,7 @@ namespace {
string token; string token;
bool ponderMode = false; bool ponderMode = false;
limits.startTime = now(); // As early as possible! limits.startTime = now(); // The search starts as early as possible
while (is >> token) while (is >> token)
if (token == "searchmoves") // Needs to be the last command on the line if (token == "searchmoves") // Needs to be the last command on the line
@@ -152,9 +152,9 @@ namespace {
} }
// bench() is called when engine receives the "bench" command. Firstly // bench() is called when the engine receives the "bench" command.
// a list of UCI commands is setup according to bench parameters, then // Firstly, a list of UCI commands is set up according to the bench
// it is run one by one printing a summary at the end. // parameters, then it is run one by one, printing a summary at the end.
void bench(Position& pos, istream& args, StateListPtr& states) { void bench(Position& pos, istream& args, StateListPtr& states) {
@@ -187,12 +187,12 @@ namespace {
} }
else if (token == "setoption") setoption(is); else if (token == "setoption") setoption(is);
else if (token == "position") position(pos, is, states); else if (token == "position") position(pos, is, states);
else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } // Search::clear() may take some while else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } // Search::clear() may take a while
} }
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero' elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
dbg_print(); // Just before exiting dbg_print();
if (Cluster::is_root()) if (Cluster::is_root())
cerr << "\n===========================" cerr << "\n==========================="
@@ -201,36 +201,36 @@ namespace {
<< "\nNodes/second : " << 1000 * nodes / elapsed << endl; << "\nNodes/second : " << 1000 * nodes / elapsed << endl;
} }
// The win rate model returns the probability (per mille) of winning given an eval // The win rate model returns the probability of winning (in per mille units) given an
// and a game-ply. The model fits rather accurately the LTC fishtest statistics. // eval and a game ply. It fits the LTC fishtest statistics rather accurately.
int win_rate_model(Value v, int ply) { int win_rate_model(Value v, int ply) {
// The model captures only up to 240 plies, so limit input (and rescale) // The model only captures up to 240 plies, so limit the input and then rescale
double m = std::min(240, ply) / 64.0; double m = std::min(240, ply) / 64.0;
// Coefficients of a 3rd order polynomial fit based on fishtest data // The coefficients of a third-order polynomial fit is based on the fishtest data
// for two parameters needed to transform eval to the argument of a // for two parameters that need to transform eval to the argument of a logistic
// logistic function. // function.
double as[] = {-3.68389304, 30.07065921, -60.52878723, 149.53378557}; double as[] = { 0.50379905, -4.12755858, 18.95487051, 152.00733652};
double bs[] = {-2.0181857, 15.85685038, -29.83452023, 47.59078827}; double bs[] = {-1.71790378, 10.71543602, -17.05515898, 41.15680404};
double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3];
double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3];
// Transform eval to centipawns with limited range // Transform the eval to centipawns with limited range
double x = std::clamp(double(100 * v) / PawnValueEg, -2000.0, 2000.0); double x = std::clamp(double(100 * v) / PawnValueEg, -2000.0, 2000.0);
// Return win rate in per mille (rounded to nearest) // Return the win rate in per mille units rounded to the nearest value
return int(0.5 + 1000 / (1 + std::exp((a - x) / b))); return int(0.5 + 1000 / (1 + std::exp((a - x) / b)));
} }
} // namespace } // namespace
/// UCI::loop() waits for a command from stdin, parses it and calls the appropriate /// UCI::loop() waits for a command from the stdin, parses it and then calls the appropriate
/// function. Also intercepts EOF from stdin to ensure gracefully exiting if the /// function. It also intercepts an end-of-file (EOF) indication from the stdin to ensure a
/// GUI dies unexpectedly. When called with some command line arguments, e.g. to /// graceful exit if the GUI dies unexpectedly. When called with some command-line arguments,
/// run 'bench', once the command is executed the function returns immediately. /// like running 'bench', the function returns immediately after the command is executed.
/// In addition to the UCI ones, also some additional debug commands are supported. /// In addition to the UCI ones, some additional debug commands are also supported.
void UCI::loop(int argc, char* argv[]) { void UCI::loop(int argc, char* argv[]) {
@@ -244,24 +244,24 @@ void UCI::loop(int argc, char* argv[]) {
cmd += std::string(argv[i]) + " "; cmd += std::string(argv[i]) + " ";
do { do {
if (argc == 1 && !Cluster::getline(cin, cmd)) // Block here waiting for input or EOF if (argc == 1 && !Cluster::getline(cin, cmd)) // Wait for an input or an end-of-file (EOF) indication
cmd = "quit"; cmd = "quit";
istringstream is(cmd); istringstream is(cmd);
token.clear(); // Avoid a stale if getline() returns empty or blank line token.clear(); // Avoid a stale if getline() returns nothing or a blank line
is >> skipws >> token; is >> skipws >> token;
if ( token == "quit" if ( token == "quit"
|| token == "stop") || token == "stop")
Threads.stop = true; Threads.stop = true;
// The GUI sends 'ponderhit' to tell us the user has played the expected move. // The GUI sends 'ponderhit' to tell that the user has played the expected move.
// So 'ponderhit' will be sent if we were told to ponder on the same move the // So, 'ponderhit' is sent if pondering was done on the same move that the user
// user has played. We should continue searching but switch from pondering to // has played. The search should continue, but should also switch from pondering
// normal search. // to the normal search.
else if (token == "ponderhit") else if (token == "ponderhit")
Threads.main()->ponder = false; // Switch to normal search Threads.main()->ponder = false; // Switch to the normal search
else if (token == "uci" && Cluster::is_root()) else if (token == "uci" && Cluster::is_root())
sync_cout << "id name " << engine_info(true) sync_cout << "id name " << engine_info(true)
@@ -275,8 +275,8 @@ void UCI::loop(int argc, char* argv[]) {
else if (token == "isready" && Cluster::is_root()) else if (token == "isready" && Cluster::is_root())
sync_cout << "readyok" << sync_endl; sync_cout << "readyok" << sync_endl;
// Additional custom non-UCI commands, mainly for debugging. // Add custom non-UCI commands, mainly for debugging purposes.
// Do not use these commands during a search! // These commands must not be used during a search!
else if (token == "flip") pos.flip(); else if (token == "flip") pos.flip();
else if (token == "bench") bench(pos, is, states); else if (token == "bench") bench(pos, is, states);
else if (token == "d" && Cluster::is_root()) else if (token == "d" && Cluster::is_root())
@@ -293,19 +293,25 @@ void UCI::loop(int argc, char* argv[]) {
filename = f; filename = f;
Eval::NNUE::save_eval(filename); Eval::NNUE::save_eval(filename);
} }
else if ((token == "--help" || token == "help" || token == "--license" || token == "license") && Cluster::is_root())
sync_cout << "\nStockfish is a powerful chess engine for playing and analyzing."
"\nIt is released as free software licensed under the GNU GPLv3 License."
"\nStockfish is normally used with a graphical user interface (GUI) and implements"
"\nthe Universal Chess Interface (UCI) protocol to communicate with a GUI, an API, etc."
"\nFor any further information, visit https://github.com/official-stockfish/Stockfish#readme"
"\nor read the corresponding README.md and Copying.txt files distributed along with this program.\n" << sync_endl;
else if (!token.empty() && token[0] != '#' && Cluster::is_root()) else if (!token.empty() && token[0] != '#' && Cluster::is_root())
sync_cout << "Unknown command: " << cmd << sync_endl; sync_cout << "Unknown command: '" << cmd << "'. Type help for more information." << sync_endl;
} while (token != "quit" && argc == 1); // Command line args are one-shot } while (token != "quit" && argc == 1); // The command-line arguments are one-shot
} }
/// UCI::value() converts a Value to a string suitable for use with the UCI /// UCI::value() converts a Value to a string by adhering to the UCI protocol specification:
/// protocol specification:
/// ///
/// cp <x> The score from the engine's point of view in centipawns. /// cp <x> The score from the engine's point of view in centipawns.
/// mate <y> Mate in y moves, not plies. If the engine is getting mated /// mate <y> Mate in 'y' moves (not plies). If the engine is getting mated,
/// use negative values for y. /// uses negative values for 'y'.
string UCI::value(Value v) { string UCI::value(Value v) {
@@ -322,8 +328,8 @@ string UCI::value(Value v) {
} }
/// UCI::wdl() report WDL statistics given an evaluation and a game ply, based on /// UCI::wdl() reports the win-draw-loss (WDL) statistics given an evaluation
/// data gathered for fishtest LTC games. /// and a game ply based on the data gathered for fishtest LTC games.
string UCI::wdl(Value v, int ply) { string UCI::wdl(Value v, int ply) {
@@ -346,9 +352,9 @@ std::string UCI::square(Square s) {
/// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q). /// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q).
/// The only special case is castling, where we print in the e1g1 notation in /// The only special case is castling where the e1g1 notation is printed in
/// normal chess mode, and in e1h1 notation in chess960 mode. Internally all /// standard chess mode and in e1h1 notation it is printed in Chess960 mode.
/// castling moves are always encoded as 'king captures rook'. /// Internally, all castling moves are always encoded as 'king captures rook'.
string UCI::move(Move m, bool chess960) { string UCI::move(Move m, bool chess960) {
@@ -378,8 +384,8 @@ string UCI::move(Move m, bool chess960) {
Move UCI::to_move(const Position& pos, string& str) { Move UCI::to_move(const Position& pos, string& str) {
if (str.length() == 5) // Junior could send promotion piece in uppercase if (str.length() == 5)
str[4] = char(tolower(str[4])); str[4] = char(tolower(str[4])); // The promotion piece character must be lowercased
for (const auto& m : MoveList<LEGAL>(pos)) for (const auto& m : MoveList<LEGAL>(pos))
if (str == UCI::move(m, pos.is_chess960())) if (str == UCI::move(m, pos.is_chess960()))
+3 -3
View File
@@ -32,15 +32,15 @@ namespace UCI {
class Option; class Option;
/// Custom comparator because UCI options should be case insensitive /// Define a custom comparator, because the UCI options should be case-insensitive
struct CaseInsensitiveLess { struct CaseInsensitiveLess {
bool operator() (const std::string&, const std::string&) const; bool operator() (const std::string&, const std::string&) const;
}; };
/// Our options container is actually a std::map /// The options container is defined as a std::map
typedef std::map<std::string, Option, CaseInsensitiveLess> OptionsMap; typedef std::map<std::string, Option, CaseInsensitiveLess> OptionsMap;
/// Option class implements an option as defined by UCI protocol /// The Option class implements each option as specified by the UCI protocol
class Option { class Option {
typedef void (*OnChange)(const Option&); typedef void (*OnChange)(const Option&);
+1 -1
View File
@@ -61,7 +61,7 @@ void init(OptionsMap& o) {
constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048;
o["Debug Log File"] << Option("", on_logger); o["Debug Log File"] << Option("", on_logger);
o["Threads"] << Option(1, 1, 512, on_threads); o["Threads"] << Option(1, 1, 1024, on_threads);
o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size);
o["Clear Hash"] << Option(on_clear_hash); o["Clear Hash"] << Option(on_clear_hash);
o["Ponder"] << Option(false); o["Ponder"] << Option(false);