mirror of
https://github.com/opelly27/Stockfish.git
synced 2026-05-20 06:17:49 +00:00
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:
@@ -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
|
||||||
@@ -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!
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
[](https://github.com/official-stockfish/Stockfish/actions)
|
[Stockfish][website-link] is a free, powerful UCI chess engine derived from
|
||||||
[](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
@@ -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
@@ -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,23 +700,21 @@ 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
|
|
||||||
|
|
||||||
### 3.9 Android 5 can only run position independent executables. Note that this
|
### 3.9 Android 5 can only run position independent executables. Note that this
|
||||||
### breaks Android 4.0 and earlier.
|
### breaks Android 4.0 and earlier.
|
||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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()))
|
||||||
|
|||||||
@@ -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
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user