mirror of
https://github.com/sockspls/badfish
synced 2025-04-29 16:23:09 +00:00
Merge NNUE (master) in the cluster branch.
fixes minor merge conflicts, and a first quick testing looks OK: 4mpix4th vs 4th at 30+0.3s: Score of cluster vs master: 3 - 0 - 37 [0.537] 40 Elo difference: 26.1 +/- 28.5, LOS: 95.8 %, DrawRatio: 92.5 % No functional change.
This commit is contained in:
commit
19129473f2
62 changed files with 3131 additions and 541 deletions
56
.travis.yml
56
.travis.yml
|
@ -1,5 +1,5 @@
|
||||||
language: cpp
|
language: cpp
|
||||||
dist: xenial
|
dist: bionic
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
|
@ -7,7 +7,6 @@ matrix:
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
sources: ['ubuntu-toolchain-r-test']
|
|
||||||
packages: ['g++-8', 'g++-8-multilib', 'g++-multilib', 'valgrind', 'expect', 'curl']
|
packages: ['g++-8', 'g++-8-multilib', 'g++-multilib', 'valgrind', 'expect', 'curl']
|
||||||
env:
|
env:
|
||||||
- COMPILER=g++-8
|
- COMPILER=g++-8
|
||||||
|
@ -17,23 +16,23 @@ matrix:
|
||||||
compiler: clang
|
compiler: clang
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-xenial-6.0']
|
packages: ['clang-10', 'llvm-10-dev', 'g++-multilib', 'valgrind', 'expect', 'curl']
|
||||||
packages: ['clang-6.0', 'llvm-6.0-dev', 'g++-multilib', 'valgrind', 'expect', 'curl']
|
|
||||||
env:
|
env:
|
||||||
- COMPILER=clang++-6.0
|
- COMPILER=clang++-10
|
||||||
- COMP=clang
|
- COMP=clang
|
||||||
- LDFLAGS=-fuse-ld=lld
|
|
||||||
|
|
||||||
- os: osx
|
- os: osx
|
||||||
|
osx_image: xcode12
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
env:
|
env:
|
||||||
- COMPILER=g++
|
- COMPILER=g++
|
||||||
- COMP=gcc
|
- COMP=gcc
|
||||||
|
|
||||||
- os: osx
|
- os: osx
|
||||||
|
osx_image: xcode12
|
||||||
compiler: clang
|
compiler: clang
|
||||||
env:
|
env:
|
||||||
- COMPILER=clang++ V='Apple LLVM 9.4.1' # Apple LLVM version 9.1.0 (clang-902.0.39.2)
|
- COMPILER=clang++
|
||||||
- COMP=clang
|
- COMP=clang
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
|
@ -44,30 +43,57 @@ before_script:
|
||||||
- cd src
|
- cd src
|
||||||
|
|
||||||
script:
|
script:
|
||||||
|
# Download net
|
||||||
|
- make net
|
||||||
|
|
||||||
# Obtain bench reference from git log
|
# Obtain bench reference from git log
|
||||||
- git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig
|
- git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig
|
||||||
- export benchref=$(cat git_sig)
|
- export benchref=$(cat git_sig)
|
||||||
- echo "Reference bench:" $benchref
|
- echo "Reference bench:" $benchref
|
||||||
#
|
|
||||||
|
# Compiler version string
|
||||||
|
- $COMPILER -v
|
||||||
|
|
||||||
|
# test help target
|
||||||
|
- make help
|
||||||
|
|
||||||
# Verify bench number against various builds
|
# Verify bench number against various builds
|
||||||
- export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG"
|
- export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG"
|
||||||
- make clean && make -j2 ARCH=x86-64 optimize=no debug=yes build && ../tests/signature.sh $benchref
|
- make clean && make -j2 ARCH=x86-64-modern optimize=no debug=yes build && ../tests/signature.sh $benchref
|
||||||
- make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref
|
- export CXXFLAGS="-Werror"
|
||||||
- make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref
|
- make clean && make -j2 ARCH=x86-64-modern build && ../tests/signature.sh $benchref
|
||||||
|
- make clean && make -j2 ARCH=x86-64-ssse3 build && ../tests/signature.sh $benchref
|
||||||
|
- make clean && make -j2 ARCH=x86-64-sse3-popcnt build && ../tests/signature.sh $benchref
|
||||||
|
- make clean && make -j2 ARCH=x86-64 build && ../tests/signature.sh $benchref
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=general-64 build && ../tests/signature.sh $benchref; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32-sse41-popcnt build && ../tests/signature.sh $benchref; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32-sse2 build && ../tests/signature.sh $benchref; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=general-32 build && ../tests/signature.sh $benchref; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "linux" && "$COMP" == "gcc" ]]; then make clean && make -j2 ARCH=x86-64-modern profile-build && ../tests/signature.sh $benchref; fi
|
||||||
|
|
||||||
|
# compile only for some more advanced architectures (might not run in travis)
|
||||||
|
- make clean && make -j2 ARCH=x86-64-avx2 build
|
||||||
|
- make clean && make -j2 ARCH=x86-64-bmi2 build
|
||||||
|
# needs gcc 10 to compile
|
||||||
|
- if [[ "$COMPILER" != "g++-8" ]]; then make clean && make -j2 ARCH=x86-64-avx512 build; fi
|
||||||
|
|
||||||
#
|
#
|
||||||
# Check perft and reproducible search
|
# Check perft and reproducible search
|
||||||
|
- make clean && make -j2 ARCH=x86-64-modern build
|
||||||
- ../tests/perft.sh
|
- ../tests/perft.sh
|
||||||
- ../tests/reprosearch.sh
|
- ../tests/reprosearch.sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Valgrind
|
# Valgrind
|
||||||
#
|
#
|
||||||
- export CXXFLAGS="-O1 -fno-inline"
|
- export CXXFLAGS="-O1 -fno-inline"
|
||||||
- if [ -x "$(command -v valgrind )" ]; then make clean && make -j2 ARCH=x86-64 debug=yes optimize=no build > /dev/null && ../tests/instrumented.sh --valgrind; fi
|
- if [ -x "$(command -v valgrind )" ]; then make clean && make -j2 ARCH=x86-64-modern debug=yes optimize=no build > /dev/null && ../tests/instrumented.sh --valgrind; fi
|
||||||
- if [ -x "$(command -v valgrind )" ]; then ../tests/instrumented.sh --valgrind-thread; fi
|
- if [ -x "$(command -v valgrind )" ]; then ../tests/instrumented.sh --valgrind-thread; fi
|
||||||
|
|
||||||
#
|
#
|
||||||
# Sanitizer
|
# Sanitizer
|
||||||
#
|
#
|
||||||
# Use g++-8 as a proxy for having sanitizers, might need revision as they become available for more recent versions of clang/gcc
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64-modern sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi
|
||||||
- if [[ "$COMPILER" == "g++-8" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64-modern sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi
|
||||||
- if [[ "$COMPILER" == "g++-8" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi
|
|
||||||
|
|
21
AUTHORS
21
AUTHORS
|
@ -1,10 +1,17 @@
|
||||||
# List of authors for Stockfish, as of March 30, 2020
|
# List of authors for Stockfish, as of August 4, 2020
|
||||||
|
|
||||||
|
# Founders of the Stockfish project and fishtest infrastructure
|
||||||
Tord Romstad (romstad)
|
Tord Romstad (romstad)
|
||||||
Marco Costalba (mcostalba)
|
Marco Costalba (mcostalba)
|
||||||
Joona Kiiski (zamar)
|
Joona Kiiski (zamar)
|
||||||
Gary Linscott (glinscott)
|
Gary Linscott (glinscott)
|
||||||
|
|
||||||
|
# Authors and inventors of NNUE, training, NNUE port
|
||||||
|
Yu Nasu (ynasu87)
|
||||||
|
Motohiro Isozaki (yaneurao)
|
||||||
|
Hisayori Noda (nodchip)
|
||||||
|
|
||||||
|
# all other authors of the code in alphabetical order
|
||||||
Aditya (absimaldata)
|
Aditya (absimaldata)
|
||||||
Adrian Petrescu (apetresc)
|
Adrian Petrescu (apetresc)
|
||||||
Ajith Chandy Jose (ajithcj)
|
Ajith Chandy Jose (ajithcj)
|
||||||
|
@ -36,6 +43,7 @@ Dariusz Orzechowski
|
||||||
David Zar
|
David Zar
|
||||||
Daylen Yang (daylen)
|
Daylen Yang (daylen)
|
||||||
DiscanX
|
DiscanX
|
||||||
|
Dominik Schlösser (domschl)
|
||||||
double-beep
|
double-beep
|
||||||
Eduardo Cáceres (eduherminio)
|
Eduardo Cáceres (eduherminio)
|
||||||
Eelco de Groot (KingDefender)
|
Eelco de Groot (KingDefender)
|
||||||
|
@ -45,6 +53,7 @@ Ernesto Gatti
|
||||||
Linmiao Xu (linrock)
|
Linmiao Xu (linrock)
|
||||||
Fabian Beuke (madnight)
|
Fabian Beuke (madnight)
|
||||||
Fabian Fichter (ianfab)
|
Fabian Fichter (ianfab)
|
||||||
|
Fanael Linithien (Fanael)
|
||||||
fanon
|
fanon
|
||||||
Fauzi Akram Dabat (FauziAkram)
|
Fauzi Akram Dabat (FauziAkram)
|
||||||
Felix Wittmann
|
Felix Wittmann
|
||||||
|
@ -71,6 +80,7 @@ Jean Gauthier (OuaisBla)
|
||||||
Jean-Francois Romang (jromang)
|
Jean-Francois Romang (jromang)
|
||||||
Jekaa
|
Jekaa
|
||||||
Jerry Donald Watson (jerrydonaldwatson)
|
Jerry Donald Watson (jerrydonaldwatson)
|
||||||
|
jjoshua2
|
||||||
Jonathan Calovski (Mysseno)
|
Jonathan Calovski (Mysseno)
|
||||||
Jonathan Dumale (SFisGOD)
|
Jonathan Dumale (SFisGOD)
|
||||||
Joost VandeVondele (vondele)
|
Joost VandeVondele (vondele)
|
||||||
|
@ -115,7 +125,9 @@ Nick Pelling (nickpelling)
|
||||||
Nicklas Persson (NicklasPersson)
|
Nicklas Persson (NicklasPersson)
|
||||||
Niklas Fiekas (niklasf)
|
Niklas Fiekas (niklasf)
|
||||||
Nikolay Kostov (NikolayIT)
|
Nikolay Kostov (NikolayIT)
|
||||||
Nguyen Pham
|
Nguyen Pham (nguyenpham)
|
||||||
|
Norman Schmidt (FireFather)
|
||||||
|
notruck
|
||||||
Ondrej Mosnáček (WOnder93)
|
Ondrej Mosnáček (WOnder93)
|
||||||
Oskar Werkelin Ahlin
|
Oskar Werkelin Ahlin
|
||||||
Pablo Vazquez
|
Pablo Vazquez
|
||||||
|
@ -135,14 +147,17 @@ Richard Lloyd
|
||||||
Rodrigo Exterckötter Tjäder
|
Rodrigo Exterckötter Tjäder
|
||||||
Ron Britvich (Britvich)
|
Ron Britvich (Britvich)
|
||||||
Ronald de Man (syzygy1, syzygy)
|
Ronald de Man (syzygy1, syzygy)
|
||||||
|
rqs
|
||||||
Ryan Schmitt
|
Ryan Schmitt
|
||||||
Ryan Takker
|
Ryan Takker
|
||||||
Sami Kiminki (skiminki)
|
Sami Kiminki (skiminki)
|
||||||
Sebastian Buchwald (UniQP)
|
Sebastian Buchwald (UniQP)
|
||||||
Sergei Antonov (saproj)
|
Sergei Antonov (saproj)
|
||||||
Sergei Ivanov (svivanov72)
|
Sergei Ivanov (svivanov72)
|
||||||
|
Sergio Vieri (sergiovieri)
|
||||||
sf-x
|
sf-x
|
||||||
Shane Booth (shane31)
|
Shane Booth (shane31)
|
||||||
|
Shawn Varghese (xXH4CKST3RXx)
|
||||||
Stefan Geschwentner (locutus2)
|
Stefan Geschwentner (locutus2)
|
||||||
Stefano Cardanobile (Stefano80)
|
Stefano Cardanobile (Stefano80)
|
||||||
Steinar Gunderson (sesse)
|
Steinar Gunderson (sesse)
|
||||||
|
@ -155,9 +170,11 @@ Tom Vijlbrief (tomtor)
|
||||||
Tomasz Sobczyk (Sopel97)
|
Tomasz Sobczyk (Sopel97)
|
||||||
Torsten Franz (torfranz, tfranzer)
|
Torsten Franz (torfranz, tfranzer)
|
||||||
Tracey Emery (basepr1me)
|
Tracey Emery (basepr1me)
|
||||||
|
tttak
|
||||||
Unai Corzo (unaiic)
|
Unai Corzo (unaiic)
|
||||||
Uri Blass (uriblass)
|
Uri Blass (uriblass)
|
||||||
Vince Negri (cuddlestmonkey)
|
Vince Negri (cuddlestmonkey)
|
||||||
|
zz4032
|
||||||
|
|
||||||
|
|
||||||
# Additionally, we acknowledge the authors and maintainers of fishtest,
|
# Additionally, we acknowledge the authors and maintainers of fishtest,
|
||||||
|
|
|
@ -4,7 +4,13 @@
|
||||||
[](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master)
|
[](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master)
|
||||||
|
|
||||||
[Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine
|
[Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine
|
||||||
derived from Glaurung 2.1. It is not a complete chess program and requires a
|
derived from Glaurung 2.1. It features two evaluation functions, the classical
|
||||||
|
evaluation based on handcrafted terms, and the NNUE evaluation based on
|
||||||
|
efficiently updateable neural networks. The classical evaluation runs efficiently
|
||||||
|
on most 64bit CPU architectures, while the NNUE evaluation benefits strongly from the
|
||||||
|
vector intrinsics available on modern CPUs (avx2 or similar).
|
||||||
|
|
||||||
|
Stockfish is not a complete chess program and requires a
|
||||||
UCI-compatible GUI (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard, Arena,
|
UCI-compatible GUI (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard, Arena,
|
||||||
Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably.
|
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
|
Read the documentation for your GUI of choice for information about how to use
|
||||||
|
@ -22,21 +28,20 @@ This distribution of Stockfish consists of the following files:
|
||||||
* src, a subdirectory containing the full source code, including a Makefile
|
* src, 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.
|
||||||
|
|
||||||
|
To use the NNUE evaluation an additional data file with neural network parameters
|
||||||
|
needs to be downloaded. The filename for the default set can be found as the default
|
||||||
|
value of the `EvalFile` UCI option, with the format
|
||||||
|
`nn-[SHA256 first 12 digits].nnue` (e.g. nn-c157e0a5755b.nnue). This file can be downloaded from
|
||||||
|
```
|
||||||
|
https://tests.stockfishchess.org/api/nn/[filename]
|
||||||
|
```
|
||||||
|
replacing `[filename]` as needed.
|
||||||
|
|
||||||
## UCI parameters
|
|
||||||
|
## UCI options
|
||||||
|
|
||||||
Currently, Stockfish has the following UCI options:
|
Currently, Stockfish has the following UCI options:
|
||||||
|
|
||||||
* #### Debug Log File
|
|
||||||
Write all communication to and from the engine into a text file.
|
|
||||||
|
|
||||||
* #### Contempt
|
|
||||||
A positive value for contempt favors middle game positions and avoids draws.
|
|
||||||
|
|
||||||
* #### Analysis Contempt
|
|
||||||
By default, contempt is set to prefer the side to move. Set this option to "White"
|
|
||||||
or "Black" to analyse with contempt for that side, or "Off" to disable contempt.
|
|
||||||
|
|
||||||
* #### Threads
|
* #### Threads
|
||||||
The number of CPU threads used for searching a position. For best performance, set
|
The number of CPU threads used for searching a position. For best performance, set
|
||||||
this equal to the number of CPU cores available.
|
this equal to the number of CPU cores available.
|
||||||
|
@ -44,9 +49,6 @@ Currently, Stockfish has the following UCI options:
|
||||||
* #### Hash
|
* #### Hash
|
||||||
The size of the hash table in MB. It is recommended to set Hash after setting Threads.
|
The size of the hash table in MB. It is recommended to set Hash after setting Threads.
|
||||||
|
|
||||||
* #### Clear Hash
|
|
||||||
Clear the hash table.
|
|
||||||
|
|
||||||
* #### Ponder
|
* #### Ponder
|
||||||
Let Stockfish ponder its next move while the opponent is thinking.
|
Let Stockfish ponder its next move while the opponent is thinking.
|
||||||
|
|
||||||
|
@ -54,10 +56,32 @@ Currently, Stockfish has the following UCI options:
|
||||||
Output the N best lines (principal variations, PVs) when searching.
|
Output the N best lines (principal variations, PVs) when searching.
|
||||||
Leave at 1 for best performance.
|
Leave at 1 for best performance.
|
||||||
|
|
||||||
* #### Skill Level
|
* #### Use NNUE
|
||||||
Lower the Skill Level in order to make Stockfish play weaker (see also UCI_LimitStrength).
|
Toggle between the NNUE and classical evaluation functions. If set to "true",
|
||||||
Internally, MultiPV is enabled, and with a certain probability depending on the Skill Level a
|
the network parameters must be available to load from file (see also EvalFile).
|
||||||
weaker move will be played.
|
|
||||||
|
* #### EvalFile
|
||||||
|
The name of the file of the NNUE evaluation parameters. Depending on the GUI the
|
||||||
|
filename should include the full path to the folder/directory that contains the file.
|
||||||
|
|
||||||
|
* #### Contempt
|
||||||
|
A positive value for contempt favors middle game positions and avoids draws,
|
||||||
|
effective for the classical evaluation only.
|
||||||
|
|
||||||
|
* #### Analysis Contempt
|
||||||
|
By default, contempt is set to prefer the side to move. Set this option to "White"
|
||||||
|
or "Black" to analyse with contempt for that side, or "Off" to disable contempt.
|
||||||
|
|
||||||
|
* #### UCI_AnalyseMode
|
||||||
|
An option handled by your GUI.
|
||||||
|
|
||||||
|
* #### UCI_Chess960
|
||||||
|
An option handled by your GUI. If true, Stockfish will play Chess960.
|
||||||
|
|
||||||
|
* #### UCI_ShowWDL
|
||||||
|
If enabled, show approximate WDL statistics as part of the engine output.
|
||||||
|
These WDL numbers model expected game outcomes for a given evaluation and
|
||||||
|
game ply for engine self-play at fishtest LTC conditions (60+0.6s per game).
|
||||||
|
|
||||||
* #### UCI_LimitStrength
|
* #### UCI_LimitStrength
|
||||||
Enable weaker play aiming for an Elo rating as set by UCI_Elo. This option overrides Skill Level.
|
Enable weaker play aiming for an Elo rating as set by UCI_Elo. This option overrides Skill Level.
|
||||||
|
@ -66,26 +90,10 @@ Currently, Stockfish has the following UCI options:
|
||||||
If enabled by UCI_LimitStrength, aim for an engine strength of the given Elo.
|
If enabled by UCI_LimitStrength, aim for an engine strength of the given Elo.
|
||||||
This Elo rating has been calibrated at a time control of 60s+0.6s and anchored to CCRL 40/4.
|
This Elo rating has been calibrated at a time control of 60s+0.6s and anchored to CCRL 40/4.
|
||||||
|
|
||||||
* #### Move Overhead
|
* #### Skill Level
|
||||||
Assume a time delay of x ms due to network and GUI overheads. This is useful to
|
Lower the Skill Level in order to make Stockfish play weaker (see also UCI_LimitStrength).
|
||||||
avoid losses on time in those cases.
|
Internally, MultiPV is enabled, and with a certain probability depending on the Skill Level a
|
||||||
|
weaker move will be played.
|
||||||
* #### Minimum Thinking Time
|
|
||||||
Search for at least x ms per move.
|
|
||||||
|
|
||||||
* #### Slow Mover
|
|
||||||
Lower values will make Stockfish take less time in games, higher values will
|
|
||||||
make it think longer.
|
|
||||||
|
|
||||||
* #### nodestime
|
|
||||||
Tells the engine to use nodes searched instead of wall time to account for
|
|
||||||
elapsed time. Useful for engine testing.
|
|
||||||
|
|
||||||
* #### UCI_Chess960
|
|
||||||
An option handled by your GUI. If true, Stockfish will play Chess960.
|
|
||||||
|
|
||||||
* #### UCI_AnalyseMode
|
|
||||||
An option handled by your GUI.
|
|
||||||
|
|
||||||
* #### SyzygyPath
|
* #### SyzygyPath
|
||||||
Path to the folders/directories storing the Syzygy tablebase files. Multiple
|
Path to the folders/directories storing the Syzygy tablebase files. Multiple
|
||||||
|
@ -112,6 +120,47 @@ Currently, Stockfish has the following UCI options:
|
||||||
Limit Syzygy tablebase probing to positions with at most this many pieces left
|
Limit Syzygy tablebase probing to positions with at most this many pieces left
|
||||||
(including kings and pawns).
|
(including kings and pawns).
|
||||||
|
|
||||||
|
* #### Move Overhead
|
||||||
|
Assume a time delay of x ms due to network and GUI overheads. This is useful to
|
||||||
|
avoid losses on time in those cases.
|
||||||
|
|
||||||
|
* #### Slow Mover
|
||||||
|
Lower values will make Stockfish take less time in games, higher values will
|
||||||
|
make it think longer.
|
||||||
|
|
||||||
|
* #### nodestime
|
||||||
|
Tells the engine to use nodes searched instead of wall time to account for
|
||||||
|
elapsed time. Useful for engine testing.
|
||||||
|
|
||||||
|
* #### Clear Hash
|
||||||
|
Clear the hash table.
|
||||||
|
|
||||||
|
* #### Debug Log File
|
||||||
|
Write all communication to and from the engine into a text file.
|
||||||
|
|
||||||
|
## Classical and NNUE evaluation
|
||||||
|
|
||||||
|
Both approaches assign a value to a position that is used in alpha-beta (PVS) search
|
||||||
|
to find the best move. The classical evaluation computes this value as a function
|
||||||
|
of various chess concepts, handcrafted by experts, tested and tuned using fishtest.
|
||||||
|
The NNUE evaluation computes this value with a neural network based on basic
|
||||||
|
inputs (e.g. piece positions only). The network is optimized and trained
|
||||||
|
on the evalutions of millions of positions at moderate search depth.
|
||||||
|
|
||||||
|
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
|
||||||
|
of the neural network need to be updated after a typical chess move.
|
||||||
|
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
|
||||||
|
tools to train and develop the NNUE networks.
|
||||||
|
|
||||||
|
On CPUs supporting modern vector instructions (avx2 and similar), the NNUE evaluation
|
||||||
|
results in stronger playing strength, even if the nodes per second computed by the engine
|
||||||
|
is somewhat lower (roughly 60% of nps is typical).
|
||||||
|
|
||||||
|
Note that the NNUE evaluation depends on the Stockfish binary and the network parameter
|
||||||
|
file (see EvalFile). Not every parameter file is compatible with a given Stockfish binary.
|
||||||
|
The default value of the EvalFile UCI option is the name of a network that is guaranteed
|
||||||
|
to be compatible with that binary.
|
||||||
|
|
||||||
## What to expect from Syzygybases?
|
## What to expect from Syzygybases?
|
||||||
|
|
||||||
|
@ -191,7 +240,6 @@ afterwards. Due to memory fragmentation, it may not always be
|
||||||
possible to allocate large pages even when enabled. A reboot
|
possible to allocate large pages even when enabled. A reboot
|
||||||
might alleviate this problem. To determine whether large pages
|
might alleviate this problem. To determine whether large pages
|
||||||
are in use, see the engine log.
|
are in use, see the engine log.
|
||||||
>>>>>>> master
|
|
||||||
|
|
||||||
## Compiling Stockfish yourself from the sources
|
## Compiling Stockfish yourself from the sources
|
||||||
|
|
31
appveyor.yml
31
appveyor.yml
|
@ -4,10 +4,9 @@ clone_depth: 50
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
- appveyor
|
|
||||||
|
|
||||||
# Operating system (build VM template)
|
# Operating system (build VM template)
|
||||||
os: Visual Studio 2017
|
os: Visual Studio 2019
|
||||||
|
|
||||||
# Build platform, i.e. x86, x64, AnyCPU. This setting is optional.
|
# Build platform, i.e. x86, x64, AnyCPU. This setting is optional.
|
||||||
platform:
|
platform:
|
||||||
|
@ -36,8 +35,11 @@ before_build:
|
||||||
$src = $src.Replace("\", "/")
|
$src = $src.Replace("\", "/")
|
||||||
|
|
||||||
# Build CMakeLists.txt
|
# Build CMakeLists.txt
|
||||||
$t = 'cmake_minimum_required(VERSION 3.8)',
|
$t = 'cmake_minimum_required(VERSION 3.17)',
|
||||||
'project(Stockfish)',
|
'project(Stockfish)',
|
||||||
|
'set(CMAKE_CXX_STANDARD 17)',
|
||||||
|
'set(CMAKE_CXX_STANDARD_REQUIRED ON)',
|
||||||
|
'set (CMAKE_CXX_EXTENSIONS OFF)',
|
||||||
'set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src)',
|
'set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src)',
|
||||||
'set(source_files', $src, ')',
|
'set(source_files', $src, ')',
|
||||||
'add_executable(stockfish ${source_files})'
|
'add_executable(stockfish ${source_files})'
|
||||||
|
@ -51,13 +53,28 @@ before_build:
|
||||||
$b = git log HEAD | sls "\b[Bb]ench[ :]+[0-9]{7}" | select -first 1
|
$b = git log HEAD | sls "\b[Bb]ench[ :]+[0-9]{7}" | select -first 1
|
||||||
$bench = $b -match '\D+(\d+)' | % { $matches[1] }
|
$bench = $b -match '\D+(\d+)' | % { $matches[1] }
|
||||||
Write-Host "Reference bench:" $bench
|
Write-Host "Reference bench:" $bench
|
||||||
$g = "Visual Studio 15 2017"
|
$g = "Visual Studio 16 2019"
|
||||||
If (${env:PLATFORM} -eq 'x64') { $g = $g + ' Win64' }
|
If (${env:PLATFORM} -eq 'x64') { $a = "x64" }
|
||||||
cmake -G "${g}" .
|
If (${env:PLATFORM} -eq 'x86') { $a = "Win32" }
|
||||||
Write-Host "Generated files for: " $g
|
cmake -G "${g}" -A ${a} .
|
||||||
|
Write-Host "Generated files for: " $g $a
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- cmake --build . --config %CONFIGURATION% -- /verbosity:minimal
|
- cmake --build . --config %CONFIGURATION% -- /verbosity:minimal
|
||||||
|
- ps: |
|
||||||
|
# Download default NNUE net from fishtest
|
||||||
|
$nnuenet = Get-Content -Path src\ucioption.cpp | Select-String -CaseSensitive -Pattern "Option" | Select-String -CaseSensitive -Pattern "nn-[a-z0-9]{12}.nnue"
|
||||||
|
$dummy = $nnuenet -match "(?<nnuenet>nn-[a-z0-9]{12}.nnue)"
|
||||||
|
$nnuenet = $Matches.nnuenet
|
||||||
|
Write-Host "Default net:" $nnuenet
|
||||||
|
$nnuedownloadurl = "https://tests.stockfishchess.org/api/nn/$nnuenet"
|
||||||
|
$nnuefilepath = "src\${env:CONFIGURATION}\$nnuenet"
|
||||||
|
if (Test-Path -Path $nnuefilepath) {
|
||||||
|
Write-Host "Already available."
|
||||||
|
} else {
|
||||||
|
Write-Host "Downloading $nnuedownloadurl to $nnuefilepath"
|
||||||
|
Invoke-WebRequest -Uri $nnuedownloadurl -OutFile $nnuefilepath
|
||||||
|
}
|
||||||
|
|
||||||
before_test:
|
before_test:
|
||||||
- cd src/%CONFIGURATION%
|
- cd src/%CONFIGURATION%
|
||||||
|
|
431
src/Makefile
431
src/Makefile
|
@ -38,11 +38,12 @@ PGOBENCH = ./$(EXE) bench
|
||||||
### Source and object files
|
### Source and object files
|
||||||
SRCS = benchmark.cpp bitbase.cpp bitboard.cpp cluster.cpp endgame.cpp evaluate.cpp main.cpp \
|
SRCS = benchmark.cpp bitbase.cpp bitboard.cpp cluster.cpp endgame.cpp evaluate.cpp main.cpp \
|
||||||
material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \
|
material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \
|
||||||
search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp
|
search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
|
||||||
|
nnue/evaluate_nnue.cpp nnue/features/half_kp.cpp
|
||||||
|
|
||||||
OBJS = $(notdir $(SRCS:.cpp=.o))
|
OBJS = $(notdir $(SRCS:.cpp=.o))
|
||||||
|
|
||||||
VPATH = syzygy
|
VPATH = syzygy:nnue:nnue/features
|
||||||
|
|
||||||
### Establish the operating system name
|
### Establish the operating system name
|
||||||
KERNEL = $(shell uname -s)
|
KERNEL = $(shell uname -s)
|
||||||
|
@ -66,8 +67,16 @@ endif
|
||||||
# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
|
# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system
|
||||||
# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction
|
# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction
|
||||||
# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction
|
# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction
|
||||||
# sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions
|
|
||||||
# pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction
|
# pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction
|
||||||
|
# sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions
|
||||||
|
# mmx = yes/no --- -mmmx --- Use Intel MMX instructions
|
||||||
|
# sse2 = yes/no --- -msse2 --- Use Intel Streaming SIMD Extensions 2
|
||||||
|
# ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3
|
||||||
|
# sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1
|
||||||
|
# avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2
|
||||||
|
# avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512
|
||||||
|
# vnni = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512
|
||||||
|
# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture
|
||||||
# mpi = yes/no --- -DUSE_MPI --- Use Message Passing Interface
|
# mpi = yes/no --- -DUSE_MPI --- Use Message Passing Interface
|
||||||
#
|
#
|
||||||
# Note that Makefile is space sensitive, so when adding new architectures
|
# Note that Makefile is space sensitive, so when adding new architectures
|
||||||
|
@ -75,69 +84,176 @@ endif
|
||||||
# at the end of the line for flag values.
|
# at the end of the line for flag values.
|
||||||
|
|
||||||
### 2.1. General and architecture defaults
|
### 2.1. General and architecture defaults
|
||||||
|
|
||||||
|
ifeq ($(ARCH),)
|
||||||
|
empty_arch = yes
|
||||||
|
endif
|
||||||
|
|
||||||
optimize = yes
|
optimize = yes
|
||||||
debug = no
|
debug = no
|
||||||
sanitize = no
|
sanitize = no
|
||||||
bits = 64
|
bits = 64
|
||||||
prefetch = no
|
prefetch = no
|
||||||
popcnt = no
|
popcnt = no
|
||||||
sse = no
|
|
||||||
pext = no
|
pext = no
|
||||||
|
sse = no
|
||||||
|
mmx = no
|
||||||
|
sse2 = no
|
||||||
|
ssse3 = no
|
||||||
|
sse41 = no
|
||||||
|
avx2 = no
|
||||||
|
avx512 = no
|
||||||
|
vnni = no
|
||||||
|
neon = no
|
||||||
mpi = no
|
mpi = no
|
||||||
|
ARCH = x86-64-modern
|
||||||
|
STRIP = strip
|
||||||
|
|
||||||
### 2.2 Architecture specific
|
### 2.2 Architecture specific
|
||||||
|
|
||||||
|
ifeq ($(findstring x86,$(ARCH)),x86)
|
||||||
|
|
||||||
|
# x86-32/64
|
||||||
|
|
||||||
|
ifeq ($(findstring x86-32,$(ARCH)),x86-32)
|
||||||
|
arch = i386
|
||||||
|
bits = 32
|
||||||
|
sse = yes
|
||||||
|
mmx = yes
|
||||||
|
else
|
||||||
|
arch = x86_64
|
||||||
|
sse = yes
|
||||||
|
sse2 = yes
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(findstring -sse,$(ARCH)),-sse)
|
||||||
|
sse = yes
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(findstring -popcnt,$(ARCH)),-popcnt)
|
||||||
|
popcnt = yes
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(findstring -mmx,$(ARCH)),-mmx)
|
||||||
|
mmx = yes
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(findstring -sse2,$(ARCH)),-sse2)
|
||||||
|
sse = yes
|
||||||
|
sse2 = yes
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(findstring -ssse3,$(ARCH)),-ssse3)
|
||||||
|
sse = yes
|
||||||
|
sse2 = yes
|
||||||
|
ssse3 = yes
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(findstring -sse41,$(ARCH)),-sse41)
|
||||||
|
sse = yes
|
||||||
|
sse2 = yes
|
||||||
|
ssse3 = yes
|
||||||
|
sse41 = yes
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(findstring -modern,$(ARCH)),-modern)
|
||||||
|
popcnt = yes
|
||||||
|
sse = yes
|
||||||
|
sse2 = yes
|
||||||
|
ssse3 = yes
|
||||||
|
sse41 = yes
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(findstring -avx2,$(ARCH)),-avx2)
|
||||||
|
popcnt = yes
|
||||||
|
sse = yes
|
||||||
|
sse2 = yes
|
||||||
|
ssse3 = yes
|
||||||
|
sse41 = yes
|
||||||
|
avx2 = yes
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(findstring -bmi2,$(ARCH)),-bmi2)
|
||||||
|
popcnt = yes
|
||||||
|
sse = yes
|
||||||
|
sse2 = yes
|
||||||
|
ssse3 = yes
|
||||||
|
sse41 = yes
|
||||||
|
avx2 = yes
|
||||||
|
pext = yes
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(findstring -avx512,$(ARCH)),-avx512)
|
||||||
|
popcnt = yes
|
||||||
|
sse = yes
|
||||||
|
sse2 = yes
|
||||||
|
ssse3 = yes
|
||||||
|
sse41 = yes
|
||||||
|
avx2 = yes
|
||||||
|
pext = yes
|
||||||
|
avx512 = yes
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(findstring -vnni,$(ARCH)),-vnni)
|
||||||
|
popcnt = yes
|
||||||
|
sse = yes
|
||||||
|
sse2 = yes
|
||||||
|
ssse3 = yes
|
||||||
|
sse41 = yes
|
||||||
|
avx2 = yes
|
||||||
|
pext = yes
|
||||||
|
avx512 = yes
|
||||||
|
vnni = yes
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(sse),yes)
|
||||||
|
prefetch = yes
|
||||||
|
endif
|
||||||
|
|
||||||
|
# 64-bit pext is not available on x86-32
|
||||||
|
ifeq ($(bits),32)
|
||||||
|
pext = no
|
||||||
|
endif
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
# all other architectures
|
||||||
|
|
||||||
ifeq ($(ARCH),general-32)
|
ifeq ($(ARCH),general-32)
|
||||||
arch = any
|
arch = any
|
||||||
bits = 32
|
bits = 32
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ARCH),x86-32-old)
|
|
||||||
arch = i386
|
|
||||||
bits = 32
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(ARCH),x86-32)
|
|
||||||
arch = i386
|
|
||||||
bits = 32
|
|
||||||
prefetch = yes
|
|
||||||
sse = yes
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(ARCH),general-64)
|
ifeq ($(ARCH),general-64)
|
||||||
arch = any
|
arch = any
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ARCH),x86-64)
|
|
||||||
arch = x86_64
|
|
||||||
prefetch = yes
|
|
||||||
sse = yes
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(ARCH),x86-64-modern)
|
|
||||||
arch = x86_64
|
|
||||||
prefetch = yes
|
|
||||||
popcnt = yes
|
|
||||||
sse = yes
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(ARCH),x86-64-bmi2)
|
|
||||||
arch = x86_64
|
|
||||||
prefetch = yes
|
|
||||||
popcnt = yes
|
|
||||||
sse = yes
|
|
||||||
pext = yes
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(ARCH),armv7)
|
ifeq ($(ARCH),armv7)
|
||||||
arch = armv7
|
arch = armv7
|
||||||
prefetch = yes
|
prefetch = yes
|
||||||
bits = 32
|
bits = 32
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(ARCH),armv7-neon)
|
||||||
|
arch = armv7
|
||||||
|
prefetch = yes
|
||||||
|
popcnt = yes
|
||||||
|
neon = yes
|
||||||
|
bits = 32
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(ARCH),armv8)
|
ifeq ($(ARCH),armv8)
|
||||||
arch = armv8-a
|
arch = armv8-a
|
||||||
prefetch = yes
|
prefetch = yes
|
||||||
popcnt = yes
|
popcnt = yes
|
||||||
|
neon = yes
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(ARCH),apple-silicon)
|
||||||
|
arch = arm64
|
||||||
|
prefetch = yes
|
||||||
|
popcnt = yes
|
||||||
|
neon = yes
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ARCH),ppc-32)
|
ifeq ($(ARCH),ppc-32)
|
||||||
|
@ -151,13 +267,15 @@ ifeq ($(ARCH),ppc-64)
|
||||||
prefetch = yes
|
prefetch = yes
|
||||||
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++11 $(EXTRACXXFLAGS)
|
CXXFLAGS += -Wall -Wcast-qual -fno-exceptions -std=c++17 $(EXTRACXXFLAGS)
|
||||||
DEPENDFLAGS += -std=c++11
|
DEPENDFLAGS += -std=c++17
|
||||||
LDFLAGS += $(EXTRALDFLAGS)
|
LDFLAGS += $(EXTRALDFLAGS)
|
||||||
|
|
||||||
ifeq ($(COMP),)
|
ifeq ($(COMP),)
|
||||||
|
@ -169,7 +287,7 @@ 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-a))
|
||||||
ifeq ($(OS),Android)
|
ifeq ($(OS),Android)
|
||||||
CXXFLAGS += -m$(bits)
|
CXXFLAGS += -m$(bits)
|
||||||
LDFLAGS += -m$(bits)
|
LDFLAGS += -m$(bits)
|
||||||
|
@ -179,9 +297,16 @@ ifeq ($(COMP),gcc)
|
||||||
LDFLAGS += -m$(bits)
|
LDFLAGS += -m$(bits)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(arch),$(filter $(arch),armv7))
|
||||||
|
LDFLAGS += -latomic
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq ($(KERNEL),Darwin)
|
ifneq ($(KERNEL),Darwin)
|
||||||
LDFLAGS += -Wl,--no-as-needed
|
LDFLAGS += -Wl,--no-as-needed
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
gccversion = $(shell $(CXX) --version)
|
||||||
|
gccisclang = $(findstring clang,$(gccversion))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(COMP),mingw)
|
ifeq ($(COMP),mingw)
|
||||||
|
@ -226,7 +351,7 @@ ifeq ($(COMP),clang)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ARCH),$(filter $(ARCH),armv7 armv8))
|
ifeq ($(arch),$(filter $(arch),armv7 armv8))
|
||||||
ifeq ($(OS),Android)
|
ifeq ($(OS),Android)
|
||||||
CXXFLAGS += -m$(bits)
|
CXXFLAGS += -m$(bits)
|
||||||
LDFLAGS += -m$(bits)
|
LDFLAGS += -m$(bits)
|
||||||
|
@ -251,8 +376,27 @@ endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(KERNEL),Darwin)
|
ifeq ($(KERNEL),Darwin)
|
||||||
CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.9
|
CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.14
|
||||||
LDFLAGS += -arch $(arch) -mmacosx-version-min=10.9
|
LDFLAGS += -arch $(arch) -mmacosx-version-min=10.14
|
||||||
|
endif
|
||||||
|
|
||||||
|
# To cross-compile for Android, NDK version r21 or later is recommended.
|
||||||
|
# In earlier NDK versions, you'll need to pass -fno-addrsig if using GNU binutils.
|
||||||
|
# Currently we don't know how to make PGO builds with the NDK yet.
|
||||||
|
ifeq ($(COMP),ndk)
|
||||||
|
CXXFLAGS += -stdlib=libc++ -fPIE
|
||||||
|
ifeq ($(arch),armv7)
|
||||||
|
comp=armv7a-linux-androideabi16-clang
|
||||||
|
CXX=armv7a-linux-androideabi16-clang++
|
||||||
|
CXXFLAGS += -mthumb -march=armv7-a -mfloat-abi=softfp -mfpu=neon
|
||||||
|
STRIP=arm-linux-androideabi-strip
|
||||||
|
endif
|
||||||
|
ifeq ($(arch),armv8-a)
|
||||||
|
comp=aarch64-linux-android21-clang
|
||||||
|
CXX=aarch64-linux-android21-clang++
|
||||||
|
STRIP=aarch64-linux-android-strip
|
||||||
|
endif
|
||||||
|
LDFLAGS += -static-libstdc++ -pie -lm -latomic
|
||||||
endif
|
endif
|
||||||
|
|
||||||
### Travis CI script uses COMPILER to overwrite CXX
|
### Travis CI script uses COMPILER to overwrite CXX
|
||||||
|
@ -271,7 +415,9 @@ ifneq ($(comp),mingw)
|
||||||
ifneq ($(OS),Android)
|
ifneq ($(OS),Android)
|
||||||
# Haiku has pthreads in its libroot, so only link it in on other platforms
|
# Haiku has pthreads in its libroot, so only link it in on other platforms
|
||||||
ifneq ($(KERNEL),Haiku)
|
ifneq ($(KERNEL),Haiku)
|
||||||
LDFLAGS += -lpthread
|
ifneq ($(COMP),ndk)
|
||||||
|
LDFLAGS += -lpthread
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
@ -285,8 +431,8 @@ endif
|
||||||
|
|
||||||
### 3.2.2 Debugging with undefined behavior sanitizers
|
### 3.2.2 Debugging with undefined behavior sanitizers
|
||||||
ifneq ($(sanitize),no)
|
ifneq ($(sanitize),no)
|
||||||
CXXFLAGS += -g3 -fsanitize=$(sanitize) -fuse-ld=gold
|
CXXFLAGS += -g3 -fsanitize=$(sanitize)
|
||||||
LDFLAGS += -fsanitize=$(sanitize) -fuse-ld=gold
|
LDFLAGS += -fsanitize=$(sanitize)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
### 3.3 Optimization
|
### 3.3 Optimization
|
||||||
|
@ -316,7 +462,6 @@ endif
|
||||||
ifeq ($(prefetch),yes)
|
ifeq ($(prefetch),yes)
|
||||||
ifeq ($(sse),yes)
|
ifeq ($(sse),yes)
|
||||||
CXXFLAGS += -msse
|
CXXFLAGS += -msse
|
||||||
DEPENDFLAGS += -msse
|
|
||||||
endif
|
endif
|
||||||
else
|
else
|
||||||
CXXFLAGS += -DNO_PREFETCH
|
CXXFLAGS += -DNO_PREFETCH
|
||||||
|
@ -324,7 +469,7 @@ endif
|
||||||
|
|
||||||
### 3.6 popcnt
|
### 3.6 popcnt
|
||||||
ifeq ($(popcnt),yes)
|
ifeq ($(popcnt),yes)
|
||||||
ifeq ($(arch),$(filter $(arch),ppc64 armv8-a))
|
ifeq ($(arch),$(filter $(arch),ppc64 armv7 armv8-a arm64))
|
||||||
CXXFLAGS += -DUSE_POPCNT
|
CXXFLAGS += -DUSE_POPCNT
|
||||||
else ifeq ($(comp),icc)
|
else ifeq ($(comp),icc)
|
||||||
CXXFLAGS += -msse3 -DUSE_POPCNT
|
CXXFLAGS += -msse3 -DUSE_POPCNT
|
||||||
|
@ -333,11 +478,70 @@ ifeq ($(popcnt),yes)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
ifeq ($(avx2),yes)
|
||||||
|
CXXFLAGS += -DUSE_AVX2
|
||||||
|
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
|
||||||
|
CXXFLAGS += -mavx2
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(avx512),yes)
|
||||||
|
CXXFLAGS += -DUSE_AVX512
|
||||||
|
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
|
||||||
|
CXXFLAGS += -mavx512f -mavx512bw
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(vnni),yes)
|
||||||
|
CXXFLAGS += -DUSE_VNNI
|
||||||
|
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
|
||||||
|
CXXFLAGS += -mavx512vnni -mavx512dq -mavx512vl
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(sse41),yes)
|
||||||
|
CXXFLAGS += -DUSE_SSE41
|
||||||
|
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
|
||||||
|
CXXFLAGS += -msse4.1
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(ssse3),yes)
|
||||||
|
CXXFLAGS += -DUSE_SSSE3
|
||||||
|
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
|
||||||
|
CXXFLAGS += -mssse3
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(sse2),yes)
|
||||||
|
CXXFLAGS += -DUSE_SSE2
|
||||||
|
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
|
||||||
|
CXXFLAGS += -msse2
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(mmx),yes)
|
||||||
|
CXXFLAGS += -DUSE_MMX
|
||||||
|
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
|
||||||
|
CXXFLAGS += -mmmx
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(neon),yes)
|
||||||
|
CXXFLAGS += -DUSE_NEON
|
||||||
|
ifeq ($(KERNEL),Linux)
|
||||||
|
ifneq ($(COMP),ndk)
|
||||||
|
CXXFLAGS += -mfpu=neon
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
### 3.7 pext
|
### 3.7 pext
|
||||||
ifeq ($(pext),yes)
|
ifeq ($(pext),yes)
|
||||||
CXXFLAGS += -DUSE_PEXT
|
CXXFLAGS += -DUSE_PEXT
|
||||||
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
|
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
|
||||||
CXXFLAGS += -msse4 -mbmi2
|
CXXFLAGS += -mbmi2
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -346,18 +550,36 @@ endif
|
||||||
### needs access to the optimization flags.
|
### needs access to the optimization flags.
|
||||||
ifeq ($(optimize),yes)
|
ifeq ($(optimize),yes)
|
||||||
ifeq ($(debug), no)
|
ifeq ($(debug), no)
|
||||||
ifeq ($(comp),$(filter $(comp),gcc clang))
|
ifeq ($(COMP),ndk)
|
||||||
|
CXXFLAGS += -flto=thin
|
||||||
|
LDFLAGS += $(CXXFLAGS)
|
||||||
|
else ifeq ($(comp),clang)
|
||||||
|
CXXFLAGS += -flto=thin
|
||||||
|
LDFLAGS += $(CXXFLAGS)
|
||||||
|
|
||||||
|
# GCC and CLANG use different methods for parallelizing LTO and CLANG pretends to be
|
||||||
|
# GCC on some systems.
|
||||||
|
else ifeq ($(comp),gcc)
|
||||||
|
ifeq ($(gccisclang),)
|
||||||
CXXFLAGS += -flto
|
CXXFLAGS += -flto
|
||||||
|
LDFLAGS += $(CXXFLAGS) -flto=jobserver
|
||||||
|
ifneq ($(findstring MINGW,$(KERNEL)),)
|
||||||
|
LDFLAGS += -save-temps
|
||||||
|
else ifneq ($(findstring MSYS,$(KERNEL)),)
|
||||||
|
LDFLAGS += -save-temps
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
CXXFLAGS += -flto=thin
|
||||||
LDFLAGS += $(CXXFLAGS)
|
LDFLAGS += $(CXXFLAGS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# To use LTO and static linking on windows, the tool chain requires a recent gcc:
|
# To use LTO and static linking on windows, the tool chain requires a recent gcc:
|
||||||
# gcc version 10.1 in msys2 or TDM-GCC version 9.2 are know to work, older might not.
|
# gcc version 10.1 in msys2 or TDM-GCC version 9.2 are known to work, older might not.
|
||||||
# So, only enable it for a cross from Linux by default.
|
# So, only enable it for a cross from Linux by default.
|
||||||
ifeq ($(comp),mingw)
|
else ifeq ($(comp),mingw)
|
||||||
ifeq ($(KERNEL),Linux)
|
ifeq ($(KERNEL),Linux)
|
||||||
CXXFLAGS += -flto
|
CXXFLAGS += -flto
|
||||||
LDFLAGS += $(CXXFLAGS)
|
LDFLAGS += $(CXXFLAGS) -flto=jobserver
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
@ -389,23 +611,34 @@ help:
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Supported targets:"
|
@echo "Supported targets:"
|
||||||
@echo ""
|
@echo ""
|
||||||
|
@echo "help > Display architecture details"
|
||||||
@echo "build > Standard build"
|
@echo "build > Standard build"
|
||||||
@echo "profile-build > PGO build"
|
@echo "net > Download the default nnue net"
|
||||||
|
@echo "profile-build > Faster build (with profile-guided optimization)"
|
||||||
@echo "strip > Strip executable"
|
@echo "strip > Strip executable"
|
||||||
@echo "install > Install executable"
|
@echo "install > Install executable"
|
||||||
@echo "clean > Clean up"
|
@echo "clean > Clean up"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Supported archs:"
|
@echo "Supported archs:"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "x86-64-bmi2 > x86 64-bit with pext support (also enables SSE4)"
|
@echo "x86-64-vnni > x86 64-bit with vnni support"
|
||||||
@echo "x86-64-modern > x86 64-bit with popcnt support (also enables SSE3)"
|
@echo "x86-64-avx512 > x86 64-bit with avx512 support"
|
||||||
@echo "x86-64 > x86 64-bit generic"
|
@echo "x86-64-bmi2 > x86 64-bit with bmi2 support"
|
||||||
@echo "x86-32 > x86 32-bit (also enables SSE)"
|
@echo "x86-64-avx2 > x86 64-bit with avx2 support"
|
||||||
@echo "x86-32-old > x86 32-bit fall back for old hardware"
|
@echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support"
|
||||||
|
@echo "x86-64-modern > common modern CPU, currently x86-64-sse41-popcnt"
|
||||||
|
@echo "x86-64-ssse3 > x86 64-bit with ssse3 support"
|
||||||
|
@echo "x86-64-sse3-popcnt > x86 64-bit with sse3 and popcnt support"
|
||||||
|
@echo "x86-64 > x86 64-bit generic (with sse2 support)"
|
||||||
|
@echo "x86-32-sse41-popcnt > x86 32-bit with sse41 and popcnt support"
|
||||||
|
@echo "x86-32-sse2 > x86 32-bit with sse2 support"
|
||||||
|
@echo "x86-32 > x86 32-bit generic (with mmx and sse support)"
|
||||||
@echo "ppc-64 > PPC 64-bit"
|
@echo "ppc-64 > PPC 64-bit"
|
||||||
@echo "ppc-32 > PPC 32-bit"
|
@echo "ppc-32 > PPC 32-bit"
|
||||||
@echo "armv7 > ARMv7 32-bit"
|
@echo "armv7 > ARMv7 32-bit"
|
||||||
@echo "armv8 > ARMv8 64-bit"
|
@echo "armv7-neon > ARMv7 32-bit with popcnt and neon"
|
||||||
|
@echo "armv8 > ARMv8 64-bit with popcnt and neon"
|
||||||
|
@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 ""
|
@echo ""
|
||||||
|
@ -415,27 +648,34 @@ help:
|
||||||
@echo "mingw > Gnu compiler with MinGW under Windows"
|
@echo "mingw > Gnu compiler with MinGW under Windows"
|
||||||
@echo "clang > LLVM Clang compiler"
|
@echo "clang > LLVM Clang compiler"
|
||||||
@echo "icc > Intel compiler"
|
@echo "icc > Intel compiler"
|
||||||
|
@echo "ndk > Google NDK to cross-compile for Android"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Simple examples. If you don't know what to do, you likely want to run: "
|
@echo "Simple examples. If you don't know what to do, you likely want to run: "
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "make build ARCH=x86-64 (This is for 64-bit systems)"
|
@echo "make -j build ARCH=x86-64 (A portable, slow compile for 64-bit systems)"
|
||||||
@echo "make build ARCH=x86-32 (This is for 32-bit systems)"
|
@echo "make -j build ARCH=x86-32 (A portable, slow compile for 32-bit systems)"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Advanced examples, for experienced users: "
|
@echo "Advanced examples, for experienced users looking for performance: "
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "make build ARCH=x86-64 COMP=clang"
|
@echo "make help ARCH=x86-64-bmi2"
|
||||||
@echo "make profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-4.8"
|
@echo "make -j profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-9.0"
|
||||||
|
@echo "make -j build ARCH=x86-64-ssse3 COMP=clang"
|
||||||
@echo ""
|
@echo ""
|
||||||
|
ifneq ($(empty_arch), yes)
|
||||||
|
@echo "-------------------------------"
|
||||||
|
@echo "The selected architecture $(ARCH) will enable the following configuration: "
|
||||||
|
@$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
.PHONY: help build profile-build strip install clean objclean profileclean \
|
.PHONY: help build profile-build strip install clean net objclean profileclean \
|
||||||
config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \
|
config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \
|
||||||
clang-profile-use clang-profile-make
|
clang-profile-use clang-profile-make
|
||||||
|
|
||||||
build: config-sanity
|
build: config-sanity
|
||||||
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) all
|
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) all
|
||||||
|
|
||||||
profile-build: config-sanity objclean profileclean
|
profile-build: net config-sanity objclean profileclean
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Step 1/4. Building instrumented executable ..."
|
@echo "Step 1/4. Building instrumented executable ..."
|
||||||
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make)
|
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make)
|
||||||
|
@ -451,7 +691,7 @@ profile-build: config-sanity objclean profileclean
|
||||||
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) profileclean
|
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) profileclean
|
||||||
|
|
||||||
strip:
|
strip:
|
||||||
strip $(EXE)
|
$(STRIP) $(EXE)
|
||||||
|
|
||||||
install:
|
install:
|
||||||
-mkdir -p -m 755 $(BINDIR)
|
-mkdir -p -m 755 $(BINDIR)
|
||||||
|
@ -462,14 +702,38 @@ install:
|
||||||
clean: objclean profileclean
|
clean: objclean profileclean
|
||||||
@rm -f .depend *~ core
|
@rm -f .depend *~ core
|
||||||
|
|
||||||
|
net:
|
||||||
|
$(eval nnuenet := $(shell grep EvalFile ucioption.cpp | grep Option | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/'))
|
||||||
|
@echo "Default net: $(nnuenet)"
|
||||||
|
$(eval nnuedownloadurl := https://tests.stockfishchess.org/api/nn/$(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))
|
||||||
|
@if test -f "$(nnuenet)"; 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; \
|
||||||
|
else \
|
||||||
|
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))
|
||||||
|
@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"; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
# clean binaries and objects
|
# clean binaries and objects
|
||||||
objclean:
|
objclean:
|
||||||
@rm -f $(EXE) *.o ./syzygy/*.o
|
@rm -f $(EXE) *.o ./syzygy/*.o ./nnue/*.o ./nnue/features/*.o
|
||||||
|
|
||||||
# clean auxiliary profiling files
|
# clean auxiliary profiling files
|
||||||
profileclean:
|
profileclean:
|
||||||
@rm -rf profdir
|
@rm -rf profdir
|
||||||
@rm -f bench.txt *.gcda *.gcno
|
@rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda *.s
|
||||||
@rm -f stockfish.profdata *.profraw
|
@rm -f stockfish.profdata *.profraw
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -493,8 +757,16 @@ config-sanity:
|
||||||
@echo "os: '$(OS)'"
|
@echo "os: '$(OS)'"
|
||||||
@echo "prefetch: '$(prefetch)'"
|
@echo "prefetch: '$(prefetch)'"
|
||||||
@echo "popcnt: '$(popcnt)'"
|
@echo "popcnt: '$(popcnt)'"
|
||||||
@echo "sse: '$(sse)'"
|
|
||||||
@echo "pext: '$(pext)'"
|
@echo "pext: '$(pext)'"
|
||||||
|
@echo "sse: '$(sse)'"
|
||||||
|
@echo "mmx: '$(mmx)'"
|
||||||
|
@echo "sse2: '$(sse2)'"
|
||||||
|
@echo "ssse3: '$(ssse3)'"
|
||||||
|
@echo "sse41: '$(sse41)'"
|
||||||
|
@echo "avx2: '$(avx2)'"
|
||||||
|
@echo "avx512: '$(avx512)'"
|
||||||
|
@echo "vnni: '$(vnni)'"
|
||||||
|
@echo "neon: '$(neon)'"
|
||||||
@echo "mpi: '$(mpi)'"
|
@echo "mpi: '$(mpi)'"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Flags:"
|
@echo "Flags:"
|
||||||
|
@ -509,16 +781,25 @@ config-sanity:
|
||||||
@test "$(optimize)" = "yes" || test "$(optimize)" = "no"
|
@test "$(optimize)" = "yes" || test "$(optimize)" = "no"
|
||||||
@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)" = "ppc64" || test "$(arch)" = "ppc" || \
|
||||||
test "$(arch)" = "armv7" || test "$(arch)" = "armv8-a"
|
test "$(arch)" = "armv7" || test "$(arch)" = "armv8-a" || test "$(arch)" = "arm64"
|
||||||
@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"
|
||||||
@test "$(sse)" = "yes" || test "$(sse)" = "no"
|
|
||||||
@test "$(pext)" = "yes" || test "$(pext)" = "no"
|
@test "$(pext)" = "yes" || test "$(pext)" = "no"
|
||||||
@test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang"
|
@test "$(sse)" = "yes" || test "$(sse)" = "no"
|
||||||
|
@test "$(mmx)" = "yes" || test "$(mmx)" = "no"
|
||||||
|
@test "$(sse2)" = "yes" || test "$(sse2)" = "no"
|
||||||
|
@test "$(ssse3)" = "yes" || test "$(ssse3)" = "no"
|
||||||
|
@test "$(sse41)" = "yes" || test "$(sse41)" = "no"
|
||||||
|
@test "$(avx2)" = "yes" || test "$(avx2)" = "no"
|
||||||
|
@test "$(avx512)" = "yes" || test "$(avx512)" = "no"
|
||||||
|
@test "$(vnni)" = "yes" || test "$(vnni)" = "no"
|
||||||
|
@test "$(neon)" = "yes" || test "$(neon)" = "no"
|
||||||
|
@test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" \
|
||||||
|
|| test "$(comp)" = "armv7a-linux-androideabi16-clang" || test "$(comp)" = "aarch64-linux-android21-clang"
|
||||||
|
|
||||||
$(EXE): $(OBJS)
|
$(EXE): $(OBJS)
|
||||||
$(CXX) -o $@ $(OBJS) $(LDFLAGS)
|
+$(CXX) -o $@ $(OBJS) $(LDFLAGS)
|
||||||
|
|
||||||
clang-profile-make:
|
clang-profile-make:
|
||||||
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
|
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -88,7 +86,7 @@ const vector<string> Defaults = {
|
||||||
|
|
||||||
// Chess 960
|
// Chess 960
|
||||||
"setoption name UCI_Chess960 value true",
|
"setoption name UCI_Chess960 value true",
|
||||||
"bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w KQkq - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6",
|
"bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w HFhf - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6",
|
||||||
"setoption name UCI_Chess960 value false"
|
"setoption name UCI_Chess960 value false"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -97,8 +95,9 @@ const vector<string> Defaults = {
|
||||||
/// setup_bench() builds a list of UCI commands to be run by bench. There
|
/// setup_bench() builds a list of UCI commands to be run by bench. There
|
||||||
/// are five parameters: TT size in MB, number of search threads that
|
/// are five parameters: TT size in MB, number of search threads that
|
||||||
/// should be used, the limit value spent for each position, a file name
|
/// should be used, the limit value spent for each position, a file name
|
||||||
/// where to look for positions in FEN format and the type of the limit:
|
/// where to look for positions in FEN format, the type of the limit:
|
||||||
/// depth, perft, nodes and movetime (in millisecs).
|
/// depth, perft, nodes and movetime (in millisecs), and evaluation type
|
||||||
|
/// mixed (default), classical, NNUE.
|
||||||
///
|
///
|
||||||
/// bench -> search default positions up to depth 13
|
/// bench -> search default positions up to depth 13
|
||||||
/// bench 64 1 15 -> search default positions up to depth 15 (TT = 64MB)
|
/// bench 64 1 15 -> search default positions up to depth 15 (TT = 64MB)
|
||||||
|
@ -117,6 +116,7 @@ vector<string> setup_bench(const Position& current, istream& is) {
|
||||||
string limit = (is >> token) ? token : "13";
|
string limit = (is >> token) ? token : "13";
|
||||||
string fenFile = (is >> token) ? token : "default";
|
string fenFile = (is >> token) ? token : "default";
|
||||||
string limitType = (is >> token) ? token : "depth";
|
string limitType = (is >> token) ? token : "depth";
|
||||||
|
string evalType = (is >> token) ? token : "mixed";
|
||||||
|
|
||||||
go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit;
|
go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit;
|
||||||
|
|
||||||
|
@ -148,13 +148,20 @@ vector<string> setup_bench(const Position& current, istream& is) {
|
||||||
list.emplace_back("setoption name Hash value " + ttSize);
|
list.emplace_back("setoption name Hash value " + ttSize);
|
||||||
list.emplace_back("ucinewgame");
|
list.emplace_back("ucinewgame");
|
||||||
|
|
||||||
|
size_t posCounter = 0;
|
||||||
|
|
||||||
for (const string& fen : fens)
|
for (const string& fen : fens)
|
||||||
if (fen.find("setoption") != string::npos)
|
if (fen.find("setoption") != string::npos)
|
||||||
list.emplace_back(fen);
|
list.emplace_back(fen);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (evalType == "classical" || (evalType == "mixed" && posCounter % 2 == 0))
|
||||||
|
list.emplace_back("setoption name Use NNUE value false");
|
||||||
|
else if (evalType == "NNUE" || (evalType == "mixed" && posCounter % 2 != 0))
|
||||||
|
list.emplace_back("setoption name Use NNUE value true");
|
||||||
list.emplace_back("position fen " + fen);
|
list.emplace_back("position fen " + fen);
|
||||||
list.emplace_back(go);
|
list.emplace_back(go);
|
||||||
|
++posCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -124,12 +122,13 @@ inline Bitboard operator&(Square s, Bitboard b) { return b & s; }
|
||||||
inline Bitboard operator|(Square s, Bitboard b) { return b | s; }
|
inline Bitboard operator|(Square s, Bitboard b) { return b | s; }
|
||||||
inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; }
|
inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; }
|
||||||
|
|
||||||
inline Bitboard operator|(Square s, Square s2) { return square_bb(s) | s2; }
|
inline Bitboard operator|(Square s1, Square s2) { return square_bb(s1) | s2; }
|
||||||
|
|
||||||
constexpr bool more_than_one(Bitboard b) {
|
constexpr bool more_than_one(Bitboard b) {
|
||||||
return b & (b - 1);
|
return b & (b - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
constexpr bool opposite_colors(Square s1, Square s2) {
|
constexpr bool opposite_colors(Square s1, Square s2) {
|
||||||
return (s1 + rank_of(s1) + s2 + rank_of(s2)) & 1;
|
return (s1 + rank_of(s1) + s2 + rank_of(s2)) & 1;
|
||||||
}
|
}
|
||||||
|
@ -138,19 +137,19 @@ constexpr bool opposite_colors(Square s1, Square s2) {
|
||||||
/// rank_bb() and file_bb() return a bitboard representing all the squares on
|
/// rank_bb() and file_bb() return a bitboard representing all the squares on
|
||||||
/// the given file or rank.
|
/// the given file or rank.
|
||||||
|
|
||||||
inline Bitboard rank_bb(Rank r) {
|
constexpr Bitboard rank_bb(Rank r) {
|
||||||
return Rank1BB << (8 * r);
|
return Rank1BB << (8 * r);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Bitboard rank_bb(Square s) {
|
constexpr Bitboard rank_bb(Square s) {
|
||||||
return rank_bb(rank_of(s));
|
return rank_bb(rank_of(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Bitboard file_bb(File f) {
|
constexpr Bitboard file_bb(File f) {
|
||||||
return FileABB << f;
|
return FileABB << f;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Bitboard file_bb(Square s) {
|
constexpr Bitboard file_bb(Square s) {
|
||||||
return file_bb(file_of(s));
|
return file_bb(file_of(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,16 +194,16 @@ constexpr Bitboard pawn_double_attacks_bb(Bitboard b) {
|
||||||
|
|
||||||
|
|
||||||
/// adjacent_files_bb() returns a bitboard representing all the squares on the
|
/// adjacent_files_bb() returns a bitboard representing all the squares on the
|
||||||
/// adjacent files of the given one.
|
/// adjacent files of a given square.
|
||||||
|
|
||||||
inline Bitboard adjacent_files_bb(Square s) {
|
constexpr Bitboard adjacent_files_bb(Square s) {
|
||||||
return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s));
|
return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// line_bb(Square, Square) returns a bitboard representing an entire line,
|
/// line_bb() returns a bitboard representing an entire line (from board edge
|
||||||
/// from board edge to board edge, that intersects the given squares. If the
|
/// to board edge) that intersects the two given squares. If the given squares
|
||||||
/// given squares are not on a same file/rank/diagonal, returns 0. For instance,
|
/// are not on a same file/rank/diagonal, the function returns 0. For instance,
|
||||||
/// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal.
|
/// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal.
|
||||||
|
|
||||||
inline Bitboard line_bb(Square s1, Square s2) {
|
inline Bitboard line_bb(Square s1, Square s2) {
|
||||||
|
@ -215,8 +214,8 @@ inline Bitboard line_bb(Square s1, Square s2) {
|
||||||
|
|
||||||
|
|
||||||
/// between_bb() returns a bitboard representing squares that are linearly
|
/// between_bb() returns a bitboard representing squares that are linearly
|
||||||
/// between the given squares (excluding the given squares). If the given
|
/// between the two given squares (excluding the given squares). If the given
|
||||||
/// squares are not on a same file/rank/diagonal, return 0. For instance,
|
/// squares are not on a same file/rank/diagonal, we return 0. For instance,
|
||||||
/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5 and E6.
|
/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5 and E6.
|
||||||
|
|
||||||
inline Bitboard between_bb(Square s1, Square s2) {
|
inline Bitboard between_bb(Square s1, Square s2) {
|
||||||
|
@ -229,7 +228,7 @@ inline Bitboard between_bb(Square s1, Square s2) {
|
||||||
/// in front of the given one, from the point of view of the given color. For instance,
|
/// in front of the given one, from the point of view of the given color. For instance,
|
||||||
/// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2.
|
/// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2.
|
||||||
|
|
||||||
inline Bitboard forward_ranks_bb(Color c, Square s) {
|
constexpr Bitboard forward_ranks_bb(Color c, Square s) {
|
||||||
return c == WHITE ? ~Rank1BB << 8 * relative_rank(WHITE, s)
|
return c == WHITE ? ~Rank1BB << 8 * relative_rank(WHITE, s)
|
||||||
: ~Rank8BB >> 8 * relative_rank(BLACK, s);
|
: ~Rank8BB >> 8 * relative_rank(BLACK, s);
|
||||||
}
|
}
|
||||||
|
@ -238,7 +237,7 @@ inline Bitboard forward_ranks_bb(Color c, Square s) {
|
||||||
/// forward_file_bb() returns a bitboard representing all the squares along the
|
/// forward_file_bb() returns a bitboard representing all the squares along the
|
||||||
/// line in front of the given one, from the point of view of the given color.
|
/// line in front of the given one, from the point of view of the given color.
|
||||||
|
|
||||||
inline Bitboard forward_file_bb(Color c, Square s) {
|
constexpr Bitboard forward_file_bb(Color c, Square s) {
|
||||||
return forward_ranks_bb(c, s) & file_bb(s);
|
return forward_ranks_bb(c, s) & file_bb(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +246,7 @@ inline Bitboard forward_file_bb(Color c, Square s) {
|
||||||
/// be attacked by a pawn of the given color when it moves along its file, starting
|
/// be attacked by a pawn of the given color when it moves along its file, starting
|
||||||
/// from the given square.
|
/// from the given square.
|
||||||
|
|
||||||
inline Bitboard pawn_attack_span(Color c, Square s) {
|
constexpr Bitboard pawn_attack_span(Color c, Square s) {
|
||||||
return forward_ranks_bb(c, s) & adjacent_files_bb(s);
|
return forward_ranks_bb(c, s) & adjacent_files_bb(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,7 +254,7 @@ inline Bitboard pawn_attack_span(Color c, Square s) {
|
||||||
/// passed_pawn_span() returns a bitboard which can be used to test if a pawn of
|
/// passed_pawn_span() returns a bitboard which can be used to test if a pawn of
|
||||||
/// the given color and on the given square is a passed pawn.
|
/// the given color and on the given square is a passed pawn.
|
||||||
|
|
||||||
inline Bitboard passed_pawn_span(Color c, Square s) {
|
constexpr Bitboard passed_pawn_span(Color c, Square s) {
|
||||||
return pawn_attack_span(c, s) | forward_file_bb(c, s);
|
return pawn_attack_span(c, s) | forward_file_bb(c, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -181,15 +179,15 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
|
||||||
assert(verify_material(pos, strongSide, RookValueMg, 0));
|
assert(verify_material(pos, strongSide, RookValueMg, 0));
|
||||||
assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
|
assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
|
||||||
|
|
||||||
Square strongKing = relative_square(strongSide, pos.square<KING>(strongSide));
|
Square strongKing = pos.square<KING>(strongSide);
|
||||||
Square weakKing = relative_square(strongSide, pos.square<KING>(weakSide));
|
Square weakKing = pos.square<KING>(weakSide);
|
||||||
Square strongRook = relative_square(strongSide, pos.square<ROOK>(strongSide));
|
Square strongRook = pos.square<ROOK>(strongSide);
|
||||||
Square weakPawn = relative_square(strongSide, pos.square<PAWN>(weakSide));
|
Square weakPawn = pos.square<PAWN>(weakSide);
|
||||||
Square queeningSquare = make_square(file_of(weakPawn), RANK_1);
|
Square queeningSquare = make_square(file_of(weakPawn), relative_rank(weakSide, RANK_8));
|
||||||
Value result;
|
Value result;
|
||||||
|
|
||||||
// If the stronger side's king is in front of the pawn, it's a win
|
// If the stronger side's king is in front of the pawn, it's a win
|
||||||
if (forward_file_bb(WHITE, strongKing) & weakPawn)
|
if (forward_file_bb(strongSide, strongKing) & weakPawn)
|
||||||
result = RookValueEg - distance(strongKing, weakPawn);
|
result = RookValueEg - distance(strongKing, weakPawn);
|
||||||
|
|
||||||
// If the weaker side's king is too far from the pawn and the rook,
|
// If the weaker side's king is too far from the pawn and the rook,
|
||||||
|
@ -200,15 +198,15 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
|
||||||
|
|
||||||
// If the pawn is far advanced and supported by the defending king,
|
// If the pawn is far advanced and supported by the defending king,
|
||||||
// the position is drawish
|
// the position is drawish
|
||||||
else if ( rank_of(weakKing) <= RANK_3
|
else if ( relative_rank(strongSide, weakKing) <= RANK_3
|
||||||
&& distance(weakKing, weakPawn) == 1
|
&& distance(weakKing, weakPawn) == 1
|
||||||
&& rank_of(strongKing) >= RANK_4
|
&& relative_rank(strongSide, strongKing) >= RANK_4
|
||||||
&& distance(strongKing, weakPawn) > 2 + (pos.side_to_move() == strongSide))
|
&& distance(strongKing, weakPawn) > 2 + (pos.side_to_move() == strongSide))
|
||||||
result = Value(80) - 8 * distance(strongKing, weakPawn);
|
result = Value(80) - 8 * distance(strongKing, weakPawn);
|
||||||
|
|
||||||
else
|
else
|
||||||
result = Value(200) - 8 * ( distance(strongKing, weakPawn + SOUTH)
|
result = Value(200) - 8 * ( distance(strongKing, weakPawn + pawn_push(weakSide))
|
||||||
- distance(weakKing, weakPawn + SOUTH)
|
- distance(weakKing, weakPawn + pawn_push(weakSide))
|
||||||
- distance(weakPawn, queeningSquare));
|
- distance(weakPawn, queeningSquare));
|
||||||
|
|
||||||
return strongSide == pos.side_to_move() ? result : -result;
|
return strongSide == pos.side_to_move() ? result : -result;
|
||||||
|
@ -589,8 +587,8 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
|
||||||
Bitboard strongPawns = pos.pieces(strongSide, PAWN);
|
Bitboard strongPawns = pos.pieces(strongSide, PAWN);
|
||||||
|
|
||||||
// If all pawns are ahead of the king on a single rook file, it's a draw.
|
// If all pawns are ahead of the king on a single rook file, it's a draw.
|
||||||
if (!((strongPawns & ~FileABB) || (strongPawns & ~FileHBB)) &&
|
if ( !(strongPawns & ~(FileABB | FileHBB))
|
||||||
!(strongPawns & ~passed_pawn_span(weakSide, weakKing)))
|
&& !(strongPawns & ~passed_pawn_span(weakSide, weakKing)))
|
||||||
return SCALE_FACTOR_DRAW;
|
return SCALE_FACTOR_DRAW;
|
||||||
|
|
||||||
return SCALE_FACTOR_NONE;
|
return SCALE_FACTOR_NONE;
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
286
src/evaluate.cpp
286
src/evaluate.cpp
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,15 +18,58 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstdlib>
|
||||||
#include <cstring> // For std::memset
|
#include <cstring> // For std::memset
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "bitboard.h"
|
#include "bitboard.h"
|
||||||
|
#include "cluster.h"
|
||||||
#include "evaluate.h"
|
#include "evaluate.h"
|
||||||
#include "material.h"
|
#include "material.h"
|
||||||
#include "pawns.h"
|
#include "pawns.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
|
#include "uci.h"
|
||||||
|
|
||||||
|
namespace Eval {
|
||||||
|
|
||||||
|
bool useNNUE;
|
||||||
|
std::string eval_file_loaded="None";
|
||||||
|
|
||||||
|
void init_NNUE() {
|
||||||
|
|
||||||
|
useNNUE = Options["Use NNUE"];
|
||||||
|
std::string eval_file = std::string(Options["EvalFile"]);
|
||||||
|
if (useNNUE && eval_file_loaded != eval_file)
|
||||||
|
if (Eval::NNUE::load_eval_file(eval_file))
|
||||||
|
eval_file_loaded = eval_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
void verify_NNUE() {
|
||||||
|
|
||||||
|
std::string eval_file = std::string(Options["EvalFile"]);
|
||||||
|
if (useNNUE && eval_file_loaded != eval_file)
|
||||||
|
{
|
||||||
|
UCI::OptionsMap defaults;
|
||||||
|
UCI::init(defaults);
|
||||||
|
|
||||||
|
std::cerr << "NNUE evaluation used, but the network file " << eval_file << " was not loaded successfully. "
|
||||||
|
<< "These network evaluation parameters must be available, and compatible with this version of the code. "
|
||||||
|
<< "The UCI option EvalFile might need to specify the full path, including the directory/folder name, to the file. "
|
||||||
|
<< "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/"+std::string(defaults["EvalFile"]) << std::endl;
|
||||||
|
std::exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Cluster::is_root())
|
||||||
|
{
|
||||||
|
if (useNNUE)
|
||||||
|
sync_cout << "info string NNUE evaluation using " << eval_file << " enabled." << sync_endl;
|
||||||
|
else
|
||||||
|
sync_cout << "info string classical evaluation enabled." << sync_endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace Trace {
|
namespace Trace {
|
||||||
|
|
||||||
|
@ -74,17 +115,20 @@ using namespace Trace;
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Threshold for lazy and space evaluation
|
// Threshold for lazy and space evaluation
|
||||||
constexpr Value LazyThreshold = Value(1400);
|
constexpr Value LazyThreshold1 = Value(1400);
|
||||||
|
constexpr Value LazyThreshold2 = Value(1300);
|
||||||
constexpr Value SpaceThreshold = Value(12222);
|
constexpr Value SpaceThreshold = Value(12222);
|
||||||
|
constexpr Value NNUEThreshold1 = Value(550);
|
||||||
|
constexpr Value NNUEThreshold2 = Value(150);
|
||||||
|
|
||||||
// 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, 81, 52, 44, 10 };
|
||||||
|
|
||||||
// Penalties for enemy's safe checks
|
// SafeCheck[PieceType][single/multiple] contains safe check bonus by piece type,
|
||||||
constexpr int QueenSafeCheck = 772;
|
// higher if multiple safe checks are possible for that piece type.
|
||||||
constexpr int RookSafeCheck = 1084;
|
constexpr int SafeCheck[][2] = {
|
||||||
constexpr int BishopSafeCheck = 645;
|
{}, {}, {792, 1283}, {645, 967}, {1084, 1897}, {772, 1119}
|
||||||
constexpr int KnightSafeCheck = 792;
|
};
|
||||||
|
|
||||||
#define S(mg, eg) make_score(mg, eg)
|
#define S(mg, eg) make_score(mg, eg)
|
||||||
|
|
||||||
|
@ -106,6 +150,18 @@ namespace {
|
||||||
S(110,182), S(114,182), S(114,192), S(116,219) }
|
S(110,182), S(114,182), S(114,192), S(116,219) }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// KingProtector[knight/bishop] contains penalty for each distance unit to own king
|
||||||
|
constexpr Score KingProtector[] = { S(8, 9), S(6, 9) };
|
||||||
|
|
||||||
|
// Outpost[knight/bishop] contains bonuses for each knight or bishop occupying a
|
||||||
|
// pawn protected square on rank 4 to 6 which is also safe from a pawn attack.
|
||||||
|
constexpr Score Outpost[] = { S(56, 36), S(30, 23) };
|
||||||
|
|
||||||
|
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn
|
||||||
|
constexpr Score PassedRank[RANK_NB] = {
|
||||||
|
S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
|
||||||
|
};
|
||||||
|
|
||||||
// RookOnFile[semiopen/open] contains bonuses for each rook when there is
|
// RookOnFile[semiopen/open] contains bonuses for each rook when there is
|
||||||
// no (friendly) pawn on the rook file.
|
// no (friendly) pawn on the rook file.
|
||||||
constexpr Score RookOnFile[] = { S(19, 7), S(48, 29) };
|
constexpr Score RookOnFile[] = { S(19, 7), S(48, 29) };
|
||||||
|
@ -121,28 +177,19 @@ namespace {
|
||||||
S(0, 0), S(3, 46), S(37, 68), S(42, 60), S(0, 38), S(58, 41)
|
S(0, 0), S(3, 46), S(37, 68), S(42, 60), S(0, 38), S(58, 41)
|
||||||
};
|
};
|
||||||
|
|
||||||
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn
|
|
||||||
constexpr Score PassedRank[RANK_NB] = {
|
|
||||||
S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Assorted bonuses and penalties
|
// Assorted bonuses and penalties
|
||||||
constexpr Score BishopKingProtector = S( 6, 9);
|
constexpr Score BadOutpost = S( -7, 36);
|
||||||
constexpr Score BishopOnKingRing = S( 24, 0);
|
constexpr Score BishopOnKingRing = S( 24, 0);
|
||||||
constexpr Score BishopOutpost = S( 30, 23);
|
|
||||||
constexpr Score BishopPawns = S( 3, 7);
|
constexpr Score BishopPawns = S( 3, 7);
|
||||||
constexpr Score BishopXRayPawns = S( 4, 5);
|
constexpr Score BishopXRayPawns = S( 4, 5);
|
||||||
constexpr Score CorneredBishop = S( 50, 50);
|
constexpr Score CorneredBishop = S( 50, 50);
|
||||||
constexpr Score FlankAttacks = S( 8, 0);
|
constexpr Score FlankAttacks = S( 8, 0);
|
||||||
constexpr Score Hanging = S( 69, 36);
|
constexpr Score Hanging = S( 69, 36);
|
||||||
constexpr Score KnightKingProtector = S( 8, 9);
|
|
||||||
constexpr Score KnightOnQueen = S( 16, 11);
|
constexpr Score KnightOnQueen = S( 16, 11);
|
||||||
constexpr Score KnightOutpost = S( 56, 36);
|
|
||||||
constexpr Score LongDiagonalBishop = S( 45, 0);
|
constexpr Score LongDiagonalBishop = S( 45, 0);
|
||||||
constexpr Score MinorBehindPawn = S( 18, 3);
|
constexpr Score MinorBehindPawn = S( 18, 3);
|
||||||
constexpr Score PassedFile = S( 11, 8);
|
constexpr Score PassedFile = S( 11, 8);
|
||||||
constexpr Score PawnlessFlank = S( 17, 95);
|
constexpr Score PawnlessFlank = S( 17, 95);
|
||||||
constexpr Score QueenInfiltration = S( -2, 14);
|
|
||||||
constexpr Score ReachableOutpost = S( 31, 22);
|
constexpr Score ReachableOutpost = S( 31, 22);
|
||||||
constexpr Score RestrictedPiece = S( 7, 7);
|
constexpr Score RestrictedPiece = S( 7, 7);
|
||||||
constexpr Score RookOnKingRing = S( 16, 0);
|
constexpr Score RookOnKingRing = S( 16, 0);
|
||||||
|
@ -305,10 +352,19 @@ namespace {
|
||||||
|
|
||||||
if (Pt == BISHOP || Pt == KNIGHT)
|
if (Pt == BISHOP || Pt == KNIGHT)
|
||||||
{
|
{
|
||||||
// Bonus if piece is on an outpost square or can reach one
|
// Bonus if the piece is on an outpost square or can reach one
|
||||||
bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them);
|
// Reduced bonus for knights (BadOutpost) if few relevant targets
|
||||||
if (bb & s)
|
bb = OutpostRanks & (attackedBy[Us][PAWN] | shift<Down>(pos.pieces(PAWN)))
|
||||||
score += (Pt == KNIGHT) ? KnightOutpost : BishopOutpost;
|
& ~pe->pawn_attacks_span(Them);
|
||||||
|
Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN);
|
||||||
|
|
||||||
|
if ( Pt == KNIGHT
|
||||||
|
&& bb & s & ~CenterFiles // on a side outpost
|
||||||
|
&& !(b & targets) // no relevant attacks
|
||||||
|
&& (!more_than_one(targets & (s & QueenSide ? QueenSide : KingSide))))
|
||||||
|
score += BadOutpost;
|
||||||
|
else if (bb & s)
|
||||||
|
score += Outpost[Pt == BISHOP];
|
||||||
else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
|
else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
|
||||||
score += ReachableOutpost;
|
score += ReachableOutpost;
|
||||||
|
|
||||||
|
@ -317,8 +373,7 @@ namespace {
|
||||||
score += MinorBehindPawn;
|
score += MinorBehindPawn;
|
||||||
|
|
||||||
// Penalty if the piece is far from the king
|
// Penalty if the piece is far from the king
|
||||||
score -= (Pt == KNIGHT ? KnightKingProtector
|
score -= KingProtector[Pt == BISHOP] * distance(pos.square<KING>(Us), s);
|
||||||
: BishopKingProtector) * distance(pos.square<KING>(Us), s);
|
|
||||||
|
|
||||||
if (Pt == BISHOP)
|
if (Pt == BISHOP)
|
||||||
{
|
{
|
||||||
|
@ -377,10 +432,6 @@ namespace {
|
||||||
Bitboard queenPinners;
|
Bitboard queenPinners;
|
||||||
if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners))
|
if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners))
|
||||||
score -= WeakQueen;
|
score -= WeakQueen;
|
||||||
|
|
||||||
// Bonus for queen on weak square in enemy camp
|
|
||||||
if (relative_rank(Us, s) > RANK_4 && (~pe->pawn_attacks_span(Them) & s))
|
|
||||||
score += QueenInfiltration;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (T)
|
if (T)
|
||||||
|
@ -420,41 +471,33 @@ namespace {
|
||||||
b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
|
b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
|
||||||
|
|
||||||
// Enemy rooks checks
|
// Enemy rooks checks
|
||||||
rookChecks = b1 & safe & attackedBy[Them][ROOK];
|
rookChecks = b1 & attackedBy[Them][ROOK] & safe;
|
||||||
if (rookChecks)
|
if (rookChecks)
|
||||||
kingDanger += more_than_one(rookChecks) ? RookSafeCheck * 175/100
|
kingDanger += SafeCheck[ROOK][more_than_one(rookChecks)];
|
||||||
: RookSafeCheck;
|
|
||||||
else
|
else
|
||||||
unsafeChecks |= b1 & attackedBy[Them][ROOK];
|
unsafeChecks |= b1 & attackedBy[Them][ROOK];
|
||||||
|
|
||||||
// Enemy queen safe checks: we count them only if they are from squares from
|
// Enemy queen safe checks: count them only if the checks are from squares from
|
||||||
// which we can't give a rook check, because rook checks are more valuable.
|
// which opponent cannot give a rook check, because rook checks are more valuable.
|
||||||
queenChecks = (b1 | b2)
|
queenChecks = (b1 | b2) & attackedBy[Them][QUEEN] & safe
|
||||||
& attackedBy[Them][QUEEN]
|
& ~(attackedBy[Us][QUEEN] | rookChecks);
|
||||||
& safe
|
|
||||||
& ~attackedBy[Us][QUEEN]
|
|
||||||
& ~rookChecks;
|
|
||||||
if (queenChecks)
|
if (queenChecks)
|
||||||
kingDanger += more_than_one(queenChecks) ? QueenSafeCheck * 145/100
|
kingDanger += SafeCheck[QUEEN][more_than_one(queenChecks)];
|
||||||
: QueenSafeCheck;
|
|
||||||
|
|
||||||
// Enemy bishops checks: we count them only if they are from squares from
|
// Enemy bishops checks: count them only if they are from squares from which
|
||||||
// which we can't give a queen check, because queen checks are more valuable.
|
// opponent cannot give a queen check, because queen checks are more valuable.
|
||||||
bishopChecks = b2
|
bishopChecks = b2 & attackedBy[Them][BISHOP] & safe
|
||||||
& attackedBy[Them][BISHOP]
|
|
||||||
& safe
|
|
||||||
& ~queenChecks;
|
& ~queenChecks;
|
||||||
if (bishopChecks)
|
if (bishopChecks)
|
||||||
kingDanger += more_than_one(bishopChecks) ? BishopSafeCheck * 3/2
|
kingDanger += SafeCheck[BISHOP][more_than_one(bishopChecks)];
|
||||||
: BishopSafeCheck;
|
|
||||||
else
|
else
|
||||||
unsafeChecks |= b2 & attackedBy[Them][BISHOP];
|
unsafeChecks |= b2 & attackedBy[Them][BISHOP];
|
||||||
|
|
||||||
// Enemy knights checks
|
// Enemy knights checks
|
||||||
knightChecks = attacks_bb<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
|
knightChecks = attacks_bb<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
|
||||||
if (knightChecks & safe)
|
if (knightChecks & safe)
|
||||||
kingDanger += more_than_one(knightChecks & safe) ? KnightSafeCheck * 162/100
|
kingDanger += SafeCheck[KNIGHT][more_than_one(knightChecks & safe)];
|
||||||
: KnightSafeCheck;
|
|
||||||
else
|
else
|
||||||
unsafeChecks |= knightChecks;
|
unsafeChecks |= knightChecks;
|
||||||
|
|
||||||
|
@ -464,7 +507,7 @@ namespace {
|
||||||
b2 = b1 & attackedBy2[Them];
|
b2 = b1 & attackedBy2[Them];
|
||||||
b3 = attackedBy[Us][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
|
b3 = attackedBy[Us][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
|
||||||
|
|
||||||
int kingFlankAttack = popcount(b1) + popcount(b2);
|
int kingFlankAttack = popcount(b1) + popcount(b2);
|
||||||
int kingFlankDefense = popcount(b3);
|
int kingFlankDefense = popcount(b3);
|
||||||
|
|
||||||
kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
|
kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
|
||||||
|
@ -575,17 +618,21 @@ namespace {
|
||||||
// Bonus for threats on the next moves against enemy queen
|
// Bonus for threats on the next moves against enemy queen
|
||||||
if (pos.count<QUEEN>(Them) == 1)
|
if (pos.count<QUEEN>(Them) == 1)
|
||||||
{
|
{
|
||||||
|
bool queenImbalance = pos.count<QUEEN>() == 1;
|
||||||
|
|
||||||
Square s = pos.square<QUEEN>(Them);
|
Square s = pos.square<QUEEN>(Them);
|
||||||
safe = mobilityArea[Us] & ~stronglyProtected;
|
safe = mobilityArea[Us]
|
||||||
|
& ~pos.pieces(Us, PAWN)
|
||||||
|
& ~stronglyProtected;
|
||||||
|
|
||||||
b = attackedBy[Us][KNIGHT] & attacks_bb<KNIGHT>(s);
|
b = attackedBy[Us][KNIGHT] & attacks_bb<KNIGHT>(s);
|
||||||
|
|
||||||
score += KnightOnQueen * popcount(b & safe);
|
score += KnightOnQueen * popcount(b & safe) * (1 + queenImbalance);
|
||||||
|
|
||||||
b = (attackedBy[Us][BISHOP] & attacks_bb<BISHOP>(s, pos.pieces()))
|
b = (attackedBy[Us][BISHOP] & attacks_bb<BISHOP>(s, pos.pieces()))
|
||||||
| (attackedBy[Us][ROOK ] & attacks_bb<ROOK >(s, pos.pieces()));
|
| (attackedBy[Us][ROOK ] & attacks_bb<ROOK >(s, pos.pieces()));
|
||||||
|
|
||||||
score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]);
|
score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (T)
|
if (T)
|
||||||
|
@ -725,9 +772,9 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Evaluation::winnable() adjusts the mg and eg score components based on the
|
// Evaluation::winnable() adjusts the midgame and endgame score components, based on
|
||||||
// known attacking/defending status of the players. A single value is derived
|
// the known attacking/defending status of the players. The final value is derived
|
||||||
// by interpolation from the mg and eg values and returned.
|
// by interpolation from the midgame and endgame values.
|
||||||
|
|
||||||
template<Tracing T>
|
template<Tracing T>
|
||||||
Value Evaluation<T>::winnable(Score score) const {
|
Value Evaluation<T>::winnable(Score score) const {
|
||||||
|
@ -741,8 +788,8 @@ namespace {
|
||||||
bool almostUnwinnable = outflanking < 0
|
bool almostUnwinnable = outflanking < 0
|
||||||
&& !pawnsOnBothFlanks;
|
&& !pawnsOnBothFlanks;
|
||||||
|
|
||||||
bool infiltration = rank_of(pos.square<KING>(WHITE)) > RANK_4
|
bool infiltration = rank_of(pos.square<KING>(WHITE)) > RANK_4
|
||||||
|| rank_of(pos.square<KING>(BLACK)) < RANK_5;
|
|| rank_of(pos.square<KING>(BLACK)) < RANK_5;
|
||||||
|
|
||||||
// Compute the initiative bonus for the attacking side
|
// Compute the initiative bonus for the attacking side
|
||||||
int complexity = 9 * pe->passed_count()
|
int complexity = 9 * pe->passed_count()
|
||||||
|
@ -767,11 +814,10 @@ namespace {
|
||||||
eg += v;
|
eg += v;
|
||||||
|
|
||||||
// Compute the scale factor for the winning side
|
// Compute the scale factor for the winning side
|
||||||
|
|
||||||
Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK;
|
Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK;
|
||||||
int sf = me->scale_factor(pos, strongSide);
|
int sf = me->scale_factor(pos, strongSide);
|
||||||
|
|
||||||
// If scale is not already specific, scale down the endgame via general heuristics
|
// If scale factor is not already specific, scale down via general heuristics
|
||||||
if (sf == SCALE_FACTOR_NORMAL)
|
if (sf == SCALE_FACTOR_NORMAL)
|
||||||
{
|
{
|
||||||
if (pos.opposite_bishops())
|
if (pos.opposite_bishops())
|
||||||
|
@ -782,6 +828,15 @@ namespace {
|
||||||
else
|
else
|
||||||
sf = 22 + 3 * pos.count<ALL_PIECES>(strongSide);
|
sf = 22 + 3 * pos.count<ALL_PIECES>(strongSide);
|
||||||
}
|
}
|
||||||
|
else if ( pos.non_pawn_material(WHITE) == RookValueMg
|
||||||
|
&& pos.non_pawn_material(BLACK) == RookValueMg
|
||||||
|
&& pos.count<PAWN>(strongSide) - pos.count<PAWN>(~strongSide) <= 1
|
||||||
|
&& bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN))
|
||||||
|
&& (attacks_bb<KING>(pos.square<KING>(~strongSide)) & pos.pieces(~strongSide, PAWN)))
|
||||||
|
sf = 36;
|
||||||
|
else if (pos.count<QUEEN>() == 1)
|
||||||
|
sf = 37 + 3 * (pos.count<QUEEN>(WHITE) == 1 ? pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK)
|
||||||
|
: pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE));
|
||||||
else
|
else
|
||||||
sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide));
|
sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide));
|
||||||
}
|
}
|
||||||
|
@ -828,9 +883,12 @@ namespace {
|
||||||
score += pe->pawn_score(WHITE) - pe->pawn_score(BLACK);
|
score += pe->pawn_score(WHITE) - pe->pawn_score(BLACK);
|
||||||
|
|
||||||
// Early exit if score is high
|
// Early exit if score is high
|
||||||
Value v = (mg_value(score) + eg_value(score)) / 2;
|
auto lazy_skip = [&](Value lazyThreshold) {
|
||||||
if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64)
|
return abs(mg_value(score) + eg_value(score)) / 2 > lazyThreshold + pos.non_pawn_material() / 64;
|
||||||
return pos.side_to_move() == WHITE ? v : -v;
|
};
|
||||||
|
|
||||||
|
if (lazy_skip(LazyThreshold1))
|
||||||
|
goto make_v;
|
||||||
|
|
||||||
// Main evaluation begins here
|
// Main evaluation begins here
|
||||||
initialize<WHITE>();
|
initialize<WHITE>();
|
||||||
|
@ -847,12 +905,17 @@ namespace {
|
||||||
|
|
||||||
// More complex interactions that require fully populated attack bitboards
|
// More complex interactions that require fully populated attack bitboards
|
||||||
score += king< WHITE>() - king< BLACK>()
|
score += king< WHITE>() - king< BLACK>()
|
||||||
+ threats<WHITE>() - threats<BLACK>()
|
+ passed< WHITE>() - passed< BLACK>();
|
||||||
+ passed< WHITE>() - passed< BLACK>()
|
|
||||||
|
if (lazy_skip(LazyThreshold2))
|
||||||
|
goto make_v;
|
||||||
|
|
||||||
|
score += threats<WHITE>() - threats<BLACK>()
|
||||||
+ space< WHITE>() - space< BLACK>();
|
+ space< WHITE>() - space< BLACK>();
|
||||||
|
|
||||||
|
make_v:
|
||||||
// Derive single value from mg and eg parts of score
|
// Derive single value from mg and eg parts of score
|
||||||
v = winnable(score);
|
Value v = winnable(score);
|
||||||
|
|
||||||
// In case of tracing add all remaining individual evaluation terms
|
// In case of tracing add all remaining individual evaluation terms
|
||||||
if (T)
|
if (T)
|
||||||
|
@ -869,9 +932,6 @@ namespace {
|
||||||
// Side to move point of view
|
// Side to move point of view
|
||||||
v = (pos.side_to_move() == WHITE ? v : -v) + Tempo;
|
v = (pos.side_to_move() == WHITE ? v : -v) + Tempo;
|
||||||
|
|
||||||
// Damp down the evaluation linearly when shuffling
|
|
||||||
v = v * (100 - pos.rule50_count()) / 100;
|
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -882,47 +942,73 @@ namespace {
|
||||||
/// 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) {
|
||||||
return Evaluation<NO_TRACE>(pos).value();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
bool classical = !Eval::useNNUE
|
||||||
|
|| abs(eg_value(pos.psq_score())) * 16 > NNUEThreshold1 * (16 + pos.rule50_count());
|
||||||
|
Value v = classical ? Evaluation<NO_TRACE>(pos).value()
|
||||||
|
: NNUE::evaluate(pos) * 5 / 4 + Tempo;
|
||||||
|
|
||||||
|
if (classical && Eval::useNNUE && abs(v) * 16 < NNUEThreshold2 * (16 + pos.rule50_count()))
|
||||||
|
v = NNUE::evaluate(pos) * 5 / 4 + Tempo;
|
||||||
|
|
||||||
|
// Damp down the evaluation linearly when shuffling
|
||||||
|
v = v * (100 - pos.rule50_count()) / 100;
|
||||||
|
|
||||||
|
// Guarantee evalution outside of TB range
|
||||||
|
v = Utility::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
/// trace() is like evaluate(), but instead of returning a value, it returns
|
/// trace() is like evaluate(), but instead of returning a value, it returns
|
||||||
/// a string (suitable for outputting to stdout) that contains the detailed
|
/// a string (suitable for outputting to stdout) that contains the detailed
|
||||||
/// descriptions and values of each evaluation term. Useful for debugging.
|
/// descriptions and values of each evaluation term. Useful for debugging.
|
||||||
|
/// Trace scores are from white's point of view
|
||||||
|
|
||||||
std::string Eval::trace(const Position& pos) {
|
std::string Eval::trace(const Position& pos) {
|
||||||
|
|
||||||
if (pos.checkers())
|
if (pos.checkers())
|
||||||
return "Total evaluation: none (in check)";
|
return "Final evaluation: none (in check)";
|
||||||
|
|
||||||
std::memset(scores, 0, sizeof(scores));
|
|
||||||
|
|
||||||
pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt
|
|
||||||
|
|
||||||
Value v = Evaluation<TRACE>(pos).value();
|
|
||||||
|
|
||||||
v = pos.side_to_move() == WHITE ? v : -v; // Trace scores are from white's point of view
|
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
|
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2);
|
||||||
<< " Term | White | Black | Total \n"
|
|
||||||
<< " | MG EG | MG EG | MG EG \n"
|
Value v;
|
||||||
<< " ------------+-------------+-------------+------------\n"
|
|
||||||
<< " Material | " << Term(MATERIAL)
|
if (Eval::useNNUE)
|
||||||
<< " Imbalance | " << Term(IMBALANCE)
|
{
|
||||||
<< " Pawns | " << Term(PAWN)
|
v = NNUE::evaluate(pos);
|
||||||
<< " Knights | " << Term(KNIGHT)
|
}
|
||||||
<< " Bishops | " << Term(BISHOP)
|
else
|
||||||
<< " Rooks | " << Term(ROOK)
|
{
|
||||||
<< " Queens | " << Term(QUEEN)
|
std::memset(scores, 0, sizeof(scores));
|
||||||
<< " Mobility | " << Term(MOBILITY)
|
|
||||||
<< " King safety | " << Term(KING)
|
pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt
|
||||||
<< " Threats | " << Term(THREAT)
|
|
||||||
<< " Passed | " << Term(PASSED)
|
v = Evaluation<TRACE>(pos).value();
|
||||||
<< " Space | " << Term(SPACE)
|
|
||||||
<< " Winnable | " << Term(WINNABLE)
|
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
|
||||||
<< " ------------+-------------+-------------+------------\n"
|
<< " Term | White | Black | Total \n"
|
||||||
<< " Total | " << Term(TOTAL);
|
<< " | MG EG | MG EG | MG EG \n"
|
||||||
|
<< " ------------+-------------+-------------+------------\n"
|
||||||
|
<< " Material | " << Term(MATERIAL)
|
||||||
|
<< " Imbalance | " << Term(IMBALANCE)
|
||||||
|
<< " Pawns | " << Term(PAWN)
|
||||||
|
<< " Knights | " << Term(KNIGHT)
|
||||||
|
<< " Bishops | " << Term(BISHOP)
|
||||||
|
<< " Rooks | " << Term(ROOK)
|
||||||
|
<< " Queens | " << Term(QUEEN)
|
||||||
|
<< " Mobility | " << Term(MOBILITY)
|
||||||
|
<< " King safety | " << Term(KING)
|
||||||
|
<< " Threats | " << Term(THREAT)
|
||||||
|
<< " Passed | " << Term(PASSED)
|
||||||
|
<< " Space | " << Term(SPACE)
|
||||||
|
<< " Winnable | " << Term(WINNABLE)
|
||||||
|
<< " ------------+-------------+-------------+------------\n"
|
||||||
|
<< " Total | " << Term(TOTAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
v = pos.side_to_move() == WHITE ? v : -v;
|
||||||
|
|
||||||
ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n";
|
ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n";
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -29,9 +27,23 @@ class Position;
|
||||||
|
|
||||||
namespace Eval {
|
namespace Eval {
|
||||||
|
|
||||||
std::string trace(const Position& pos);
|
std::string trace(const Position& pos);
|
||||||
|
Value evaluate(const Position& pos);
|
||||||
|
|
||||||
Value evaluate(const Position& pos);
|
extern bool useNNUE;
|
||||||
}
|
extern std::string eval_file_loaded;
|
||||||
|
void init_NNUE();
|
||||||
|
void verify_NNUE();
|
||||||
|
|
||||||
|
namespace NNUE {
|
||||||
|
|
||||||
|
Value evaluate(const Position& pos);
|
||||||
|
Value compute_eval(const Position& pos);
|
||||||
|
void update_eval(const Position& pos);
|
||||||
|
bool load_eval_file(const std::string& evalFile);
|
||||||
|
|
||||||
|
} // namespace NNUE
|
||||||
|
|
||||||
|
} // namespace Eval
|
||||||
|
|
||||||
#endif // #ifndef EVALUATE_H_INCLUDED
|
#endif // #ifndef EVALUATE_H_INCLUDED
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -48,6 +46,7 @@ int main(int argc, char* argv[]) {
|
||||||
Endgames::init();
|
Endgames::init();
|
||||||
Threads.set(size_t(Options["Threads"]));
|
Threads.set(size_t(Options["Threads"]));
|
||||||
Search::clear(); // After threads are up
|
Search::clear(); // After threads are up
|
||||||
|
Eval::init_NNUE();
|
||||||
|
|
||||||
UCI::loop(argc, argv);
|
UCI::loop(argc, argv);
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
77
src/misc.cpp
77
src/misc.cpp
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -46,12 +44,18 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
#if defined(__linux__) && !defined(__ANDROID__)
|
#if defined(__linux__) && !defined(__ANDROID__)
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32))
|
||||||
|
#define POSIXALIGNEDALLOC
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
|
|
||||||
|
@ -147,10 +151,8 @@ const string engine_info(bool to_uci) {
|
||||||
ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
|
ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
ss << (Is64Bit ? " 64" : "")
|
ss << (to_uci ? "\nid author ": " by ")
|
||||||
<< (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : ""))
|
<< "the Stockfish developers (see AUTHORS file)";
|
||||||
<< (to_uci ? "\nid author ": " by ")
|
|
||||||
<< "T. Romstad, M. Costalba, J. Kiiski, G. Linscott";
|
|
||||||
|
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
@ -215,7 +217,40 @@ const std::string compiler_info() {
|
||||||
compiler += " on unknown system";
|
compiler += " on unknown system";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
compiler += "\n __VERSION__ macro expands to: ";
|
compiler += "\nCompilation settings include: ";
|
||||||
|
compiler += (Is64Bit ? " 64bit" : " 32bit");
|
||||||
|
#if defined(USE_VNNI)
|
||||||
|
compiler += " VNNI";
|
||||||
|
#endif
|
||||||
|
#if defined(USE_AVX512)
|
||||||
|
compiler += " AVX512";
|
||||||
|
#endif
|
||||||
|
compiler += (HasPext ? " BMI2" : "");
|
||||||
|
#if defined(USE_AVX2)
|
||||||
|
compiler += " AVX2";
|
||||||
|
#endif
|
||||||
|
#if defined(USE_SSE41)
|
||||||
|
compiler += " SSE41";
|
||||||
|
#endif
|
||||||
|
#if defined(USE_SSSE3)
|
||||||
|
compiler += " SSSE3";
|
||||||
|
#endif
|
||||||
|
#if defined(USE_SSE2)
|
||||||
|
compiler += " SSE2";
|
||||||
|
#endif
|
||||||
|
compiler += (HasPopCnt ? " POPCNT" : "");
|
||||||
|
#if defined(USE_MMX)
|
||||||
|
compiler += " MMX";
|
||||||
|
#endif
|
||||||
|
#if defined(USE_NEON)
|
||||||
|
compiler += " NEON";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
compiler += " DEBUG";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
compiler += "\n__VERSION__ macro expands to: ";
|
||||||
#ifdef __VERSION__
|
#ifdef __VERSION__
|
||||||
compiler += __VERSION__;
|
compiler += __VERSION__;
|
||||||
#else
|
#else
|
||||||
|
@ -293,6 +328,32 @@ void prefetch(void* addr) {
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// Wrappers for systems where the c++17 implementation doesn't guarantee the availability of aligned_alloc.
|
||||||
|
/// Memory allocated with std_aligned_alloc must be freed with std_aligned_free.
|
||||||
|
///
|
||||||
|
|
||||||
|
void* std_aligned_alloc(size_t alignment, size_t size) {
|
||||||
|
#if defined(POSIXALIGNEDALLOC)
|
||||||
|
void *pointer;
|
||||||
|
if(posix_memalign(&pointer, alignment, size) == 0)
|
||||||
|
return pointer;
|
||||||
|
return nullptr;
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
return _mm_malloc(size, alignment);
|
||||||
|
#else
|
||||||
|
return std::aligned_alloc(alignment, size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void std_aligned_free(void* ptr) {
|
||||||
|
#if defined(POSIXALIGNEDALLOC)
|
||||||
|
free(ptr);
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
_mm_free(ptr);
|
||||||
|
#else
|
||||||
|
free(ptr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/// aligned_ttmem_alloc() will return suitably aligned memory, and if possible use large pages.
|
/// aligned_ttmem_alloc() will return suitably aligned memory, and if possible use large pages.
|
||||||
/// The returned pointer is the aligned one, while the mem argument is the one that needs
|
/// The returned pointer is the aligned one, while the mem argument is the one that needs
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -33,6 +31,8 @@ const std::string engine_info(bool to_uci = false);
|
||||||
const std::string compiler_info();
|
const std::string compiler_info();
|
||||||
void prefetch(void* addr);
|
void prefetch(void* addr);
|
||||||
void start_logger(const std::string& fname);
|
void start_logger(const std::string& fname);
|
||||||
|
void* std_aligned_alloc(size_t alignment, size_t size);
|
||||||
|
void std_aligned_free(void* ptr);
|
||||||
void* aligned_ttmem_alloc(size_t size, void*& mem);
|
void* aligned_ttmem_alloc(size_t size, void*& mem);
|
||||||
void aligned_ttmem_free(void* mem); // nop if mem == nullptr
|
void aligned_ttmem_free(void* mem); // nop if mem == nullptr
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -29,22 +27,20 @@ namespace {
|
||||||
ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {
|
ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {
|
||||||
|
|
||||||
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
||||||
|
{
|
||||||
*moveList++ = make<PROMOTION>(to - D, to, QUEEN);
|
*moveList++ = make<PROMOTION>(to - D, to, QUEEN);
|
||||||
|
if (attacks_bb<KNIGHT>(to) & ksq)
|
||||||
|
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
|
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
|
||||||
{
|
{
|
||||||
*moveList++ = make<PROMOTION>(to - D, to, ROOK);
|
*moveList++ = make<PROMOTION>(to - D, to, ROOK);
|
||||||
*moveList++ = make<PROMOTION>(to - D, to, BISHOP);
|
*moveList++ = make<PROMOTION>(to - D, to, BISHOP);
|
||||||
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
|
if (!(attacks_bb<KNIGHT>(to) & ksq))
|
||||||
|
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Knight promotion is the only promotion that can give a direct check
|
|
||||||
// that's not already included in the queen promotion.
|
|
||||||
if (Type == QUIET_CHECKS && (attacks_bb<KNIGHT>(to) & ksq))
|
|
||||||
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
|
|
||||||
else
|
|
||||||
(void)ksq; // Silence a warning under MSVC
|
|
||||||
|
|
||||||
return moveList;
|
return moveList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,8 +259,8 @@ namespace {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
/// <CAPTURES> Generates all pseudo-legal captures and queen promotions
|
/// <CAPTURES> Generates all pseudo-legal captures plus queen and checking knight promotions
|
||||||
/// <QUIETS> Generates all pseudo-legal non-captures and underpromotions
|
/// <QUIETS> Generates all pseudo-legal non-captures and underpromotions(except checking knight)
|
||||||
/// <NON_EVASIONS> Generates all pseudo-legal captures and non-captures
|
/// <NON_EVASIONS> Generates all pseudo-legal captures and non-captures
|
||||||
///
|
///
|
||||||
/// Returns a pointer to the end of the move list.
|
/// Returns a pointer to the end of the move list.
|
||||||
|
@ -287,8 +283,8 @@ template ExtMove* generate<QUIETS>(const Position&, ExtMove*);
|
||||||
template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
|
template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
|
||||||
|
|
||||||
|
|
||||||
/// generate<QUIET_CHECKS> generates all pseudo-legal non-captures and knight
|
/// generate<QUIET_CHECKS> generates all pseudo-legal non-captures.
|
||||||
/// underpromotions that give check. Returns a pointer to the end of the move list.
|
/// Returns a pointer to the end of the move list.
|
||||||
template<>
|
template<>
|
||||||
ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
|
ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
54
src/nnue/architectures/halfkp_256x2-32-32.h
Normal file
54
src/nnue/architectures/halfkp_256x2-32-32.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2020 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 input features and network structure used in NNUE evaluation function
|
||||||
|
|
||||||
|
#ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED
|
||||||
|
#define NNUE_HALFKP_256X2_32_32_H_INCLUDED
|
||||||
|
|
||||||
|
#include "../features/feature_set.h"
|
||||||
|
#include "../features/half_kp.h"
|
||||||
|
|
||||||
|
#include "../layers/input_slice.h"
|
||||||
|
#include "../layers/affine_transform.h"
|
||||||
|
#include "../layers/clipped_relu.h"
|
||||||
|
|
||||||
|
namespace Eval::NNUE {
|
||||||
|
|
||||||
|
// Input features used in evaluation function
|
||||||
|
using RawFeatures = Features::FeatureSet<
|
||||||
|
Features::HalfKP<Features::Side::kFriend>>;
|
||||||
|
|
||||||
|
// Number of input feature dimensions after conversion
|
||||||
|
constexpr IndexType kTransformedFeatureDimensions = 256;
|
||||||
|
|
||||||
|
namespace Layers {
|
||||||
|
|
||||||
|
// Define network structure
|
||||||
|
using InputLayer = InputSlice<kTransformedFeatureDimensions * 2>;
|
||||||
|
using HiddenLayer1 = ClippedReLU<AffineTransform<InputLayer, 32>>;
|
||||||
|
using HiddenLayer2 = ClippedReLU<AffineTransform<HiddenLayer1, 32>>;
|
||||||
|
using OutputLayer = AffineTransform<HiddenLayer2, 1>;
|
||||||
|
|
||||||
|
} // namespace Layers
|
||||||
|
|
||||||
|
using Network = Layers::OutputLayer;
|
||||||
|
|
||||||
|
} // namespace Eval::NNUE
|
||||||
|
|
||||||
|
#endif // #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED
|
175
src/nnue/evaluate_nnue.cpp
Normal file
175
src/nnue/evaluate_nnue.cpp
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code for calculating NNUE evaluation function
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "../evaluate.h"
|
||||||
|
#include "../position.h"
|
||||||
|
#include "../misc.h"
|
||||||
|
#include "../uci.h"
|
||||||
|
|
||||||
|
#include "evaluate_nnue.h"
|
||||||
|
|
||||||
|
ExtPieceSquare kpp_board_index[PIECE_NB] = {
|
||||||
|
// convention: W - us, B - them
|
||||||
|
// viewed from other side, W and B are reversed
|
||||||
|
{ PS_NONE, PS_NONE },
|
||||||
|
{ PS_W_PAWN, PS_B_PAWN },
|
||||||
|
{ PS_W_KNIGHT, PS_B_KNIGHT },
|
||||||
|
{ PS_W_BISHOP, PS_B_BISHOP },
|
||||||
|
{ PS_W_ROOK, PS_B_ROOK },
|
||||||
|
{ PS_W_QUEEN, PS_B_QUEEN },
|
||||||
|
{ PS_W_KING, PS_B_KING },
|
||||||
|
{ PS_NONE, PS_NONE },
|
||||||
|
{ PS_NONE, PS_NONE },
|
||||||
|
{ PS_B_PAWN, PS_W_PAWN },
|
||||||
|
{ PS_B_KNIGHT, PS_W_KNIGHT },
|
||||||
|
{ PS_B_BISHOP, PS_W_BISHOP },
|
||||||
|
{ PS_B_ROOK, PS_W_ROOK },
|
||||||
|
{ PS_B_QUEEN, PS_W_QUEEN },
|
||||||
|
{ PS_B_KING, PS_W_KING },
|
||||||
|
{ PS_NONE, PS_NONE }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace Eval::NNUE {
|
||||||
|
|
||||||
|
// Input feature converter
|
||||||
|
AlignedPtr<FeatureTransformer> feature_transformer;
|
||||||
|
|
||||||
|
// Evaluation function
|
||||||
|
AlignedPtr<Network> network;
|
||||||
|
|
||||||
|
// Evaluation function file name
|
||||||
|
std::string fileName;
|
||||||
|
|
||||||
|
namespace Detail {
|
||||||
|
|
||||||
|
// Initialize the evaluation function parameters
|
||||||
|
template <typename T>
|
||||||
|
void Initialize(AlignedPtr<T>& pointer) {
|
||||||
|
|
||||||
|
pointer.reset(reinterpret_cast<T*>(std_aligned_alloc(alignof(T), sizeof(T))));
|
||||||
|
std::memset(pointer.get(), 0, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read evaluation function parameters
|
||||||
|
template <typename T>
|
||||||
|
bool ReadParameters(std::istream& stream, const AlignedPtr<T>& pointer) {
|
||||||
|
|
||||||
|
std::uint32_t header;
|
||||||
|
header = read_little_endian<std::uint32_t>(stream);
|
||||||
|
if (!stream || header != T::GetHashValue()) return false;
|
||||||
|
return pointer->ReadParameters(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Detail
|
||||||
|
|
||||||
|
// Initialize the evaluation function parameters
|
||||||
|
void Initialize() {
|
||||||
|
|
||||||
|
Detail::Initialize(feature_transformer);
|
||||||
|
Detail::Initialize(network);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read network header
|
||||||
|
bool ReadHeader(std::istream& stream, std::uint32_t* hash_value, std::string* architecture)
|
||||||
|
{
|
||||||
|
std::uint32_t version, size;
|
||||||
|
|
||||||
|
version = read_little_endian<std::uint32_t>(stream);
|
||||||
|
*hash_value = read_little_endian<std::uint32_t>(stream);
|
||||||
|
size = read_little_endian<std::uint32_t>(stream);
|
||||||
|
if (!stream || version != kVersion) return false;
|
||||||
|
architecture->resize(size);
|
||||||
|
stream.read(&(*architecture)[0], size);
|
||||||
|
return !stream.fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read network parameters
|
||||||
|
bool ReadParameters(std::istream& stream) {
|
||||||
|
|
||||||
|
std::uint32_t hash_value;
|
||||||
|
std::string architecture;
|
||||||
|
if (!ReadHeader(stream, &hash_value, &architecture)) return false;
|
||||||
|
if (hash_value != kHashValue) return false;
|
||||||
|
if (!Detail::ReadParameters(stream, feature_transformer)) return false;
|
||||||
|
if (!Detail::ReadParameters(stream, network)) return false;
|
||||||
|
return stream && stream.peek() == std::ios::traits_type::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proceed with the difference calculation if possible
|
||||||
|
static void UpdateAccumulatorIfPossible(const Position& pos) {
|
||||||
|
|
||||||
|
feature_transformer->UpdateAccumulatorIfPossible(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the evaluation value
|
||||||
|
static Value ComputeScore(const Position& pos, bool refresh) {
|
||||||
|
|
||||||
|
auto& accumulator = pos.state()->accumulator;
|
||||||
|
if (!refresh && accumulator.computed_score) {
|
||||||
|
return accumulator.score;
|
||||||
|
}
|
||||||
|
|
||||||
|
alignas(kCacheLineSize) TransformedFeatureType
|
||||||
|
transformed_features[FeatureTransformer::kBufferSize];
|
||||||
|
feature_transformer->Transform(pos, transformed_features, refresh);
|
||||||
|
alignas(kCacheLineSize) char buffer[Network::kBufferSize];
|
||||||
|
const auto output = network->Propagate(transformed_features, buffer);
|
||||||
|
|
||||||
|
auto score = static_cast<Value>(output[0] / FV_SCALE);
|
||||||
|
|
||||||
|
accumulator.score = score;
|
||||||
|
accumulator.computed_score = true;
|
||||||
|
return accumulator.score;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the evaluation function file
|
||||||
|
bool load_eval_file(const std::string& evalFile) {
|
||||||
|
|
||||||
|
Initialize();
|
||||||
|
fileName = evalFile;
|
||||||
|
|
||||||
|
std::ifstream stream(evalFile, std::ios::binary);
|
||||||
|
|
||||||
|
const bool result = ReadParameters(stream);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluation function. Perform differential calculation.
|
||||||
|
Value evaluate(const Position& pos) {
|
||||||
|
return ComputeScore(pos, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluation function. Perform full calculation.
|
||||||
|
Value compute_eval(const Position& pos) {
|
||||||
|
return ComputeScore(pos, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proceed with the difference calculation if possible
|
||||||
|
void update_eval(const Position& pos) {
|
||||||
|
UpdateAccumulatorIfPossible(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Eval::NNUE
|
48
src/nnue/evaluate_nnue.h
Normal file
48
src/nnue/evaluate_nnue.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// header used in NNUE evaluation function
|
||||||
|
|
||||||
|
#ifndef NNUE_EVALUATE_NNUE_H_INCLUDED
|
||||||
|
#define NNUE_EVALUATE_NNUE_H_INCLUDED
|
||||||
|
|
||||||
|
#include "nnue_feature_transformer.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Eval::NNUE {
|
||||||
|
|
||||||
|
// Hash value of evaluation function structure
|
||||||
|
constexpr std::uint32_t kHashValue =
|
||||||
|
FeatureTransformer::GetHashValue() ^ Network::GetHashValue();
|
||||||
|
|
||||||
|
// Deleter for automating release of memory area
|
||||||
|
template <typename T>
|
||||||
|
struct AlignedDeleter {
|
||||||
|
void operator()(T* ptr) const {
|
||||||
|
ptr->~T();
|
||||||
|
std_aligned_free(ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using AlignedPtr = std::unique_ptr<T, AlignedDeleter<T>>;
|
||||||
|
|
||||||
|
} // namespace Eval::NNUE
|
||||||
|
|
||||||
|
#endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED
|
135
src/nnue/features/feature_set.h
Normal file
135
src/nnue/features/feature_set.h
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// A class template that represents the input feature set of the NNUE evaluation function
|
||||||
|
|
||||||
|
#ifndef NNUE_FEATURE_SET_H_INCLUDED
|
||||||
|
#define NNUE_FEATURE_SET_H_INCLUDED
|
||||||
|
|
||||||
|
#include "features_common.h"
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace Eval::NNUE::Features {
|
||||||
|
|
||||||
|
// Class template that represents a list of values
|
||||||
|
template <typename T, T... Values>
|
||||||
|
struct CompileTimeList;
|
||||||
|
|
||||||
|
template <typename T, T First, T... Remaining>
|
||||||
|
struct CompileTimeList<T, First, Remaining...> {
|
||||||
|
static constexpr bool Contains(T value) {
|
||||||
|
return value == First || CompileTimeList<T, Remaining...>::Contains(value);
|
||||||
|
}
|
||||||
|
static constexpr std::array<T, sizeof...(Remaining) + 1>
|
||||||
|
kValues = {{First, Remaining...}};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Base class of feature set
|
||||||
|
template <typename Derived>
|
||||||
|
class FeatureSetBase {
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Get a list of indices for active features
|
||||||
|
template <typename IndexListType>
|
||||||
|
static void AppendActiveIndices(
|
||||||
|
const Position& pos, TriggerEvent trigger, IndexListType active[2]) {
|
||||||
|
|
||||||
|
for (Color perspective : { WHITE, BLACK }) {
|
||||||
|
Derived::CollectActiveIndices(
|
||||||
|
pos, trigger, perspective, &active[perspective]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a list of indices for recently changed features
|
||||||
|
template <typename PositionType, typename IndexListType>
|
||||||
|
static void AppendChangedIndices(
|
||||||
|
const PositionType& pos, TriggerEvent trigger,
|
||||||
|
IndexListType removed[2], IndexListType added[2], bool reset[2]) {
|
||||||
|
|
||||||
|
const auto& dp = pos.state()->dirtyPiece;
|
||||||
|
if (dp.dirty_num == 0) return;
|
||||||
|
|
||||||
|
for (Color perspective : { WHITE, BLACK }) {
|
||||||
|
reset[perspective] = false;
|
||||||
|
switch (trigger) {
|
||||||
|
case TriggerEvent::kFriendKingMoved:
|
||||||
|
reset[perspective] =
|
||||||
|
dp.pieceId[0] == PIECE_ID_KING + perspective;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (reset[perspective]) {
|
||||||
|
Derived::CollectActiveIndices(
|
||||||
|
pos, trigger, perspective, &added[perspective]);
|
||||||
|
} else {
|
||||||
|
Derived::CollectChangedIndices(
|
||||||
|
pos, trigger, perspective,
|
||||||
|
&removed[perspective], &added[perspective]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Class template that represents the feature set
|
||||||
|
template <typename FeatureType>
|
||||||
|
class FeatureSet<FeatureType> : public FeatureSetBase<FeatureSet<FeatureType>> {
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Hash value embedded in the evaluation file
|
||||||
|
static constexpr std::uint32_t kHashValue = FeatureType::kHashValue;
|
||||||
|
// Number of feature dimensions
|
||||||
|
static constexpr IndexType kDimensions = FeatureType::kDimensions;
|
||||||
|
// Maximum number of simultaneously active features
|
||||||
|
static constexpr IndexType kMaxActiveDimensions =
|
||||||
|
FeatureType::kMaxActiveDimensions;
|
||||||
|
// Trigger for full calculation instead of difference calculation
|
||||||
|
using SortedTriggerSet =
|
||||||
|
CompileTimeList<TriggerEvent, FeatureType::kRefreshTrigger>;
|
||||||
|
static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Get a list of indices for active features
|
||||||
|
static void CollectActiveIndices(
|
||||||
|
const Position& pos, const TriggerEvent trigger, const Color perspective,
|
||||||
|
IndexList* const active) {
|
||||||
|
if (FeatureType::kRefreshTrigger == trigger) {
|
||||||
|
FeatureType::AppendActiveIndices(pos, perspective, active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a list of indices for recently changed features
|
||||||
|
static void CollectChangedIndices(
|
||||||
|
const Position& pos, const TriggerEvent trigger, const Color perspective,
|
||||||
|
IndexList* const removed, IndexList* const added) {
|
||||||
|
|
||||||
|
if (FeatureType::kRefreshTrigger == trigger) {
|
||||||
|
FeatureType::AppendChangedIndices(pos, perspective, removed, added);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the base class and the class template that recursively uses itself a friend
|
||||||
|
friend class FeatureSetBase<FeatureSet>;
|
||||||
|
template <typename... FeatureTypes>
|
||||||
|
friend class FeatureSet;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Eval::NNUE::Features
|
||||||
|
|
||||||
|
#endif // #ifndef NNUE_FEATURE_SET_H_INCLUDED
|
45
src/nnue/features/features_common.h
Normal file
45
src/nnue/features/features_common.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Common header of input features of NNUE evaluation function
|
||||||
|
|
||||||
|
#ifndef NNUE_FEATURES_COMMON_H_INCLUDED
|
||||||
|
#define NNUE_FEATURES_COMMON_H_INCLUDED
|
||||||
|
|
||||||
|
#include "../../evaluate.h"
|
||||||
|
#include "../nnue_common.h"
|
||||||
|
|
||||||
|
namespace Eval::NNUE::Features {
|
||||||
|
|
||||||
|
class IndexList;
|
||||||
|
|
||||||
|
template <typename... FeatureTypes>
|
||||||
|
class FeatureSet;
|
||||||
|
|
||||||
|
// Trigger to perform full calculations instead of difference only
|
||||||
|
enum class TriggerEvent {
|
||||||
|
kFriendKingMoved // calculate full evaluation when own king moves
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Side {
|
||||||
|
kFriend // side to move
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Eval::NNUE::Features
|
||||||
|
|
||||||
|
#endif // #ifndef NNUE_FEATURES_COMMON_H_INCLUDED
|
92
src/nnue/features/half_kp.cpp
Normal file
92
src/nnue/features/half_kp.cpp
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2020 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 input features HalfKP of NNUE evaluation function
|
||||||
|
|
||||||
|
#include "half_kp.h"
|
||||||
|
#include "index_list.h"
|
||||||
|
|
||||||
|
namespace Eval::NNUE::Features {
|
||||||
|
|
||||||
|
// Find the index of the feature quantity from the king position and PieceSquare
|
||||||
|
template <Side AssociatedKing>
|
||||||
|
inline IndexType HalfKP<AssociatedKing>::MakeIndex(Square sq_k, PieceSquare p) {
|
||||||
|
return static_cast<IndexType>(PS_END) * static_cast<IndexType>(sq_k) + p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get pieces information
|
||||||
|
template <Side AssociatedKing>
|
||||||
|
inline void HalfKP<AssociatedKing>::GetPieces(
|
||||||
|
const Position& pos, Color perspective,
|
||||||
|
PieceSquare** pieces, Square* sq_target_k) {
|
||||||
|
|
||||||
|
*pieces = (perspective == BLACK) ?
|
||||||
|
pos.eval_list()->piece_list_fb() :
|
||||||
|
pos.eval_list()->piece_list_fw();
|
||||||
|
const PieceId target = (AssociatedKing == Side::kFriend) ?
|
||||||
|
static_cast<PieceId>(PIECE_ID_KING + perspective) :
|
||||||
|
static_cast<PieceId>(PIECE_ID_KING + ~perspective);
|
||||||
|
*sq_target_k = static_cast<Square>(((*pieces)[target] - PS_W_KING) % SQUARE_NB);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a list of indices for active features
|
||||||
|
template <Side AssociatedKing>
|
||||||
|
void HalfKP<AssociatedKing>::AppendActiveIndices(
|
||||||
|
const Position& pos, Color perspective, IndexList* active) {
|
||||||
|
|
||||||
|
// Do nothing if array size is small to avoid compiler warning
|
||||||
|
if (RawFeatures::kMaxActiveDimensions < kMaxActiveDimensions) return;
|
||||||
|
|
||||||
|
PieceSquare* pieces;
|
||||||
|
Square sq_target_k;
|
||||||
|
GetPieces(pos, perspective, &pieces, &sq_target_k);
|
||||||
|
for (PieceId i = PIECE_ID_ZERO; i < PIECE_ID_KING; ++i) {
|
||||||
|
if (pieces[i] != PS_NONE) {
|
||||||
|
active->push_back(MakeIndex(sq_target_k, pieces[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a list of indices for recently changed features
|
||||||
|
template <Side AssociatedKing>
|
||||||
|
void HalfKP<AssociatedKing>::AppendChangedIndices(
|
||||||
|
const Position& pos, Color perspective,
|
||||||
|
IndexList* removed, IndexList* added) {
|
||||||
|
|
||||||
|
PieceSquare* pieces;
|
||||||
|
Square sq_target_k;
|
||||||
|
GetPieces(pos, perspective, &pieces, &sq_target_k);
|
||||||
|
const auto& dp = pos.state()->dirtyPiece;
|
||||||
|
for (int i = 0; i < dp.dirty_num; ++i) {
|
||||||
|
if (dp.pieceId[i] >= PIECE_ID_KING) continue;
|
||||||
|
const auto old_p = static_cast<PieceSquare>(
|
||||||
|
dp.old_piece[i].from[perspective]);
|
||||||
|
if (old_p != PS_NONE) {
|
||||||
|
removed->push_back(MakeIndex(sq_target_k, old_p));
|
||||||
|
}
|
||||||
|
const auto new_p = static_cast<PieceSquare>(
|
||||||
|
dp.new_piece[i].from[perspective]);
|
||||||
|
if (new_p != PS_NONE) {
|
||||||
|
added->push_back(MakeIndex(sq_target_k, new_p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template class HalfKP<Side::kFriend>;
|
||||||
|
|
||||||
|
} // namespace Eval::NNUE::Features
|
67
src/nnue/features/half_kp.h
Normal file
67
src/nnue/features/half_kp.h
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2020 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 input features HalfKP of NNUE evaluation function
|
||||||
|
|
||||||
|
#ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED
|
||||||
|
#define NNUE_FEATURES_HALF_KP_H_INCLUDED
|
||||||
|
|
||||||
|
#include "../../evaluate.h"
|
||||||
|
#include "features_common.h"
|
||||||
|
|
||||||
|
namespace Eval::NNUE::Features {
|
||||||
|
|
||||||
|
// Feature HalfKP: Combination of the position of own king
|
||||||
|
// and the position of pieces other than kings
|
||||||
|
template <Side AssociatedKing>
|
||||||
|
class HalfKP {
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Feature name
|
||||||
|
static constexpr const char* kName = "HalfKP(Friend)";
|
||||||
|
// Hash value embedded in the evaluation file
|
||||||
|
static constexpr std::uint32_t kHashValue =
|
||||||
|
0x5D69D5B9u ^ (AssociatedKing == Side::kFriend);
|
||||||
|
// Number of feature dimensions
|
||||||
|
static constexpr IndexType kDimensions =
|
||||||
|
static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_END);
|
||||||
|
// Maximum number of simultaneously active features
|
||||||
|
static constexpr IndexType kMaxActiveDimensions = PIECE_ID_KING;
|
||||||
|
// Trigger for full calculation instead of difference calculation
|
||||||
|
static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved;
|
||||||
|
|
||||||
|
// Get a list of indices for active features
|
||||||
|
static void AppendActiveIndices(const Position& pos, Color perspective,
|
||||||
|
IndexList* active);
|
||||||
|
|
||||||
|
// Get a list of indices for recently changed features
|
||||||
|
static void AppendChangedIndices(const Position& pos, Color perspective,
|
||||||
|
IndexList* removed, IndexList* added);
|
||||||
|
|
||||||
|
// Index of a feature for a given king position and another piece on some square
|
||||||
|
static IndexType MakeIndex(Square sq_k, PieceSquare p);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Get pieces information
|
||||||
|
static void GetPieces(const Position& pos, Color perspective,
|
||||||
|
PieceSquare** pieces, Square* sq_target_k);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Eval::NNUE::Features
|
||||||
|
|
||||||
|
#endif // #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED
|
64
src/nnue/features/index_list.h
Normal file
64
src/nnue/features/index_list.h
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2020 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 index list of input features
|
||||||
|
|
||||||
|
#ifndef NNUE_FEATURES_INDEX_LIST_H_INCLUDED
|
||||||
|
#define NNUE_FEATURES_INDEX_LIST_H_INCLUDED
|
||||||
|
|
||||||
|
#include "../../position.h"
|
||||||
|
#include "../nnue_architecture.h"
|
||||||
|
|
||||||
|
namespace Eval::NNUE::Features {
|
||||||
|
|
||||||
|
// Class template used for feature index list
|
||||||
|
template <typename T, std::size_t MaxSize>
|
||||||
|
class ValueList {
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::size_t size() const { return size_; }
|
||||||
|
void resize(std::size_t size) { size_ = size; }
|
||||||
|
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* end() const { return values_ + size_; }
|
||||||
|
|
||||||
|
void swap(ValueList& other) {
|
||||||
|
const std::size_t max_size = std::max(size_, other.size_);
|
||||||
|
for (std::size_t i = 0; i < max_size; ++i) {
|
||||||
|
std::swap(values_[i], other.values_[i]);
|
||||||
|
}
|
||||||
|
std::swap(size_, other.size_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T values_[MaxSize];
|
||||||
|
std::size_t size_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Type of feature index list
|
||||||
|
class IndexList
|
||||||
|
: public ValueList<IndexType, RawFeatures::kMaxActiveDimensions> {
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Eval::NNUE::Features
|
||||||
|
|
||||||
|
#endif // NNUE_FEATURES_INDEX_LIST_H_INCLUDED
|
260
src/nnue/layers/affine_transform.h
Normal file
260
src/nnue/layers/affine_transform.h
Normal file
|
@ -0,0 +1,260 @@
|
||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2020 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 AffineTransform of NNUE evaluation function
|
||||||
|
|
||||||
|
#ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED
|
||||||
|
#define NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "../nnue_common.h"
|
||||||
|
|
||||||
|
namespace Eval::NNUE::Layers {
|
||||||
|
|
||||||
|
// Affine transformation layer
|
||||||
|
template <typename PreviousLayer, IndexType OutputDimensions>
|
||||||
|
class AffineTransform {
|
||||||
|
public:
|
||||||
|
// Input/output type
|
||||||
|
using InputType = typename PreviousLayer::OutputType;
|
||||||
|
using OutputType = std::int32_t;
|
||||||
|
static_assert(std::is_same<InputType, std::uint8_t>::value, "");
|
||||||
|
|
||||||
|
// Number of input/output dimensions
|
||||||
|
static constexpr IndexType kInputDimensions =
|
||||||
|
PreviousLayer::kOutputDimensions;
|
||||||
|
static constexpr IndexType kOutputDimensions = OutputDimensions;
|
||||||
|
static constexpr IndexType kPaddedInputDimensions =
|
||||||
|
CeilToMultiple<IndexType>(kInputDimensions, kMaxSimdWidth);
|
||||||
|
|
||||||
|
// Size of forward propagation buffer used in this layer
|
||||||
|
static constexpr std::size_t kSelfBufferSize =
|
||||||
|
CeilToMultiple(kOutputDimensions * sizeof(OutputType), kCacheLineSize);
|
||||||
|
|
||||||
|
// Size of the forward propagation buffer used from the input layer to this layer
|
||||||
|
static constexpr std::size_t kBufferSize =
|
||||||
|
PreviousLayer::kBufferSize + kSelfBufferSize;
|
||||||
|
|
||||||
|
// Hash value embedded in the evaluation file
|
||||||
|
static constexpr std::uint32_t GetHashValue() {
|
||||||
|
std::uint32_t hash_value = 0xCC03DAE4u;
|
||||||
|
hash_value += kOutputDimensions;
|
||||||
|
hash_value ^= PreviousLayer::GetHashValue() >> 1;
|
||||||
|
hash_value ^= PreviousLayer::GetHashValue() << 31;
|
||||||
|
return hash_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read network parameters
|
||||||
|
bool ReadParameters(std::istream& stream) {
|
||||||
|
if (!previous_layer_.ReadParameters(stream)) return false;
|
||||||
|
for (std::size_t i = 0; i < kOutputDimensions; ++i)
|
||||||
|
biases_[i] = read_little_endian<BiasType>(stream);
|
||||||
|
for (std::size_t i = 0; i < kOutputDimensions * kPaddedInputDimensions; ++i)
|
||||||
|
weights_[i] = read_little_endian<WeightType>(stream);
|
||||||
|
return !stream.fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward propagation
|
||||||
|
const OutputType* Propagate(
|
||||||
|
const TransformedFeatureType* transformed_features, char* buffer) const {
|
||||||
|
const auto input = previous_layer_.Propagate(
|
||||||
|
transformed_features, buffer + kSelfBufferSize);
|
||||||
|
const auto output = reinterpret_cast<OutputType*>(buffer);
|
||||||
|
|
||||||
|
#if defined(USE_AVX512)
|
||||||
|
constexpr IndexType kNumChunks = kPaddedInputDimensions / (kSimdWidth * 2);
|
||||||
|
const auto input_vector = reinterpret_cast<const __m512i*>(input);
|
||||||
|
#if !defined(USE_VNNI)
|
||||||
|
const __m512i kOnes = _mm512_set1_epi16(1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(USE_AVX2)
|
||||||
|
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
|
||||||
|
const __m256i kOnes = _mm256_set1_epi16(1);
|
||||||
|
const auto input_vector = reinterpret_cast<const __m256i*>(input);
|
||||||
|
|
||||||
|
#elif defined(USE_SSE2)
|
||||||
|
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
|
||||||
|
#ifndef USE_SSSE3
|
||||||
|
const __m128i kZeros = _mm_setzero_si128();
|
||||||
|
#else
|
||||||
|
const __m128i kOnes = _mm_set1_epi16(1);
|
||||||
|
#endif
|
||||||
|
const auto input_vector = reinterpret_cast<const __m128i*>(input);
|
||||||
|
|
||||||
|
#elif defined(USE_MMX)
|
||||||
|
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
|
||||||
|
const __m64 kZeros = _mm_setzero_si64();
|
||||||
|
const auto input_vector = reinterpret_cast<const __m64*>(input);
|
||||||
|
|
||||||
|
#elif defined(USE_NEON)
|
||||||
|
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
|
||||||
|
const auto input_vector = reinterpret_cast<const int8x8_t*>(input);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (IndexType i = 0; i < kOutputDimensions; ++i) {
|
||||||
|
const IndexType offset = i * kPaddedInputDimensions;
|
||||||
|
|
||||||
|
#if defined(USE_AVX512)
|
||||||
|
__m512i sum = _mm512_setzero_si512();
|
||||||
|
const auto row = reinterpret_cast<const __m512i*>(&weights_[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
#if defined(USE_VNNI)
|
||||||
|
sum = _mm512_dpbusd_epi32(sum, _mm512_loadA_si512(&input_vector[j]), _mm512_load_si512(&row[j]));
|
||||||
|
#else
|
||||||
|
__m512i product = _mm512_maddubs_epi16(_mm512_loadA_si512(&input_vector[j]), _mm512_load_si512(&row[j]));
|
||||||
|
product = _mm512_madd_epi16(product, kOnes);
|
||||||
|
sum = _mm512_add_epi32(sum, product);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Changing kMaxSimdWidth from 32 to 64 breaks loading existing networks.
|
||||||
|
// As a result kPaddedInputDimensions may not be an even multiple of 64(512bit)
|
||||||
|
// and we have to do one more 256bit chunk.
|
||||||
|
if (kPaddedInputDimensions != kNumChunks * kSimdWidth * 2)
|
||||||
|
{
|
||||||
|
const auto iv256 = reinterpret_cast<const __m256i*>(&input_vector[kNumChunks]);
|
||||||
|
const auto row256 = reinterpret_cast<const __m256i*>(&row[kNumChunks]);
|
||||||
|
#if defined(USE_VNNI)
|
||||||
|
__m256i product256 = _mm256_dpbusd_epi32(
|
||||||
|
_mm512_castsi512_si256(sum), _mm256_loadA_si256(&iv256[0]), _mm256_load_si256(&row256[0]));
|
||||||
|
sum = _mm512_inserti32x8(sum, product256, 0);
|
||||||
|
#else
|
||||||
|
__m256i product256 = _mm256_maddubs_epi16(_mm256_loadA_si256(&iv256[0]), _mm256_load_si256(&row256[0]));
|
||||||
|
sum = _mm512_add_epi32(sum, _mm512_cvtepi16_epi32(product256));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
output[i] = _mm512_reduce_add_epi32(sum) + biases_[i];
|
||||||
|
|
||||||
|
#elif defined(USE_AVX2)
|
||||||
|
__m256i sum = _mm256_setzero_si256();
|
||||||
|
const auto row = reinterpret_cast<const __m256i*>(&weights_[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
__m256i product = _mm256_maddubs_epi16(_mm256_loadA_si256(&input_vector[j]), _mm256_load_si256(&row[j]));
|
||||||
|
product = _mm256_madd_epi16(product, kOnes);
|
||||||
|
sum = _mm256_add_epi32(sum, product);
|
||||||
|
}
|
||||||
|
__m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1));
|
||||||
|
sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC));
|
||||||
|
sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB));
|
||||||
|
output[i] = _mm_cvtsi128_si32(sum128) + biases_[i];
|
||||||
|
|
||||||
|
#elif defined(USE_SSSE3)
|
||||||
|
__m128i sum = _mm_setzero_si128();
|
||||||
|
const auto row = reinterpret_cast<const __m128i*>(&weights_[offset]);
|
||||||
|
for (int j = 0; j < (int)kNumChunks - 1; j += 2) {
|
||||||
|
__m128i product0 = _mm_maddubs_epi16(_mm_load_si128(&input_vector[j]), _mm_load_si128(&row[j]));
|
||||||
|
product0 = _mm_madd_epi16(product0, kOnes);
|
||||||
|
sum = _mm_add_epi32(sum, product0);
|
||||||
|
__m128i product1 = _mm_maddubs_epi16(_mm_load_si128(&input_vector[j+1]), _mm_load_si128(&row[j+1]));
|
||||||
|
product1 = _mm_madd_epi16(product1, kOnes);
|
||||||
|
sum = _mm_add_epi32(sum, product1);
|
||||||
|
}
|
||||||
|
if (kNumChunks & 0x1) {
|
||||||
|
__m128i product = _mm_maddubs_epi16(_mm_load_si128(&input_vector[kNumChunks-1]), _mm_load_si128(&row[kNumChunks-1]));
|
||||||
|
product = _mm_madd_epi16(product, kOnes);
|
||||||
|
sum = _mm_add_epi32(sum, product);
|
||||||
|
}
|
||||||
|
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC
|
||||||
|
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB
|
||||||
|
output[i] = _mm_cvtsi128_si32(sum) + biases_[i];
|
||||||
|
|
||||||
|
#elif defined(USE_SSE2)
|
||||||
|
__m128i sum_lo = _mm_cvtsi32_si128(biases_[i]);
|
||||||
|
__m128i sum_hi = kZeros;
|
||||||
|
const auto row = reinterpret_cast<const __m128i*>(&weights_[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
__m128i row_j = _mm_load_si128(&row[j]);
|
||||||
|
__m128i input_j = _mm_load_si128(&input_vector[j]);
|
||||||
|
__m128i row_signs = _mm_cmpgt_epi8(kZeros, row_j);
|
||||||
|
__m128i extended_row_lo = _mm_unpacklo_epi8(row_j, row_signs);
|
||||||
|
__m128i extended_row_hi = _mm_unpackhi_epi8(row_j, row_signs);
|
||||||
|
__m128i extended_input_lo = _mm_unpacklo_epi8(input_j, kZeros);
|
||||||
|
__m128i extended_input_hi = _mm_unpackhi_epi8(input_j, kZeros);
|
||||||
|
__m128i product_lo = _mm_madd_epi16(extended_row_lo, extended_input_lo);
|
||||||
|
__m128i product_hi = _mm_madd_epi16(extended_row_hi, extended_input_hi);
|
||||||
|
sum_lo = _mm_add_epi32(sum_lo, product_lo);
|
||||||
|
sum_hi = _mm_add_epi32(sum_hi, product_hi);
|
||||||
|
}
|
||||||
|
__m128i sum = _mm_add_epi32(sum_lo, sum_hi);
|
||||||
|
__m128i sum_high_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2));
|
||||||
|
sum = _mm_add_epi32(sum, sum_high_64);
|
||||||
|
__m128i sum_second_32 = _mm_shufflelo_epi16(sum, _MM_SHUFFLE(1, 0, 3, 2));
|
||||||
|
sum = _mm_add_epi32(sum, sum_second_32);
|
||||||
|
output[i] = _mm_cvtsi128_si32(sum);
|
||||||
|
|
||||||
|
#elif defined(USE_MMX)
|
||||||
|
__m64 sum_lo = _mm_cvtsi32_si64(biases_[i]);
|
||||||
|
__m64 sum_hi = kZeros;
|
||||||
|
const auto row = reinterpret_cast<const __m64*>(&weights_[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
__m64 row_j = row[j];
|
||||||
|
__m64 input_j = input_vector[j];
|
||||||
|
__m64 row_signs = _mm_cmpgt_pi8(kZeros, row_j);
|
||||||
|
__m64 extended_row_lo = _mm_unpacklo_pi8(row_j, row_signs);
|
||||||
|
__m64 extended_row_hi = _mm_unpackhi_pi8(row_j, row_signs);
|
||||||
|
__m64 extended_input_lo = _mm_unpacklo_pi8(input_j, kZeros);
|
||||||
|
__m64 extended_input_hi = _mm_unpackhi_pi8(input_j, kZeros);
|
||||||
|
__m64 product_lo = _mm_madd_pi16(extended_row_lo, extended_input_lo);
|
||||||
|
__m64 product_hi = _mm_madd_pi16(extended_row_hi, extended_input_hi);
|
||||||
|
sum_lo = _mm_add_pi32(sum_lo, product_lo);
|
||||||
|
sum_hi = _mm_add_pi32(sum_hi, product_hi);
|
||||||
|
}
|
||||||
|
__m64 sum = _mm_add_pi32(sum_lo, sum_hi);
|
||||||
|
sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum));
|
||||||
|
output[i] = _mm_cvtsi64_si32(sum);
|
||||||
|
|
||||||
|
#elif defined(USE_NEON)
|
||||||
|
int32x4_t sum = {biases_[i]};
|
||||||
|
const auto row = reinterpret_cast<const int8x8_t*>(&weights_[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
int16x8_t product = vmull_s8(input_vector[j * 2], row[j * 2]);
|
||||||
|
product = vmlal_s8(product, input_vector[j * 2 + 1], row[j * 2 + 1]);
|
||||||
|
sum = vpadalq_s16(sum, product);
|
||||||
|
}
|
||||||
|
output[i] = sum[0] + sum[1] + sum[2] + sum[3];
|
||||||
|
|
||||||
|
#else
|
||||||
|
OutputType sum = biases_[i];
|
||||||
|
for (IndexType j = 0; j < kInputDimensions; ++j) {
|
||||||
|
sum += weights_[offset + j] * input[j];
|
||||||
|
}
|
||||||
|
output[i] = sum;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
#if defined(USE_MMX)
|
||||||
|
_mm_empty();
|
||||||
|
#endif
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using BiasType = OutputType;
|
||||||
|
using WeightType = std::int8_t;
|
||||||
|
|
||||||
|
PreviousLayer previous_layer_;
|
||||||
|
|
||||||
|
alignas(kCacheLineSize) BiasType biases_[kOutputDimensions];
|
||||||
|
alignas(kCacheLineSize)
|
||||||
|
WeightType weights_[kOutputDimensions * kPaddedInputDimensions];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Eval::NNUE::Layers
|
||||||
|
|
||||||
|
#endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED
|
166
src/nnue/layers/clipped_relu.h
Normal file
166
src/nnue/layers/clipped_relu.h
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2020 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_CLIPPED_RELU_H_INCLUDED
|
||||||
|
#define NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED
|
||||||
|
|
||||||
|
#include "../nnue_common.h"
|
||||||
|
|
||||||
|
namespace Eval::NNUE::Layers {
|
||||||
|
|
||||||
|
// Clipped ReLU
|
||||||
|
template <typename PreviousLayer>
|
||||||
|
class ClippedReLU {
|
||||||
|
public:
|
||||||
|
// Input/output type
|
||||||
|
using InputType = typename PreviousLayer::OutputType;
|
||||||
|
using OutputType = std::uint8_t;
|
||||||
|
static_assert(std::is_same<InputType, std::int32_t>::value, "");
|
||||||
|
|
||||||
|
// Number of input/output dimensions
|
||||||
|
static constexpr IndexType kInputDimensions =
|
||||||
|
PreviousLayer::kOutputDimensions;
|
||||||
|
static constexpr IndexType kOutputDimensions = kInputDimensions;
|
||||||
|
|
||||||
|
// Size of forward propagation buffer used in this layer
|
||||||
|
static constexpr std::size_t kSelfBufferSize =
|
||||||
|
CeilToMultiple(kOutputDimensions * sizeof(OutputType), kCacheLineSize);
|
||||||
|
|
||||||
|
// Size of the forward propagation buffer used from the input layer to this layer
|
||||||
|
static constexpr std::size_t kBufferSize =
|
||||||
|
PreviousLayer::kBufferSize + kSelfBufferSize;
|
||||||
|
|
||||||
|
// Hash value embedded in the evaluation file
|
||||||
|
static constexpr std::uint32_t GetHashValue() {
|
||||||
|
std::uint32_t hash_value = 0x538D24C7u;
|
||||||
|
hash_value += PreviousLayer::GetHashValue();
|
||||||
|
return hash_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read network parameters
|
||||||
|
bool ReadParameters(std::istream& stream) {
|
||||||
|
return previous_layer_.ReadParameters(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward propagation
|
||||||
|
const OutputType* Propagate(
|
||||||
|
const TransformedFeatureType* transformed_features, char* buffer) const {
|
||||||
|
const auto input = previous_layer_.Propagate(
|
||||||
|
transformed_features, buffer + kSelfBufferSize);
|
||||||
|
const auto output = reinterpret_cast<OutputType*>(buffer);
|
||||||
|
|
||||||
|
#if defined(USE_AVX2)
|
||||||
|
constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth;
|
||||||
|
const __m256i kZero = _mm256_setzero_si256();
|
||||||
|
const __m256i kOffsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0);
|
||||||
|
const auto in = reinterpret_cast<const __m256i*>(input);
|
||||||
|
const auto out = reinterpret_cast<__m256i*>(output);
|
||||||
|
for (IndexType i = 0; i < kNumChunks; ++i) {
|
||||||
|
const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32(
|
||||||
|
_mm256_loadA_si256(&in[i * 4 + 0]),
|
||||||
|
_mm256_loadA_si256(&in[i * 4 + 1])), kWeightScaleBits);
|
||||||
|
const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32(
|
||||||
|
_mm256_loadA_si256(&in[i * 4 + 2]),
|
||||||
|
_mm256_loadA_si256(&in[i * 4 + 3])), kWeightScaleBits);
|
||||||
|
_mm256_storeA_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8(
|
||||||
|
_mm256_packs_epi16(words0, words1), kZero), kOffsets));
|
||||||
|
}
|
||||||
|
constexpr IndexType kStart = kNumChunks * kSimdWidth;
|
||||||
|
|
||||||
|
#elif defined(USE_SSE2)
|
||||||
|
constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth;
|
||||||
|
|
||||||
|
#ifdef USE_SSE41
|
||||||
|
const __m128i kZero = _mm_setzero_si128();
|
||||||
|
#else
|
||||||
|
const __m128i k0x80s = _mm_set1_epi8(-128);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const auto in = reinterpret_cast<const __m128i*>(input);
|
||||||
|
const auto out = reinterpret_cast<__m128i*>(output);
|
||||||
|
for (IndexType i = 0; i < kNumChunks; ++i) {
|
||||||
|
const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32(
|
||||||
|
_mm_load_si128(&in[i * 4 + 0]),
|
||||||
|
_mm_load_si128(&in[i * 4 + 1])), kWeightScaleBits);
|
||||||
|
const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32(
|
||||||
|
_mm_load_si128(&in[i * 4 + 2]),
|
||||||
|
_mm_load_si128(&in[i * 4 + 3])), kWeightScaleBits);
|
||||||
|
const __m128i packedbytes = _mm_packs_epi16(words0, words1);
|
||||||
|
_mm_store_si128(&out[i],
|
||||||
|
|
||||||
|
#ifdef USE_SSE41
|
||||||
|
_mm_max_epi8(packedbytes, kZero)
|
||||||
|
#else
|
||||||
|
_mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
constexpr IndexType kStart = kNumChunks * kSimdWidth;
|
||||||
|
|
||||||
|
#elif defined(USE_MMX)
|
||||||
|
constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth;
|
||||||
|
const __m64 k0x80s = _mm_set1_pi8(-128);
|
||||||
|
const auto in = reinterpret_cast<const __m64*>(input);
|
||||||
|
const auto out = reinterpret_cast<__m64*>(output);
|
||||||
|
for (IndexType i = 0; i < kNumChunks; ++i) {
|
||||||
|
const __m64 words0 = _mm_srai_pi16(
|
||||||
|
_mm_packs_pi32(in[i * 4 + 0], in[i * 4 + 1]),
|
||||||
|
kWeightScaleBits);
|
||||||
|
const __m64 words1 = _mm_srai_pi16(
|
||||||
|
_mm_packs_pi32(in[i * 4 + 2], in[i * 4 + 3]),
|
||||||
|
kWeightScaleBits);
|
||||||
|
const __m64 packedbytes = _mm_packs_pi16(words0, words1);
|
||||||
|
out[i] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s);
|
||||||
|
}
|
||||||
|
_mm_empty();
|
||||||
|
constexpr IndexType kStart = kNumChunks * kSimdWidth;
|
||||||
|
|
||||||
|
#elif defined(USE_NEON)
|
||||||
|
constexpr IndexType kNumChunks = kInputDimensions / (kSimdWidth / 2);
|
||||||
|
const int8x8_t kZero = {0};
|
||||||
|
const auto in = reinterpret_cast<const int32x4_t*>(input);
|
||||||
|
const auto out = reinterpret_cast<int8x8_t*>(output);
|
||||||
|
for (IndexType i = 0; i < kNumChunks; ++i) {
|
||||||
|
int16x8_t shifted;
|
||||||
|
const auto pack = reinterpret_cast<int16x4_t*>(&shifted);
|
||||||
|
pack[0] = vqshrn_n_s32(in[i * 2 + 0], kWeightScaleBits);
|
||||||
|
pack[1] = vqshrn_n_s32(in[i * 2 + 1], kWeightScaleBits);
|
||||||
|
out[i] = vmax_s8(vqmovn_s16(shifted), kZero);
|
||||||
|
}
|
||||||
|
constexpr IndexType kStart = kNumChunks * (kSimdWidth / 2);
|
||||||
|
#else
|
||||||
|
constexpr IndexType kStart = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (IndexType i = kStart; i < kInputDimensions; ++i) {
|
||||||
|
output[i] = static_cast<OutputType>(
|
||||||
|
std::max(0, std::min(127, input[i] >> kWeightScaleBits)));
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PreviousLayer previous_layer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Eval::NNUE::Layers
|
||||||
|
|
||||||
|
#endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED
|
68
src/nnue/layers/input_slice.h
Normal file
68
src/nnue/layers/input_slice.h
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// NNUE evaluation function layer InputSlice definition
|
||||||
|
|
||||||
|
#ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED
|
||||||
|
#define NNUE_LAYERS_INPUT_SLICE_H_INCLUDED
|
||||||
|
|
||||||
|
#include "../nnue_common.h"
|
||||||
|
|
||||||
|
namespace Eval::NNUE::Layers {
|
||||||
|
|
||||||
|
// Input layer
|
||||||
|
template <IndexType OutputDimensions, IndexType Offset = 0>
|
||||||
|
class InputSlice {
|
||||||
|
public:
|
||||||
|
// Need to maintain alignment
|
||||||
|
static_assert(Offset % kMaxSimdWidth == 0, "");
|
||||||
|
|
||||||
|
// Output type
|
||||||
|
using OutputType = TransformedFeatureType;
|
||||||
|
|
||||||
|
// Output dimensionality
|
||||||
|
static constexpr IndexType kOutputDimensions = OutputDimensions;
|
||||||
|
|
||||||
|
// Size of forward propagation buffer used from the input layer to this layer
|
||||||
|
static constexpr std::size_t kBufferSize = 0;
|
||||||
|
|
||||||
|
// Hash value embedded in the evaluation file
|
||||||
|
static constexpr std::uint32_t GetHashValue() {
|
||||||
|
std::uint32_t hash_value = 0xEC42E90Du;
|
||||||
|
hash_value ^= kOutputDimensions ^ (Offset << 10);
|
||||||
|
return hash_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read network parameters
|
||||||
|
bool ReadParameters(std::istream& /*stream*/) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward propagation
|
||||||
|
const OutputType* Propagate(
|
||||||
|
const TransformedFeatureType* transformed_features,
|
||||||
|
char* /*buffer*/) const {
|
||||||
|
return transformed_features + Offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Layers
|
||||||
|
|
||||||
|
#endif // #ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED
|
39
src/nnue/nnue_accumulator.h
Normal file
39
src/nnue/nnue_accumulator.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Class for difference calculation of NNUE evaluation function
|
||||||
|
|
||||||
|
#ifndef NNUE_ACCUMULATOR_H_INCLUDED
|
||||||
|
#define NNUE_ACCUMULATOR_H_INCLUDED
|
||||||
|
|
||||||
|
#include "nnue_architecture.h"
|
||||||
|
|
||||||
|
namespace Eval::NNUE {
|
||||||
|
|
||||||
|
// Class that holds the result of affine transformation of input features
|
||||||
|
struct alignas(kCacheLineSize) Accumulator {
|
||||||
|
std::int16_t
|
||||||
|
accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions];
|
||||||
|
Value score;
|
||||||
|
bool computed_accumulation;
|
||||||
|
bool computed_score;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Eval::NNUE
|
||||||
|
|
||||||
|
#endif // NNUE_ACCUMULATOR_H_INCLUDED
|
38
src/nnue/nnue_architecture.h
Normal file
38
src/nnue/nnue_architecture.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Input features and network structure used in NNUE evaluation function
|
||||||
|
|
||||||
|
#ifndef NNUE_ARCHITECTURE_H_INCLUDED
|
||||||
|
#define NNUE_ARCHITECTURE_H_INCLUDED
|
||||||
|
|
||||||
|
// Defines the network structure
|
||||||
|
#include "architectures/halfkp_256x2-32-32.h"
|
||||||
|
|
||||||
|
namespace Eval::NNUE {
|
||||||
|
|
||||||
|
static_assert(kTransformedFeatureDimensions % kMaxSimdWidth == 0, "");
|
||||||
|
static_assert(Network::kOutputDimensions == 1, "");
|
||||||
|
static_assert(std::is_same<Network::OutputType, std::int32_t>::value, "");
|
||||||
|
|
||||||
|
// Trigger for full calculation instead of difference calculation
|
||||||
|
constexpr auto kRefreshTriggers = RawFeatures::kRefreshTriggers;
|
||||||
|
|
||||||
|
} // namespace Eval::NNUE
|
||||||
|
|
||||||
|
#endif // #ifndef NNUE_ARCHITECTURE_H_INCLUDED
|
127
src/nnue/nnue_common.h
Normal file
127
src/nnue/nnue_common.h
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Constants used in NNUE evaluation function
|
||||||
|
|
||||||
|
#ifndef NNUE_COMMON_H_INCLUDED
|
||||||
|
#define NNUE_COMMON_H_INCLUDED
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#if defined(USE_AVX2)
|
||||||
|
#include <immintrin.h>
|
||||||
|
|
||||||
|
#elif defined(USE_SSE41)
|
||||||
|
#include <smmintrin.h>
|
||||||
|
|
||||||
|
#elif defined(USE_SSSE3)
|
||||||
|
#include <tmmintrin.h>
|
||||||
|
|
||||||
|
#elif defined(USE_SSE2)
|
||||||
|
#include <emmintrin.h>
|
||||||
|
|
||||||
|
#elif defined(USE_MMX)
|
||||||
|
#include <mmintrin.h>
|
||||||
|
|
||||||
|
#elif defined(USE_NEON)
|
||||||
|
#include <arm_neon.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Otherwise a binary
|
||||||
|
// compiled with older g++ crashes because the output memory is not aligned
|
||||||
|
// even though alignas is specified.
|
||||||
|
#if defined(USE_AVX2)
|
||||||
|
#if defined(__GNUC__ ) && (__GNUC__ < 9) && defined(_WIN32)
|
||||||
|
#define _mm256_loadA_si256 _mm256_loadu_si256
|
||||||
|
#define _mm256_storeA_si256 _mm256_storeu_si256
|
||||||
|
#else
|
||||||
|
#define _mm256_loadA_si256 _mm256_load_si256
|
||||||
|
#define _mm256_storeA_si256 _mm256_store_si256
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(USE_AVX512)
|
||||||
|
#if defined(__GNUC__ ) && (__GNUC__ < 9) && defined(_WIN32)
|
||||||
|
#define _mm512_loadA_si512 _mm512_loadu_si512
|
||||||
|
#define _mm512_storeA_si512 _mm512_storeu_si512
|
||||||
|
#else
|
||||||
|
#define _mm512_loadA_si512 _mm512_load_si512
|
||||||
|
#define _mm512_storeA_si512 _mm512_store_si512
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Eval::NNUE {
|
||||||
|
|
||||||
|
// Version of the evaluation file
|
||||||
|
constexpr std::uint32_t kVersion = 0x7AF32F16u;
|
||||||
|
|
||||||
|
// Constant used in evaluation value calculation
|
||||||
|
constexpr int FV_SCALE = 16;
|
||||||
|
constexpr int kWeightScaleBits = 6;
|
||||||
|
|
||||||
|
// Size of cache line (in bytes)
|
||||||
|
constexpr std::size_t kCacheLineSize = 64;
|
||||||
|
|
||||||
|
// SIMD width (in bytes)
|
||||||
|
#if defined(USE_AVX2)
|
||||||
|
constexpr std::size_t kSimdWidth = 32;
|
||||||
|
|
||||||
|
#elif defined(USE_SSE2)
|
||||||
|
constexpr std::size_t kSimdWidth = 16;
|
||||||
|
|
||||||
|
#elif defined(USE_MMX)
|
||||||
|
constexpr std::size_t kSimdWidth = 8;
|
||||||
|
|
||||||
|
#elif defined(USE_NEON)
|
||||||
|
constexpr std::size_t kSimdWidth = 16;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
constexpr std::size_t kMaxSimdWidth = 32;
|
||||||
|
|
||||||
|
// Type of input feature after conversion
|
||||||
|
using TransformedFeatureType = std::uint8_t;
|
||||||
|
using IndexType = std::uint32_t;
|
||||||
|
|
||||||
|
// Round n up to be a multiple of base
|
||||||
|
template <typename IntType>
|
||||||
|
constexpr IntType CeilToMultiple(IntType n, IntType base) {
|
||||||
|
return (n + base - 1) / base * base;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read_little_endian() is our utility to read an integer (signed or unsigned, any size)
|
||||||
|
// from a stream in little-endian order. We swap the byte order after the read if
|
||||||
|
// necessary to return a result with the byte ordering of the compiling machine.
|
||||||
|
template <typename IntType>
|
||||||
|
inline IntType read_little_endian(std::istream& stream) {
|
||||||
|
|
||||||
|
IntType result;
|
||||||
|
std::uint8_t u[sizeof(IntType)];
|
||||||
|
typename std::make_unsigned<IntType>::type v = 0;
|
||||||
|
|
||||||
|
stream.read(reinterpret_cast<char*>(u), sizeof(IntType));
|
||||||
|
for (std::size_t i = 0; i < sizeof(IntType); ++i)
|
||||||
|
v = (v << 8) | u[sizeof(IntType) - i - 1];
|
||||||
|
|
||||||
|
std::memcpy(&result, &v, sizeof(IntType));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Eval::NNUE
|
||||||
|
|
||||||
|
#endif // #ifndef NNUE_COMMON_H_INCLUDED
|
378
src/nnue/nnue_feature_transformer.h
Normal file
378
src/nnue/nnue_feature_transformer.h
Normal file
|
@ -0,0 +1,378 @@
|
||||||
|
/*
|
||||||
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
|
Copyright (C) 2004-2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// A class that converts the input features of the NNUE evaluation function
|
||||||
|
|
||||||
|
#ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED
|
||||||
|
#define NNUE_FEATURE_TRANSFORMER_H_INCLUDED
|
||||||
|
|
||||||
|
#include "nnue_common.h"
|
||||||
|
#include "nnue_architecture.h"
|
||||||
|
#include "features/index_list.h"
|
||||||
|
|
||||||
|
#include <cstring> // std::memset()
|
||||||
|
|
||||||
|
namespace Eval::NNUE {
|
||||||
|
|
||||||
|
// Input feature converter
|
||||||
|
class FeatureTransformer {
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Number of output dimensions for one side
|
||||||
|
static constexpr IndexType kHalfDimensions = kTransformedFeatureDimensions;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Output type
|
||||||
|
using OutputType = TransformedFeatureType;
|
||||||
|
|
||||||
|
// Number of input/output dimensions
|
||||||
|
static constexpr IndexType kInputDimensions = RawFeatures::kDimensions;
|
||||||
|
static constexpr IndexType kOutputDimensions = kHalfDimensions * 2;
|
||||||
|
|
||||||
|
// Size of forward propagation buffer
|
||||||
|
static constexpr std::size_t kBufferSize =
|
||||||
|
kOutputDimensions * sizeof(OutputType);
|
||||||
|
|
||||||
|
// Hash value embedded in the evaluation file
|
||||||
|
static constexpr std::uint32_t GetHashValue() {
|
||||||
|
return RawFeatures::kHashValue ^ kOutputDimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read network parameters
|
||||||
|
bool ReadParameters(std::istream& stream) {
|
||||||
|
for (std::size_t i = 0; i < kHalfDimensions; ++i)
|
||||||
|
biases_[i] = read_little_endian<BiasType>(stream);
|
||||||
|
for (std::size_t i = 0; i < kHalfDimensions * kInputDimensions; ++i)
|
||||||
|
weights_[i] = read_little_endian<WeightType>(stream);
|
||||||
|
return !stream.fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proceed with the difference calculation if possible
|
||||||
|
bool UpdateAccumulatorIfPossible(const Position& pos) const {
|
||||||
|
const auto now = pos.state();
|
||||||
|
if (now->accumulator.computed_accumulation) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const auto prev = now->previous;
|
||||||
|
if (prev && prev->accumulator.computed_accumulation) {
|
||||||
|
UpdateAccumulator(pos);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert input features
|
||||||
|
void Transform(const Position& pos, OutputType* output, bool refresh) const {
|
||||||
|
if (refresh || !UpdateAccumulatorIfPossible(pos)) {
|
||||||
|
RefreshAccumulator(pos);
|
||||||
|
}
|
||||||
|
const auto& accumulation = pos.state()->accumulator.accumulation;
|
||||||
|
|
||||||
|
#if defined(USE_AVX2)
|
||||||
|
constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth;
|
||||||
|
constexpr int kControl = 0b11011000;
|
||||||
|
const __m256i kZero = _mm256_setzero_si256();
|
||||||
|
|
||||||
|
#elif defined(USE_SSE2)
|
||||||
|
constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth;
|
||||||
|
|
||||||
|
#ifdef USE_SSE41
|
||||||
|
const __m128i kZero = _mm_setzero_si128();
|
||||||
|
#else
|
||||||
|
const __m128i k0x80s = _mm_set1_epi8(-128);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(USE_MMX)
|
||||||
|
constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth;
|
||||||
|
const __m64 k0x80s = _mm_set1_pi8(-128);
|
||||||
|
|
||||||
|
#elif defined(USE_NEON)
|
||||||
|
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||||
|
const int8x8_t kZero = {0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()};
|
||||||
|
for (IndexType p = 0; p < 2; ++p) {
|
||||||
|
const IndexType offset = kHalfDimensions * p;
|
||||||
|
|
||||||
|
#if defined(USE_AVX2)
|
||||||
|
auto out = reinterpret_cast<__m256i*>(&output[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
__m256i sum0 = _mm256_loadA_si256(
|
||||||
|
&reinterpret_cast<const __m256i*>(accumulation[perspectives[p]][0])[j * 2 + 0]);
|
||||||
|
__m256i sum1 = _mm256_loadA_si256(
|
||||||
|
&reinterpret_cast<const __m256i*>(accumulation[perspectives[p]][0])[j * 2 + 1]);
|
||||||
|
_mm256_storeA_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8(
|
||||||
|
_mm256_packs_epi16(sum0, sum1), kZero), kControl));
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(USE_SSE2)
|
||||||
|
auto out = reinterpret_cast<__m128i*>(&output[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
__m128i sum0 = _mm_load_si128(&reinterpret_cast<const __m128i*>(
|
||||||
|
accumulation[perspectives[p]][0])[j * 2 + 0]);
|
||||||
|
__m128i sum1 = _mm_load_si128(&reinterpret_cast<const __m128i*>(
|
||||||
|
accumulation[perspectives[p]][0])[j * 2 + 1]);
|
||||||
|
const __m128i packedbytes = _mm_packs_epi16(sum0, sum1);
|
||||||
|
|
||||||
|
_mm_store_si128(&out[j],
|
||||||
|
|
||||||
|
#ifdef USE_SSE41
|
||||||
|
_mm_max_epi8(packedbytes, kZero)
|
||||||
|
#else
|
||||||
|
_mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(USE_MMX)
|
||||||
|
auto out = reinterpret_cast<__m64*>(&output[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
__m64 sum0 = *(&reinterpret_cast<const __m64*>(
|
||||||
|
accumulation[perspectives[p]][0])[j * 2 + 0]);
|
||||||
|
__m64 sum1 = *(&reinterpret_cast<const __m64*>(
|
||||||
|
accumulation[perspectives[p]][0])[j * 2 + 1]);
|
||||||
|
const __m64 packedbytes = _mm_packs_pi16(sum0, sum1);
|
||||||
|
out[j] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(USE_NEON)
|
||||||
|
const auto out = reinterpret_cast<int8x8_t*>(&output[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
int16x8_t sum = reinterpret_cast<const int16x8_t*>(
|
||||||
|
accumulation[perspectives[p]][0])[j];
|
||||||
|
out[j] = vmax_s8(vqmovn_s16(sum), kZero);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
for (IndexType j = 0; j < kHalfDimensions; ++j) {
|
||||||
|
BiasType sum = accumulation[static_cast<int>(perspectives[p])][0][j];
|
||||||
|
output[offset + j] = static_cast<OutputType>(
|
||||||
|
std::max<int>(0, std::min<int>(127, sum)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
#if defined(USE_MMX)
|
||||||
|
_mm_empty();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Calculate cumulative value without using difference calculation
|
||||||
|
void RefreshAccumulator(const Position& pos) const {
|
||||||
|
auto& accumulator = pos.state()->accumulator;
|
||||||
|
IndexType i = 0;
|
||||||
|
Features::IndexList active_indices[2];
|
||||||
|
RawFeatures::AppendActiveIndices(pos, kRefreshTriggers[i],
|
||||||
|
active_indices);
|
||||||
|
for (Color perspective : { WHITE, BLACK }) {
|
||||||
|
std::memcpy(accumulator.accumulation[perspective][i], biases_,
|
||||||
|
kHalfDimensions * sizeof(BiasType));
|
||||||
|
for (const auto index : active_indices[perspective]) {
|
||||||
|
const IndexType offset = kHalfDimensions * index;
|
||||||
|
#if defined(USE_AVX512)
|
||||||
|
auto accumulation = reinterpret_cast<__m512i*>(
|
||||||
|
&accumulator.accumulation[perspective][i][0]);
|
||||||
|
auto column = reinterpret_cast<const __m512i*>(&weights_[offset]);
|
||||||
|
constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth;
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j)
|
||||||
|
_mm512_storeA_si512(&accumulation[j], _mm512_add_epi16(_mm512_loadA_si512(&accumulation[j]), column[j]));
|
||||||
|
|
||||||
|
#elif defined(USE_AVX2)
|
||||||
|
auto accumulation = reinterpret_cast<__m256i*>(
|
||||||
|
&accumulator.accumulation[perspective][i][0]);
|
||||||
|
auto column = reinterpret_cast<const __m256i*>(&weights_[offset]);
|
||||||
|
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j)
|
||||||
|
_mm256_storeA_si256(&accumulation[j], _mm256_add_epi16(_mm256_loadA_si256(&accumulation[j]), column[j]));
|
||||||
|
|
||||||
|
#elif defined(USE_SSE2)
|
||||||
|
auto accumulation = reinterpret_cast<__m128i*>(
|
||||||
|
&accumulator.accumulation[perspective][i][0]);
|
||||||
|
auto column = reinterpret_cast<const __m128i*>(&weights_[offset]);
|
||||||
|
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j)
|
||||||
|
accumulation[j] = _mm_add_epi16(accumulation[j], column[j]);
|
||||||
|
|
||||||
|
#elif defined(USE_MMX)
|
||||||
|
auto accumulation = reinterpret_cast<__m64*>(
|
||||||
|
&accumulator.accumulation[perspective][i][0]);
|
||||||
|
auto column = reinterpret_cast<const __m64*>(&weights_[offset]);
|
||||||
|
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
accumulation[j] = _mm_add_pi16(accumulation[j], column[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(USE_NEON)
|
||||||
|
auto accumulation = reinterpret_cast<int16x8_t*>(
|
||||||
|
&accumulator.accumulation[perspective][i][0]);
|
||||||
|
auto column = reinterpret_cast<const int16x8_t*>(&weights_[offset]);
|
||||||
|
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j)
|
||||||
|
accumulation[j] = vaddq_s16(accumulation[j], column[j]);
|
||||||
|
|
||||||
|
#else
|
||||||
|
for (IndexType j = 0; j < kHalfDimensions; ++j)
|
||||||
|
accumulator.accumulation[perspective][i][j] += weights_[offset + j];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if defined(USE_MMX)
|
||||||
|
_mm_empty();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
accumulator.computed_accumulation = true;
|
||||||
|
accumulator.computed_score = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate cumulative value using difference calculation
|
||||||
|
void UpdateAccumulator(const Position& pos) const {
|
||||||
|
const auto prev_accumulator = pos.state()->previous->accumulator;
|
||||||
|
auto& accumulator = pos.state()->accumulator;
|
||||||
|
IndexType i = 0;
|
||||||
|
Features::IndexList removed_indices[2], added_indices[2];
|
||||||
|
bool reset[2];
|
||||||
|
RawFeatures::AppendChangedIndices(pos, kRefreshTriggers[i],
|
||||||
|
removed_indices, added_indices, reset);
|
||||||
|
for (Color perspective : { WHITE, BLACK }) {
|
||||||
|
|
||||||
|
#if defined(USE_AVX2)
|
||||||
|
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||||
|
auto accumulation = reinterpret_cast<__m256i*>(
|
||||||
|
&accumulator.accumulation[perspective][i][0]);
|
||||||
|
|
||||||
|
#elif defined(USE_SSE2)
|
||||||
|
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||||
|
auto accumulation = reinterpret_cast<__m128i*>(
|
||||||
|
&accumulator.accumulation[perspective][i][0]);
|
||||||
|
|
||||||
|
#elif defined(USE_MMX)
|
||||||
|
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||||
|
auto accumulation = reinterpret_cast<__m64*>(
|
||||||
|
&accumulator.accumulation[perspective][i][0]);
|
||||||
|
|
||||||
|
#elif defined(USE_NEON)
|
||||||
|
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||||
|
auto accumulation = reinterpret_cast<int16x8_t*>(
|
||||||
|
&accumulator.accumulation[perspective][i][0]);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (reset[perspective]) {
|
||||||
|
std::memcpy(accumulator.accumulation[perspective][i], biases_,
|
||||||
|
kHalfDimensions * sizeof(BiasType));
|
||||||
|
} else {
|
||||||
|
std::memcpy(accumulator.accumulation[perspective][i],
|
||||||
|
prev_accumulator.accumulation[perspective][i],
|
||||||
|
kHalfDimensions * sizeof(BiasType));
|
||||||
|
// Difference calculation for the deactivated features
|
||||||
|
for (const auto index : removed_indices[perspective]) {
|
||||||
|
const IndexType offset = kHalfDimensions * index;
|
||||||
|
|
||||||
|
#if defined(USE_AVX2)
|
||||||
|
auto column = reinterpret_cast<const __m256i*>(&weights_[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
accumulation[j] = _mm256_sub_epi16(accumulation[j], column[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(USE_SSE2)
|
||||||
|
auto column = reinterpret_cast<const __m128i*>(&weights_[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
accumulation[j] = _mm_sub_epi16(accumulation[j], column[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(USE_MMX)
|
||||||
|
auto column = reinterpret_cast<const __m64*>(&weights_[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
accumulation[j] = _mm_sub_pi16(accumulation[j], column[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(USE_NEON)
|
||||||
|
auto column = reinterpret_cast<const int16x8_t*>(&weights_[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
accumulation[j] = vsubq_s16(accumulation[j], column[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
for (IndexType j = 0; j < kHalfDimensions; ++j) {
|
||||||
|
accumulator.accumulation[perspective][i][j] -=
|
||||||
|
weights_[offset + j];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ // Difference calculation for the activated features
|
||||||
|
for (const auto index : added_indices[perspective]) {
|
||||||
|
const IndexType offset = kHalfDimensions * index;
|
||||||
|
|
||||||
|
#if defined(USE_AVX2)
|
||||||
|
auto column = reinterpret_cast<const __m256i*>(&weights_[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
accumulation[j] = _mm256_add_epi16(accumulation[j], column[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(USE_SSE2)
|
||||||
|
auto column = reinterpret_cast<const __m128i*>(&weights_[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
accumulation[j] = _mm_add_epi16(accumulation[j], column[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(USE_MMX)
|
||||||
|
auto column = reinterpret_cast<const __m64*>(&weights_[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
accumulation[j] = _mm_add_pi16(accumulation[j], column[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(USE_NEON)
|
||||||
|
auto column = reinterpret_cast<const int16x8_t*>(&weights_[offset]);
|
||||||
|
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||||
|
accumulation[j] = vaddq_s16(accumulation[j], column[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
for (IndexType j = 0; j < kHalfDimensions; ++j) {
|
||||||
|
accumulator.accumulation[perspective][i][j] +=
|
||||||
|
weights_[offset + j];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if defined(USE_MMX)
|
||||||
|
_mm_empty();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
accumulator.computed_accumulation = true;
|
||||||
|
accumulator.computed_score = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
using BiasType = std::int16_t;
|
||||||
|
using WeightType = std::int16_t;
|
||||||
|
|
||||||
|
alignas(kCacheLineSize) BiasType biases_[kHalfDimensions];
|
||||||
|
alignas(kCacheLineSize)
|
||||||
|
WeightType weights_[kHalfDimensions * kInputDimensions];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Eval::NNUE
|
||||||
|
|
||||||
|
#endif // #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -32,16 +30,21 @@ 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, 24);
|
constexpr Score Backward = S( 8, 27);
|
||||||
constexpr Score Doubled = S(11, 56);
|
constexpr Score Doubled = S(11, 55);
|
||||||
constexpr Score Isolated = S( 5, 15);
|
constexpr Score Isolated = S( 5, 17);
|
||||||
constexpr Score WeakLever = S( 0, 56);
|
constexpr Score WeakLever = S( 2, 54);
|
||||||
constexpr Score WeakUnopposed = S(13, 27);
|
constexpr Score WeakUnopposed = S(15, 25);
|
||||||
|
|
||||||
constexpr Score BlockedStorm[RANK_NB] = {S( 0, 0), S( 0, 0), S( 76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2)};
|
// Bonus for blocked pawns at 5th or 6th rank
|
||||||
|
constexpr Score BlockedPawn[2] = { S(-13, -4), S(-4, 3) };
|
||||||
|
|
||||||
|
constexpr Score BlockedStorm[RANK_NB] = {
|
||||||
|
S(0, 0), S(0, 0), S(76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2)
|
||||||
|
};
|
||||||
|
|
||||||
// Connected pawn bonus
|
// Connected pawn bonus
|
||||||
constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 };
|
constexpr int Connected[RANK_NB] = { 0, 7, 8, 11, 24, 45, 85 };
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -143,7 +146,7 @@ namespace {
|
||||||
// Score this pawn
|
// Score this pawn
|
||||||
if (support | phalanx)
|
if (support | phalanx)
|
||||||
{
|
{
|
||||||
int v = Connected[r] * (4 + 2 * bool(phalanx) - 2 * bool(opposed) - bool(blocked)) / 2
|
int v = Connected[r] * (2 + bool(phalanx) - bool(opposed))
|
||||||
+ 21 * popcount(support);
|
+ 21 * popcount(support);
|
||||||
|
|
||||||
score += make_score(v, v * (r - 2) / 4);
|
score += make_score(v, v * (r - 2) / 4);
|
||||||
|
@ -167,6 +170,9 @@ namespace {
|
||||||
if (!support)
|
if (!support)
|
||||||
score -= Doubled * doubled
|
score -= Doubled * doubled
|
||||||
+ WeakLever * more_than_one(lever);
|
+ WeakLever * more_than_one(lever);
|
||||||
|
|
||||||
|
if (blocked && r > RANK_4)
|
||||||
|
score += BlockedPawn[r-4];
|
||||||
}
|
}
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
150
src/position.cpp
150
src/position.cpp
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -119,15 +117,7 @@ void Position::init() {
|
||||||
Zobrist::enpassant[f] = rng.rand<Key>();
|
Zobrist::enpassant[f] = rng.rand<Key>();
|
||||||
|
|
||||||
for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr)
|
for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr)
|
||||||
{
|
Zobrist::castling[cr] = rng.rand<Key>();
|
||||||
Zobrist::castling[cr] = 0;
|
|
||||||
Bitboard b = cr;
|
|
||||||
while (b)
|
|
||||||
{
|
|
||||||
Key k = Zobrist::castling[1ULL << pop_lsb(&b)];
|
|
||||||
Zobrist::castling[cr] ^= k ? k : rng.rand<Key>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Zobrist::side = rng.rand<Key>();
|
Zobrist::side = rng.rand<Key>();
|
||||||
Zobrist::noPawns = rng.rand<Key>();
|
Zobrist::noPawns = rng.rand<Key>();
|
||||||
|
@ -186,9 +176,9 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
|
||||||
|
|
||||||
4) En passant target square (in algebraic notation). If there's no en passant
|
4) En passant target square (in algebraic notation). If there's no en passant
|
||||||
target square, this is "-". If a pawn has just made a 2-square move, this
|
target square, this is "-". If a pawn has just made a 2-square move, this
|
||||||
is the position "behind" the pawn. This is recorded only if there is a pawn
|
is the position "behind" the pawn. Following X-FEN standard, this is recorded only
|
||||||
in position to make an en passant capture, and if there really is a pawn
|
if there is a pawn in position to make an en passant capture, and if there really
|
||||||
that might have advanced two squares.
|
is a pawn that might have advanced two squares.
|
||||||
|
|
||||||
5) Halfmove clock. This is the number of halfmoves since the last pawn advance
|
5) Halfmove clock. This is the number of halfmoves since the last pawn advance
|
||||||
or capture. This is used to determine if a draw can be claimed under the
|
or capture. This is used to determine if a draw can be claimed under the
|
||||||
|
@ -208,6 +198,9 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
|
||||||
std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE);
|
std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE);
|
||||||
st = si;
|
st = si;
|
||||||
|
|
||||||
|
// Each piece on board gets a unique ID used to track the piece later
|
||||||
|
PieceId piece_id, next_piece_id = PIECE_ID_ZERO;
|
||||||
|
|
||||||
ss >> std::noskipws;
|
ss >> std::noskipws;
|
||||||
|
|
||||||
// 1. Piece placement
|
// 1. Piece placement
|
||||||
|
@ -221,7 +214,19 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
|
||||||
|
|
||||||
else if ((idx = PieceToChar.find(token)) != string::npos)
|
else if ((idx = PieceToChar.find(token)) != string::npos)
|
||||||
{
|
{
|
||||||
put_piece(Piece(idx), sq);
|
auto pc = Piece(idx);
|
||||||
|
put_piece(pc, sq);
|
||||||
|
|
||||||
|
if (Eval::useNNUE)
|
||||||
|
{
|
||||||
|
// Kings get a fixed ID, other pieces get ID in order of placement
|
||||||
|
piece_id =
|
||||||
|
(idx == W_KING) ? PIECE_ID_WKING :
|
||||||
|
(idx == B_KING) ? PIECE_ID_BKING :
|
||||||
|
next_piece_id++;
|
||||||
|
evalList.put_piece(piece_id, sq, pc);
|
||||||
|
}
|
||||||
|
|
||||||
++sq;
|
++sq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,17 +264,25 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
|
||||||
set_castling_right(c, rsq);
|
set_castling_right(c, rsq);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. En passant square. Ignore if no pawn capture is possible
|
// 4. En passant square.
|
||||||
|
// Ignore if square is invalid or not on side to move relative rank 6.
|
||||||
|
bool enpassant = false;
|
||||||
|
|
||||||
if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
|
if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
|
||||||
&& ((ss >> row) && (row == '3' || row == '6')))
|
&& ((ss >> row) && (row == (sideToMove == WHITE ? '6' : '3'))))
|
||||||
{
|
{
|
||||||
st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
|
st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
|
||||||
|
|
||||||
if ( !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))
|
// En passant square will be considered only if
|
||||||
|| !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))))
|
// a) side to move have a pawn threatening epSquare
|
||||||
st->epSquare = SQ_NONE;
|
// b) there is an enemy pawn in front of epSquare
|
||||||
|
// c) there is no piece on epSquare or behind epSquare
|
||||||
|
enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN)
|
||||||
|
&& (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))
|
||||||
|
&& !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (!enpassant)
|
||||||
st->epSquare = SQ_NONE;
|
st->epSquare = SQ_NONE;
|
||||||
|
|
||||||
// 5-6. Halfmove clock and fullmove number
|
// 5-6. Halfmove clock and fullmove number
|
||||||
|
@ -705,6 +718,14 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||||
++st->rule50;
|
++st->rule50;
|
||||||
++st->pliesFromNull;
|
++st->pliesFromNull;
|
||||||
|
|
||||||
|
// Used by NNUE
|
||||||
|
st->accumulator.computed_accumulation = false;
|
||||||
|
st->accumulator.computed_score = false;
|
||||||
|
PieceId dp0 = PIECE_ID_NONE;
|
||||||
|
PieceId dp1 = PIECE_ID_NONE;
|
||||||
|
auto& dp = st->dirtyPiece;
|
||||||
|
dp.dirty_num = 1;
|
||||||
|
|
||||||
Color us = sideToMove;
|
Color us = sideToMove;
|
||||||
Color them = ~us;
|
Color them = ~us;
|
||||||
Square from = from_sq(m);
|
Square from = from_sq(m);
|
||||||
|
@ -752,6 +773,16 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||||
else
|
else
|
||||||
st->nonPawnMaterial[them] -= PieceValue[MG][captured];
|
st->nonPawnMaterial[them] -= PieceValue[MG][captured];
|
||||||
|
|
||||||
|
if (Eval::useNNUE)
|
||||||
|
{
|
||||||
|
dp.dirty_num = 2; // 2 pieces moved
|
||||||
|
dp1 = piece_id_on(capsq);
|
||||||
|
dp.pieceId[1] = dp1;
|
||||||
|
dp.old_piece[1] = evalList.piece_with_id(dp1);
|
||||||
|
evalList.put_piece(dp1, capsq, NO_PIECE);
|
||||||
|
dp.new_piece[1] = evalList.piece_with_id(dp1);
|
||||||
|
}
|
||||||
|
|
||||||
// Update board and piece lists
|
// Update board and piece lists
|
||||||
remove_piece(capsq);
|
remove_piece(capsq);
|
||||||
|
|
||||||
|
@ -780,14 +811,25 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||||
// Update castling rights if needed
|
// Update castling rights if needed
|
||||||
if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to]))
|
if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to]))
|
||||||
{
|
{
|
||||||
int cr = castlingRightsMask[from] | castlingRightsMask[to];
|
k ^= Zobrist::castling[st->castlingRights];
|
||||||
k ^= Zobrist::castling[st->castlingRights & cr];
|
st->castlingRights &= ~(castlingRightsMask[from] | castlingRightsMask[to]);
|
||||||
st->castlingRights &= ~cr;
|
k ^= Zobrist::castling[st->castlingRights];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the piece. The tricky Chess960 castling is handled earlier
|
// Move the piece. The tricky Chess960 castling is handled earlier
|
||||||
if (type_of(m) != CASTLING)
|
if (type_of(m) != CASTLING)
|
||||||
|
{
|
||||||
|
if (Eval::useNNUE)
|
||||||
|
{
|
||||||
|
dp0 = piece_id_on(from);
|
||||||
|
dp.pieceId[0] = dp0;
|
||||||
|
dp.old_piece[0] = evalList.piece_with_id(dp0);
|
||||||
|
evalList.put_piece(dp0, to, pc);
|
||||||
|
dp.new_piece[0] = evalList.piece_with_id(dp0);
|
||||||
|
}
|
||||||
|
|
||||||
move_piece(from, to);
|
move_piece(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
// If the moving piece is a pawn do some special extra work
|
// If the moving piece is a pawn do some special extra work
|
||||||
if (type_of(pc) == PAWN)
|
if (type_of(pc) == PAWN)
|
||||||
|
@ -810,6 +852,13 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||||
remove_piece(to);
|
remove_piece(to);
|
||||||
put_piece(promotion, to);
|
put_piece(promotion, to);
|
||||||
|
|
||||||
|
if (Eval::useNNUE)
|
||||||
|
{
|
||||||
|
dp0 = piece_id_on(to);
|
||||||
|
evalList.put_piece(dp0, to, promotion);
|
||||||
|
dp.new_piece[0] = evalList.piece_with_id(dp0);
|
||||||
|
}
|
||||||
|
|
||||||
// Update hash keys
|
// Update hash keys
|
||||||
k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to];
|
k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to];
|
||||||
st->pawnKey ^= Zobrist::psq[pc][to];
|
st->pawnKey ^= Zobrist::psq[pc][to];
|
||||||
|
@ -901,6 +950,12 @@ void Position::undo_move(Move m) {
|
||||||
{
|
{
|
||||||
move_piece(to, from); // Put the piece back at the source square
|
move_piece(to, from); // Put the piece back at the source square
|
||||||
|
|
||||||
|
if (Eval::useNNUE)
|
||||||
|
{
|
||||||
|
PieceId dp0 = st->dirtyPiece.pieceId[0];
|
||||||
|
evalList.put_piece(dp0, from, pc);
|
||||||
|
}
|
||||||
|
|
||||||
if (st->capturedPiece)
|
if (st->capturedPiece)
|
||||||
{
|
{
|
||||||
Square capsq = to;
|
Square capsq = to;
|
||||||
|
@ -917,6 +972,14 @@ void Position::undo_move(Move m) {
|
||||||
}
|
}
|
||||||
|
|
||||||
put_piece(st->capturedPiece, capsq); // Restore the captured piece
|
put_piece(st->capturedPiece, capsq); // Restore the captured piece
|
||||||
|
|
||||||
|
if (Eval::useNNUE)
|
||||||
|
{
|
||||||
|
PieceId dp1 = st->dirtyPiece.pieceId[1];
|
||||||
|
assert(evalList.piece_with_id(dp1).from[WHITE] == PS_NONE);
|
||||||
|
assert(evalList.piece_with_id(dp1).from[BLACK] == PS_NONE);
|
||||||
|
evalList.put_piece(dp1, capsq, st->capturedPiece);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -938,6 +1001,34 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ
|
||||||
rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
|
rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
|
||||||
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
|
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
|
||||||
|
|
||||||
|
if (Eval::useNNUE)
|
||||||
|
{
|
||||||
|
PieceId dp0, dp1;
|
||||||
|
auto& dp = st->dirtyPiece;
|
||||||
|
dp.dirty_num = 2; // 2 pieces moved
|
||||||
|
|
||||||
|
if (Do)
|
||||||
|
{
|
||||||
|
dp0 = piece_id_on(from);
|
||||||
|
dp1 = piece_id_on(rfrom);
|
||||||
|
dp.pieceId[0] = dp0;
|
||||||
|
dp.old_piece[0] = evalList.piece_with_id(dp0);
|
||||||
|
evalList.put_piece(dp0, to, make_piece(us, KING));
|
||||||
|
dp.new_piece[0] = evalList.piece_with_id(dp0);
|
||||||
|
dp.pieceId[1] = dp1;
|
||||||
|
dp.old_piece[1] = evalList.piece_with_id(dp1);
|
||||||
|
evalList.put_piece(dp1, rto, make_piece(us, ROOK));
|
||||||
|
dp.new_piece[1] = evalList.piece_with_id(dp1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dp0 = piece_id_on(to);
|
||||||
|
dp1 = piece_id_on(rto);
|
||||||
|
evalList.put_piece(dp0, from, make_piece(us, KING));
|
||||||
|
evalList.put_piece(dp1, rfrom, make_piece(us, ROOK));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Remove both pieces first since squares could overlap in Chess960
|
// Remove both pieces first since squares could overlap in Chess960
|
||||||
remove_piece(Do ? from : to);
|
remove_piece(Do ? from : to);
|
||||||
remove_piece(Do ? rfrom : rto);
|
remove_piece(Do ? rfrom : rto);
|
||||||
|
@ -955,7 +1046,14 @@ void Position::do_null_move(StateInfo& newSt) {
|
||||||
assert(!checkers());
|
assert(!checkers());
|
||||||
assert(&newSt != st);
|
assert(&newSt != st);
|
||||||
|
|
||||||
std::memcpy(&newSt, st, sizeof(StateInfo));
|
if (Eval::useNNUE)
|
||||||
|
{
|
||||||
|
std::memcpy(&newSt, st, sizeof(StateInfo));
|
||||||
|
st->accumulator.computed_score = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
std::memcpy(&newSt, st, offsetof(StateInfo, accumulator));
|
||||||
|
|
||||||
newSt.previous = st;
|
newSt.previous = st;
|
||||||
st = &newSt;
|
st = &newSt;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -27,8 +25,11 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "bitboard.h"
|
#include "bitboard.h"
|
||||||
|
#include "evaluate.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
|
#include "nnue/nnue_accumulator.h"
|
||||||
|
|
||||||
|
|
||||||
/// StateInfo struct stores information needed to restore a Position object to
|
/// StateInfo struct stores information needed to restore a Position object to
|
||||||
/// its previous state when we retract a move. Whenever a move is made on the
|
/// its previous state when we retract a move. Whenever a move is made on the
|
||||||
|
@ -54,6 +55,10 @@ struct StateInfo {
|
||||||
Bitboard pinners[COLOR_NB];
|
Bitboard pinners[COLOR_NB];
|
||||||
Bitboard checkSquares[PIECE_TYPE_NB];
|
Bitboard checkSquares[PIECE_TYPE_NB];
|
||||||
int repetition;
|
int repetition;
|
||||||
|
|
||||||
|
// Used by NNUE
|
||||||
|
Eval::NNUE::Accumulator accumulator;
|
||||||
|
DirtyPiece dirtyPiece;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -163,6 +168,10 @@ public:
|
||||||
bool pos_is_ok() const;
|
bool pos_is_ok() const;
|
||||||
void flip();
|
void flip();
|
||||||
|
|
||||||
|
// Used by NNUE
|
||||||
|
StateInfo* state() const;
|
||||||
|
const EvalList* eval_list() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Initialization helpers (used while setting up a position)
|
// Initialization helpers (used while setting up a position)
|
||||||
void set_castling_right(Color c, Square rfrom);
|
void set_castling_right(Color c, Square rfrom);
|
||||||
|
@ -176,6 +185,9 @@ private:
|
||||||
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);
|
||||||
|
|
||||||
|
// ID of a piece on a given square
|
||||||
|
PieceId piece_id_on(Square sq) const;
|
||||||
|
|
||||||
// Data members
|
// Data members
|
||||||
Piece board[SQUARE_NB];
|
Piece board[SQUARE_NB];
|
||||||
Bitboard byTypeBB[PIECE_TYPE_NB];
|
Bitboard byTypeBB[PIECE_TYPE_NB];
|
||||||
|
@ -192,6 +204,9 @@ private:
|
||||||
Thread* thisThread;
|
Thread* thisThread;
|
||||||
StateInfo* st;
|
StateInfo* st;
|
||||||
bool chess960;
|
bool chess960;
|
||||||
|
|
||||||
|
// List of pieces used in NNUE evaluation function
|
||||||
|
EvalList evalList;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace PSQT {
|
namespace PSQT {
|
||||||
|
@ -426,4 +441,25 @@ inline void Position::do_move(Move m, StateInfo& newSt) {
|
||||||
do_move(m, newSt, gives_check(m));
|
do_move(m, newSt, gives_check(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline StateInfo* Position::state() const {
|
||||||
|
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const EvalList* Position::eval_list() const {
|
||||||
|
|
||||||
|
return &evalList;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline PieceId Position::piece_id_on(Square sq) const
|
||||||
|
{
|
||||||
|
|
||||||
|
assert(piece_on(sq) != NO_PIECE);
|
||||||
|
|
||||||
|
PieceId pid = evalList.piece_id_list[sq];
|
||||||
|
assert(is_ok(pid));
|
||||||
|
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // #ifndef POSITION_H_INCLUDED
|
#endif // #ifndef POSITION_H_INCLUDED
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -92,7 +90,7 @@ constexpr Score PBonus[RANK_NB][FILE_NB] =
|
||||||
{ S( 3,-10), S( 3, -6), S( 10, 10), S( 19, 0), S( 16, 14), S( 19, 7), S( 7, -5), S( -5,-19) },
|
{ S( 3,-10), S( 3, -6), S( 10, 10), S( 19, 0), S( 16, 14), S( 19, 7), S( 7, -5), S( -5,-19) },
|
||||||
{ S( -9,-10), S(-15,-10), S( 11,-10), S( 15, 4), S( 32, 4), S( 22, 3), S( 5, -6), S(-22, -4) },
|
{ S( -9,-10), S(-15,-10), S( 11,-10), S( 15, 4), S( 32, 4), S( 22, 3), S( 5, -6), S(-22, -4) },
|
||||||
{ S( -4, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S( -8, -9) },
|
{ S( -4, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S( -8, -9) },
|
||||||
{ S( 13, 9), S( 0, 4), S(-13, 3), S( 1,-12), S( 11,-12), S( -2, -6), S(-13, 13), S( 5, 8) },
|
{ S( 13, 10), S( 0, 5), S(-13, 4), S( 1, -5), S( 11, -5), S( -2, -5), S(-13, 14), S( 5, 9) },
|
||||||
{ S( 5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S( -8, 13) },
|
{ S( 5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S( -8, 13) },
|
||||||
{ S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) }
|
{ S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) }
|
||||||
};
|
};
|
||||||
|
|
189
src/search.cpp
189
src/search.cpp
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -66,9 +64,9 @@ namespace {
|
||||||
constexpr uint64_t TtHitAverageResolution = 1024;
|
constexpr uint64_t TtHitAverageResolution = 1024;
|
||||||
|
|
||||||
// Razor and futility margins
|
// Razor and futility margins
|
||||||
constexpr int RazorMargin = 527;
|
constexpr int RazorMargin = 510;
|
||||||
Value futility_margin(Depth d, bool improving) {
|
Value futility_margin(Depth d, bool improving) {
|
||||||
return Value(227 * (d - improving));
|
return Value(223 * (d - improving));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reductions lookup table, initialized at startup
|
// Reductions lookup table, initialized at startup
|
||||||
|
@ -76,7 +74,7 @@ namespace {
|
||||||
|
|
||||||
Depth reduction(bool i, Depth d, int mn) {
|
Depth reduction(bool i, Depth d, int mn) {
|
||||||
int r = Reductions[d] * Reductions[mn];
|
int r = Reductions[d] * Reductions[mn];
|
||||||
return (r + 570) / 1024 + (!i && r > 1018);
|
return (r + 509) / 1024 + (!i && r > 894);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int futility_move_count(bool improving, Depth depth) {
|
constexpr int futility_move_count(bool improving, Depth depth) {
|
||||||
|
@ -85,7 +83,7 @@ namespace {
|
||||||
|
|
||||||
// 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 d > 15 ? 27 : 17 * d * d + 133 * d - 134;
|
return d > 13 ? 29 : 17 * d * d + 134 * d - 134;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a small random component to draw evaluations to avoid 3fold-blindness
|
// Add a small random component to draw evaluations to avoid 3fold-blindness
|
||||||
|
@ -195,7 +193,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((24.8 + std::log(Threads.size())) * std::log(i));
|
Reductions[i] = int((22.0 + std::log(Threads.size())) * std::log(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -230,6 +228,8 @@ void MainThread::search() {
|
||||||
Time.init(Limits, us, rootPos.game_ply());
|
Time.init(Limits, us, rootPos.game_ply());
|
||||||
TT.new_search();
|
TT.new_search();
|
||||||
|
|
||||||
|
Eval::verify_NNUE();
|
||||||
|
|
||||||
if (rootMoves.empty())
|
if (rootMoves.empty())
|
||||||
{
|
{
|
||||||
rootMoves.emplace_back(MOVE_NONE);
|
rootMoves.emplace_back(MOVE_NONE);
|
||||||
|
@ -270,10 +270,10 @@ void MainThread::search() {
|
||||||
|
|
||||||
Thread* bestThread = this;
|
Thread* bestThread = this;
|
||||||
|
|
||||||
if (int(Options["MultiPV"]) == 1 &&
|
if ( int(Options["MultiPV"]) == 1
|
||||||
!Limits.depth &&
|
&& !Limits.depth
|
||||||
!(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) &&
|
&& !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"]))
|
||||||
rootMoves[0].pv[0] != MOVE_NONE)
|
&& rootMoves[0].pv[0] != MOVE_NONE)
|
||||||
bestThread = Threads.get_best_thread();
|
bestThread = Threads.get_best_thread();
|
||||||
|
|
||||||
// Prepare PVLine and ponder move
|
// Prepare PVLine and ponder move
|
||||||
|
@ -433,12 +433,12 @@ void Thread::search() {
|
||||||
if (rootDepth >= 4)
|
if (rootDepth >= 4)
|
||||||
{
|
{
|
||||||
Value prev = rootMoves[pvIdx].previousScore;
|
Value prev = rootMoves[pvIdx].previousScore;
|
||||||
delta = Value(19);
|
delta = Value(17);
|
||||||
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 contempt based on root move's previousScore (dynamic contempt)
|
// Adjust contempt based on root move's previousScore (dynamic contempt)
|
||||||
int dct = ct + (110 - ct / 2) * prev / (abs(prev) + 140);
|
int dct = ct + (105 - ct / 2) * prev / (abs(prev) + 149);
|
||||||
|
|
||||||
contempt = (us == WHITE ? make_score(dct, dct / 2)
|
contempt = (us == WHITE ? make_score(dct, dct / 2)
|
||||||
: -make_score(dct, dct / 2));
|
: -make_score(dct, dct / 2));
|
||||||
|
@ -543,13 +543,13 @@ void Thread::search() {
|
||||||
&& !Threads.stop
|
&& !Threads.stop
|
||||||
&& !mainThread->stopOnPonderhit)
|
&& !mainThread->stopOnPonderhit)
|
||||||
{
|
{
|
||||||
double fallingEval = (296 + 6 * (mainThread->bestPreviousScore - bestValue)
|
double fallingEval = (318 + 6 * (mainThread->bestPreviousScore - bestValue)
|
||||||
+ 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 725.0;
|
+ 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 825.0;
|
||||||
fallingEval = Utility::clamp(fallingEval, 0.5, 1.5);
|
fallingEval = Utility::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.92 : 0.95;
|
timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.92 : 0.95;
|
||||||
double reduction = (1.47 + mainThread->previousTimeReduction) / (2.22 * timeReduction);
|
double reduction = (1.47 + mainThread->previousTimeReduction) / (2.32 * timeReduction);
|
||||||
|
|
||||||
// Use part of the gained time from a previous stable move for the current move
|
// Use part of the gained time from a previous stable move for the current move
|
||||||
for (Thread* th : Threads)
|
for (Thread* th : Threads)
|
||||||
|
@ -574,7 +574,7 @@ void Thread::search() {
|
||||||
}
|
}
|
||||||
else if ( Threads.increaseDepth
|
else if ( Threads.increaseDepth
|
||||||
&& !mainThread->ponder
|
&& !mainThread->ponder
|
||||||
&& Time.elapsed() > totalTime * 0.56)
|
&& Time.elapsed() > totalTime * 0.58)
|
||||||
Threads.increaseDepth = false;
|
Threads.increaseDepth = false;
|
||||||
else
|
else
|
||||||
Threads.increaseDepth = true;
|
Threads.increaseDepth = true;
|
||||||
|
@ -633,7 +633,7 @@ namespace {
|
||||||
Key posKey;
|
Key posKey;
|
||||||
Move ttMove, move, excludedMove, bestMove;
|
Move ttMove, move, excludedMove, bestMove;
|
||||||
Depth extension, newDepth;
|
Depth extension, newDepth;
|
||||||
Value bestValue, value, ttValue, eval, maxValue;
|
Value bestValue, value, ttValue, eval, maxValue, probCutBeta;
|
||||||
bool ttHit, ttPv, formerPv, givesCheck, improving, didLMR, priorCapture;
|
bool ttHit, ttPv, formerPv, givesCheck, improving, didLMR, priorCapture;
|
||||||
bool captureOrPromotion, doFullDepthSearch, moveCountPruning,
|
bool captureOrPromotion, doFullDepthSearch, moveCountPruning,
|
||||||
ttCapture, singularQuietLMR;
|
ttCapture, singularQuietLMR;
|
||||||
|
@ -699,7 +699,7 @@ namespace {
|
||||||
// search to overwrite a previous full search TT value, so we use a different
|
// search to overwrite a previous full search TT value, so we use a different
|
||||||
// position key in case of an excluded move.
|
// position key in case of an excluded move.
|
||||||
excludedMove = ss->excludedMove;
|
excludedMove = ss->excludedMove;
|
||||||
posKey = pos.key() ^ (Key(excludedMove) << 48); // Isn't a very good hash
|
posKey = excludedMove == MOVE_NONE ? pos.key() : pos.key() ^ make_key(excludedMove);
|
||||||
tte = TT.probe(posKey, ttHit);
|
tte = TT.probe(posKey, ttHit);
|
||||||
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
|
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
|
||||||
ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
|
ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
|
||||||
|
@ -707,7 +707,11 @@ namespace {
|
||||||
ttPv = PvNode || (ttHit && tte->is_pv());
|
ttPv = PvNode || (ttHit && tte->is_pv());
|
||||||
formerPv = ttPv && !PvNode;
|
formerPv = ttPv && !PvNode;
|
||||||
|
|
||||||
if (ttPv && depth > 12 && ss->ply - 1 < MAX_LPH && !priorCapture && is_ok((ss-1)->currentMove))
|
if ( ttPv
|
||||||
|
&& depth > 12
|
||||||
|
&& ss->ply - 1 < MAX_LPH
|
||||||
|
&& !priorCapture
|
||||||
|
&& is_ok((ss-1)->currentMove))
|
||||||
thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5);
|
thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5);
|
||||||
|
|
||||||
// thisThread->ttHitAverage can be used to approximate the running average of ttHit
|
// thisThread->ttHitAverage can be used to approximate the running average of ttHit
|
||||||
|
@ -828,11 +832,7 @@ namespace {
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ((ss-1)->currentMove != MOVE_NULL)
|
if ((ss-1)->currentMove != MOVE_NULL)
|
||||||
{
|
ss->staticEval = eval = evaluate(pos);
|
||||||
int bonus = -(ss-1)->statScore / 512;
|
|
||||||
|
|
||||||
ss->staticEval = eval = evaluate(pos) + bonus;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
ss->staticEval = eval = -(ss-1)->staticEval + 2 * Tempo;
|
ss->staticEval = eval = -(ss-1)->staticEval + 2 * Tempo;
|
||||||
|
|
||||||
|
@ -852,7 +852,7 @@ namespace {
|
||||||
|
|
||||||
// Step 8. Futility pruning: child node (~50 Elo)
|
// Step 8. Futility pruning: child node (~50 Elo)
|
||||||
if ( !PvNode
|
if ( !PvNode
|
||||||
&& depth < 6
|
&& depth < 8
|
||||||
&& eval - futility_margin(depth, improving) >= beta
|
&& eval - futility_margin(depth, improving) >= beta
|
||||||
&& eval < VALUE_KNOWN_WIN) // Do not return unproven wins
|
&& eval < VALUE_KNOWN_WIN) // Do not return unproven wins
|
||||||
return eval;
|
return eval;
|
||||||
|
@ -860,10 +860,10 @@ namespace {
|
||||||
// Step 9. Null move search with verification search (~40 Elo)
|
// Step 9. Null move search with verification search (~40 Elo)
|
||||||
if ( !PvNode
|
if ( !PvNode
|
||||||
&& (ss-1)->currentMove != MOVE_NULL
|
&& (ss-1)->currentMove != MOVE_NULL
|
||||||
&& (ss-1)->statScore < 23824
|
&& (ss-1)->statScore < 22977
|
||||||
&& eval >= beta
|
&& eval >= beta
|
||||||
&& eval >= ss->staticEval
|
&& eval >= ss->staticEval
|
||||||
&& ss->staticEval >= beta - 33 * depth - 33 * improving + 112 * ttPv + 311
|
&& ss->staticEval >= beta - 30 * depth - 28 * improving + 84 * ttPv + 182
|
||||||
&& !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))
|
||||||
|
@ -871,7 +871,7 @@ namespace {
|
||||||
assert(eval - beta >= 0);
|
assert(eval - beta >= 0);
|
||||||
|
|
||||||
// Null move dynamic reduction based on depth and value
|
// Null move dynamic reduction based on depth and value
|
||||||
Depth R = (737 + 77 * depth) / 246 + std::min(int(eval - beta) / 192, 3);
|
Depth R = (817 + 71 * depth) / 213 + std::min(int(eval - beta) / 192, 3);
|
||||||
|
|
||||||
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];
|
||||||
|
@ -907,23 +907,39 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
probCutBeta = beta + 176 - 49 * improving;
|
||||||
|
|
||||||
// Step 10. ProbCut (~10 Elo)
|
// Step 10. ProbCut (~10 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
|
||||||
// much above beta, we can (almost) safely prune the previous move.
|
// much above beta, we can (almost) safely prune the previous move.
|
||||||
if ( !PvNode
|
if ( !PvNode
|
||||||
&& depth > 4
|
&& depth > 4
|
||||||
&& abs(beta) < VALUE_TB_WIN_IN_MAX_PLY)
|
&& abs(beta) < VALUE_TB_WIN_IN_MAX_PLY
|
||||||
|
// if value from transposition table is lower than probCutBeta, don't attempt probCut
|
||||||
|
// there and in further interactions with transposition table cutoff depth is set to depth - 3
|
||||||
|
// because probCut search has depth set to depth - 4 but we also do a move before it
|
||||||
|
// so effective depth is equal to depth - 3
|
||||||
|
&& !( ttHit
|
||||||
|
&& tte->depth() >= depth - 3
|
||||||
|
&& ttValue != VALUE_NONE
|
||||||
|
&& ttValue < probCutBeta))
|
||||||
{
|
{
|
||||||
Value raisedBeta = beta + 176 - 49 * improving;
|
// if ttMove is a capture and value from transposition table is good enough produce probCut
|
||||||
assert(raisedBeta < VALUE_INFINITE);
|
// cutoff without digging into actual probCut search
|
||||||
MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &captureHistory);
|
if ( ttHit
|
||||||
|
&& tte->depth() >= depth - 3
|
||||||
|
&& ttValue != VALUE_NONE
|
||||||
|
&& ttValue >= probCutBeta
|
||||||
|
&& ttMove
|
||||||
|
&& pos.capture_or_promotion(ttMove))
|
||||||
|
return probCutBeta;
|
||||||
|
|
||||||
|
assert(probCutBeta < VALUE_INFINITE);
|
||||||
|
MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory);
|
||||||
int probCutCount = 0;
|
int probCutCount = 0;
|
||||||
|
|
||||||
while ( (move = mp.next_move()) != MOVE_NONE
|
while ( (move = mp.next_move()) != MOVE_NONE
|
||||||
&& probCutCount < 2 + 2 * cutNode
|
&& probCutCount < 2 + 2 * cutNode)
|
||||||
&& !( move == ttMove
|
|
||||||
&& tte->depth() >= depth - 4
|
|
||||||
&& ttValue < raisedBeta))
|
|
||||||
if (move != excludedMove && pos.legal(move))
|
if (move != excludedMove && pos.legal(move))
|
||||||
{
|
{
|
||||||
assert(pos.capture_or_promotion(move));
|
assert(pos.capture_or_promotion(move));
|
||||||
|
@ -941,16 +957,25 @@ namespace {
|
||||||
pos.do_move(move, st);
|
pos.do_move(move, st);
|
||||||
|
|
||||||
// Perform a preliminary qsearch to verify that the move holds
|
// Perform a preliminary qsearch to verify that the move holds
|
||||||
value = -qsearch<NonPV>(pos, ss+1, -raisedBeta, -raisedBeta+1);
|
value = -qsearch<NonPV>(pos, ss+1, -probCutBeta, -probCutBeta+1);
|
||||||
|
|
||||||
// If the qsearch held, perform the regular search
|
// If the qsearch held, perform the regular search
|
||||||
if (value >= raisedBeta)
|
if (value >= probCutBeta)
|
||||||
value = -search<NonPV>(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4, !cutNode);
|
value = -search<NonPV>(pos, ss+1, -probCutBeta, -probCutBeta+1, depth - 4, !cutNode);
|
||||||
|
|
||||||
pos.undo_move(move);
|
pos.undo_move(move);
|
||||||
|
|
||||||
if (value >= raisedBeta)
|
if (value >= probCutBeta)
|
||||||
|
{
|
||||||
|
// if transposition table doesn't have equal or more deep info write probCut data into it
|
||||||
|
if ( !(ttHit
|
||||||
|
&& tte->depth() >= depth - 3
|
||||||
|
&& ttValue != VALUE_NONE))
|
||||||
|
tte->save(posKey, value_to_tt(value, ss->ply), ttPv,
|
||||||
|
BOUND_LOWER,
|
||||||
|
depth - 3, move, ss->staticEval);
|
||||||
return value;
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1004,6 +1029,10 @@ moves_loop: // When in check, search starts from here
|
||||||
thisThread->rootMoves.begin() + thisThread->pvLast, move))
|
thisThread->rootMoves.begin() + thisThread->pvLast, move))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// Check for legality
|
||||||
|
if (!rootNode && !pos.legal(move))
|
||||||
|
continue;
|
||||||
|
|
||||||
ss->moveCount = ++moveCount;
|
ss->moveCount = ++moveCount;
|
||||||
|
|
||||||
if (rootNode && Cluster::is_root() && thisThread == Threads.main() && Time.elapsed() > 3000)
|
if (rootNode && Cluster::is_root() && thisThread == Threads.main() && Time.elapsed() > 3000)
|
||||||
|
@ -1042,17 +1071,17 @@ moves_loop: // When in check, search starts from here
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Futility pruning: parent node (~5 Elo)
|
// Futility pruning: parent node (~5 Elo)
|
||||||
if ( lmrDepth < 6
|
if ( lmrDepth < 7
|
||||||
&& !ss->inCheck
|
&& !ss->inCheck
|
||||||
&& ss->staticEval + 284 + 188 * lmrDepth <= alpha
|
&& ss->staticEval + 283 + 170 * lmrDepth <= alpha
|
||||||
&& (*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)]
|
||||||
+ (*contHist[5])[movedPiece][to_sq(move)] / 2 < 28388)
|
+ (*contHist[5])[movedPiece][to_sq(move)] / 2 < 27376)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Prune moves with negative SEE (~20 Elo)
|
// Prune moves with negative SEE (~20 Elo)
|
||||||
if (!pos.see_ge(move, Value(-(29 - std::min(lmrDepth, 17)) * lmrDepth * lmrDepth)))
|
if (!pos.see_ge(move, Value(-(29 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth)))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1069,12 +1098,12 @@ moves_loop: // When in check, search starts from here
|
||||||
&& !(PvNode && abs(bestValue) < 2)
|
&& !(PvNode && abs(bestValue) < 2)
|
||||||
&& PieceValue[MG][type_of(movedPiece)] >= PieceValue[MG][type_of(pos.piece_on(to_sq(move)))]
|
&& PieceValue[MG][type_of(movedPiece)] >= PieceValue[MG][type_of(pos.piece_on(to_sq(move)))]
|
||||||
&& !ss->inCheck
|
&& !ss->inCheck
|
||||||
&& ss->staticEval + 267 + 391 * lmrDepth
|
&& ss->staticEval + 169 + 244 * lmrDepth
|
||||||
+ PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha)
|
+ PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// See based pruning
|
// See based pruning
|
||||||
if (!pos.see_ge(move, Value(-202) * depth)) // (~25 Elo)
|
if (!pos.see_ge(move, Value(-221) * depth)) // (~25 Elo)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1085,16 +1114,15 @@ moves_loop: // When in check, search starts from here
|
||||||
// search of (alpha-s, beta-s), and just one fails high on (alpha, beta),
|
// search of (alpha-s, beta-s), and just one fails high on (alpha, beta),
|
||||||
// then that move is singular and should be extended. To verify this we do
|
// then that move is singular and should be extended. To verify this we do
|
||||||
// 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 ( depth >= 6
|
if ( depth >= 7
|
||||||
&& move == ttMove
|
&& move == ttMove
|
||||||
&& !rootNode
|
&& !rootNode
|
||||||
&& !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 */
|
||||||
&& abs(ttValue) < VALUE_KNOWN_WIN
|
&& abs(ttValue) < VALUE_KNOWN_WIN
|
||||||
&& (tte->bound() & BOUND_LOWER)
|
&& (tte->bound() & BOUND_LOWER)
|
||||||
&& tte->depth() >= depth - 3
|
&& tte->depth() >= depth - 3)
|
||||||
&& pos.legal(move))
|
|
||||||
{
|
{
|
||||||
Value singularBeta = ttValue - ((formerPv + 4) * depth) / 2;
|
Value singularBeta = ttValue - ((formerPv + 4) * depth) / 2;
|
||||||
Depth singularDepth = (depth - 1 + 3 * formerPv) / 2;
|
Depth singularDepth = (depth - 1 + 3 * formerPv) / 2;
|
||||||
|
@ -1134,19 +1162,9 @@ moves_loop: // When in check, search starts from here
|
||||||
&& (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move)))
|
&& (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move)))
|
||||||
extension = 1;
|
extension = 1;
|
||||||
|
|
||||||
// Passed pawn extension
|
|
||||||
else if ( move == ss->killers[0]
|
|
||||||
&& pos.advanced_pawn_push(move)
|
|
||||||
&& pos.pawn_passed(us, to_sq(move)))
|
|
||||||
extension = 1;
|
|
||||||
|
|
||||||
// Last captures extension
|
|
||||||
else if ( PieceValue[EG][pos.captured_piece()] > PawnValueEg
|
|
||||||
&& pos.non_pawn_material() <= 2 * RookValueMg)
|
|
||||||
extension = 1;
|
|
||||||
|
|
||||||
// Castling extension
|
// Castling extension
|
||||||
if (type_of(move) == CASTLING)
|
if ( type_of(move) == CASTLING
|
||||||
|
&& popcount(pos.pieces(us) & ~pos.pieces(PAWN) & (to_sq(move) & KingSide ? KingSide : QueenSide)) <= 2)
|
||||||
extension = 1;
|
extension = 1;
|
||||||
|
|
||||||
// Late irreversible move extension
|
// Late irreversible move extension
|
||||||
|
@ -1161,13 +1179,6 @@ moves_loop: // When in check, search starts from here
|
||||||
// Speculative prefetch as early as possible
|
// Speculative prefetch as early as possible
|
||||||
prefetch(TT.first_entry(pos.key_after(move)));
|
prefetch(TT.first_entry(pos.key_after(move)));
|
||||||
|
|
||||||
// Check for legality just before making the move
|
|
||||||
if (!rootNode && !pos.legal(move))
|
|
||||||
{
|
|
||||||
ss->moveCount = --moveCount;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the current move (this must be done after singular extension search)
|
// Update the current move (this must be done after singular extension search)
|
||||||
ss->currentMove = move;
|
ss->currentMove = move;
|
||||||
ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck]
|
ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck]
|
||||||
|
@ -1181,18 +1192,25 @@ moves_loop: // When in check, search starts from here
|
||||||
// Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be
|
// Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be
|
||||||
// re-searched at full depth.
|
// re-searched at full depth.
|
||||||
if ( depth >= 3
|
if ( depth >= 3
|
||||||
&& moveCount > 1 + 2 * rootNode
|
&& moveCount > 1 + 2 * rootNode + 2 * (PvNode && abs(bestValue) < 2)
|
||||||
&& (!rootNode || thisThread->best_move_count(move) == 0)
|
&& (!rootNode || thisThread->best_move_count(move) == 0)
|
||||||
&& ( !captureOrPromotion
|
&& ( !captureOrPromotion
|
||||||
|| moveCountPruning
|
|| moveCountPruning
|
||||||
|| ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha
|
|| ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha
|
||||||
|| cutNode
|
|| cutNode
|
||||||
|| thisThread->ttHitAverage < 415 * TtHitAverageResolution * TtHitAverageWindow / 1024))
|
|| thisThread->ttHitAverage < 427 * TtHitAverageResolution * TtHitAverageWindow / 1024))
|
||||||
{
|
{
|
||||||
Depth r = reduction(improving, depth, moveCount);
|
Depth r = reduction(improving, depth, moveCount);
|
||||||
|
|
||||||
|
// Decrease reduction at non-check cut nodes for second move at low depths
|
||||||
|
if ( cutNode
|
||||||
|
&& depth <= 10
|
||||||
|
&& moveCount <= 2
|
||||||
|
&& !ss->inCheck)
|
||||||
|
r--;
|
||||||
|
|
||||||
// Decrease reduction if the ttHit running average is large
|
// Decrease reduction if the ttHit running average is large
|
||||||
if (thisThread->ttHitAverage > 473 * TtHitAverageResolution * TtHitAverageWindow / 1024)
|
if (thisThread->ttHitAverage > 509 * TtHitAverageResolution * TtHitAverageWindow / 1024)
|
||||||
r--;
|
r--;
|
||||||
|
|
||||||
// Reduction if other threads are searching this position
|
// Reduction if other threads are searching this position
|
||||||
|
@ -1235,17 +1253,17 @@ moves_loop: // When in check, search starts from here
|
||||||
+ (*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)]
|
||||||
- 4826;
|
- 5287;
|
||||||
|
|
||||||
// Decrease/increase reduction by comparing opponent's stat score (~10 Elo)
|
// Decrease/increase reduction by comparing opponent's stat score (~10 Elo)
|
||||||
if (ss->statScore >= -100 && (ss-1)->statScore < -112)
|
if (ss->statScore >= -106 && (ss-1)->statScore < -104)
|
||||||
r--;
|
r--;
|
||||||
|
|
||||||
else if ((ss-1)->statScore >= -125 && ss->statScore < -138)
|
else if ((ss-1)->statScore >= -119 && ss->statScore < -140)
|
||||||
r++;
|
r++;
|
||||||
|
|
||||||
// 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 / 14615;
|
r -= ss->statScore / 14884;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1255,7 +1273,7 @@ moves_loop: // When in check, search starts from here
|
||||||
|
|
||||||
// Unless giving check, this capture is likely bad
|
// Unless giving check, this capture is likely bad
|
||||||
if ( !givesCheck
|
if ( !givesCheck
|
||||||
&& ss->staticEval + PieceValue[EG][pos.captured_piece()] + 211 * depth <= alpha)
|
&& ss->staticEval + PieceValue[EG][pos.captured_piece()] + 213 * depth <= alpha)
|
||||||
r++;
|
r++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1519,7 +1537,7 @@ moves_loop: // When in check, search starts from here
|
||||||
if (PvNode && bestValue > alpha)
|
if (PvNode && bestValue > alpha)
|
||||||
alpha = bestValue;
|
alpha = bestValue;
|
||||||
|
|
||||||
futilityBase = bestValue + 141;
|
futilityBase = bestValue + 145;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory,
|
const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory,
|
||||||
|
@ -1528,8 +1546,8 @@ moves_loop: // When in check, search starts from here
|
||||||
|
|
||||||
// Initialize a MovePicker object for the current position, and prepare
|
// Initialize a MovePicker object for the current position, and prepare
|
||||||
// to search the moves. Because the depth is <= 0 here, only captures,
|
// to search the moves. Because the depth is <= 0 here, only captures,
|
||||||
// queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will
|
// queen and checking knight promotions, and other checks(only if depth >= DEPTH_QS_CHECKS)
|
||||||
// be generated.
|
// will be generated.
|
||||||
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory,
|
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory,
|
||||||
&thisThread->captureHistory,
|
&thisThread->captureHistory,
|
||||||
contHist,
|
contHist,
|
||||||
|
@ -1771,7 +1789,7 @@ moves_loop: // When in check, search starts from here
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depth > 11 && ss->ply < MAX_LPH)
|
if (depth > 11 && ss->ply < MAX_LPH)
|
||||||
thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 6);
|
thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When playing with strength handicap, choose best move among a set of RootMoves
|
// When playing with strength handicap, choose best move among a set of RootMoves
|
||||||
|
@ -1881,6 +1899,9 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) {
|
||||||
<< " multipv " << i + 1
|
<< " multipv " << i + 1
|
||||||
<< " score " << UCI::value(v);
|
<< " score " << UCI::value(v);
|
||||||
|
|
||||||
|
if (Options["UCI_ShowWDL"])
|
||||||
|
ss << UCI::wdl(v, pos.game_ply());
|
||||||
|
|
||||||
if (!tb && i == pvIdx)
|
if (!tb && i == pvIdx)
|
||||||
ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
|
ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : "");
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (c) 2013 Ronald de Man
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (c) 2013 Ronald de Man
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -209,21 +207,18 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
|
||||||
|
|
||||||
// We use Position::set() to set root position across threads. But there are
|
// We use Position::set() to set root position across threads. But there are
|
||||||
// some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot
|
// some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot
|
||||||
// be deduced from a fen string, so set() clears them and to not lose the info
|
// be deduced from a fen string, so set() clears them and they are set from
|
||||||
// we need to backup and later restore setupStates->back(). Note that setupStates
|
// setupStates->back() later. The rootState is per thread, earlier states are shared
|
||||||
// is shared by threads but is accessed in read-only mode.
|
// since they are read-only.
|
||||||
StateInfo tmp = setupStates->back();
|
|
||||||
|
|
||||||
for (Thread* th : *this)
|
for (Thread* th : *this)
|
||||||
{
|
{
|
||||||
th->nodes = th->tbHits = th->TTsaves = th->nmpMinPly = th->bestMoveChanges = 0;
|
th->nodes = th->tbHits = th->TTsaves = th->nmpMinPly = th->bestMoveChanges = 0;
|
||||||
th->rootDepth = th->completedDepth = 0;
|
th->rootDepth = th->completedDepth = 0;
|
||||||
th->rootMoves = rootMoves;
|
th->rootMoves = rootMoves;
|
||||||
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
|
th->rootPos.set(pos.fen(), pos.is_chess960(), &th->rootState, th);
|
||||||
|
th->rootState = setupStates->back();
|
||||||
}
|
}
|
||||||
|
|
||||||
setupStates->back() = tmp;
|
|
||||||
|
|
||||||
Cluster::signals_init();
|
Cluster::signals_init();
|
||||||
|
|
||||||
main()->start_searching();
|
main()->start_searching();
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -68,6 +66,7 @@ public:
|
||||||
std::atomic<uint64_t> nodes, tbHits, TTsaves, bestMoveChanges;
|
std::atomic<uint64_t> nodes, tbHits, TTsaves, bestMoveChanges;
|
||||||
|
|
||||||
Position rootPos;
|
Position rootPos;
|
||||||
|
StateInfo rootState;
|
||||||
Search::RootMoves rootMoves;
|
Search::RootMoves rootMoves;
|
||||||
Depth rootDepth, completedDepth;
|
Depth rootDepth, completedDepth;
|
||||||
CounterMoveHistory counterMoves;
|
CounterMoveHistory counterMoves;
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
4
src/tt.h
4
src/tt.h
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2017 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
136
src/types.h
136
src/types.h
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -180,7 +178,7 @@ enum Value : int {
|
||||||
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
|
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
|
||||||
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY,
|
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY,
|
||||||
|
|
||||||
PawnValueMg = 124, PawnValueEg = 206,
|
PawnValueMg = 126, PawnValueEg = 208,
|
||||||
KnightValueMg = 781, KnightValueEg = 854,
|
KnightValueMg = 781, KnightValueEg = 854,
|
||||||
BishopValueMg = 825, BishopValueEg = 915,
|
BishopValueMg = 825, BishopValueEg = 915,
|
||||||
RookValueMg = 1276, RookValueEg = 1380,
|
RookValueMg = 1276, RookValueEg = 1380,
|
||||||
|
@ -203,6 +201,22 @@ enum Piece {
|
||||||
PIECE_NB = 16
|
PIECE_NB = 16
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// An ID used to track the pieces. Max. 32 pieces on board.
|
||||||
|
enum PieceId {
|
||||||
|
PIECE_ID_ZERO = 0,
|
||||||
|
PIECE_ID_KING = 30,
|
||||||
|
PIECE_ID_WKING = 30,
|
||||||
|
PIECE_ID_BKING = 31,
|
||||||
|
PIECE_ID_NONE = 32
|
||||||
|
};
|
||||||
|
|
||||||
|
inline PieceId operator++(PieceId& d, int) {
|
||||||
|
|
||||||
|
PieceId x = d;
|
||||||
|
d = PieceId(int(d) + 1);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr Value PieceValue[PHASE_NB][PIECE_NB] = {
|
constexpr Value PieceValue[PHASE_NB][PIECE_NB] = {
|
||||||
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO,
|
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO,
|
||||||
VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO },
|
VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO },
|
||||||
|
@ -232,7 +246,8 @@ enum Square : int {
|
||||||
SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8,
|
SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8,
|
||||||
SQ_NONE,
|
SQ_NONE,
|
||||||
|
|
||||||
SQUARE_NB = 64
|
SQUARE_ZERO = 0,
|
||||||
|
SQUARE_NB = 64
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Direction : int {
|
enum Direction : int {
|
||||||
|
@ -255,6 +270,94 @@ enum Rank : int {
|
||||||
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB
|
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// unique number for each piece type on each square
|
||||||
|
enum PieceSquare : uint32_t {
|
||||||
|
PS_NONE = 0,
|
||||||
|
PS_W_PAWN = 1,
|
||||||
|
PS_B_PAWN = 1 * SQUARE_NB + 1,
|
||||||
|
PS_W_KNIGHT = 2 * SQUARE_NB + 1,
|
||||||
|
PS_B_KNIGHT = 3 * SQUARE_NB + 1,
|
||||||
|
PS_W_BISHOP = 4 * SQUARE_NB + 1,
|
||||||
|
PS_B_BISHOP = 5 * SQUARE_NB + 1,
|
||||||
|
PS_W_ROOK = 6 * SQUARE_NB + 1,
|
||||||
|
PS_B_ROOK = 7 * SQUARE_NB + 1,
|
||||||
|
PS_W_QUEEN = 8 * SQUARE_NB + 1,
|
||||||
|
PS_B_QUEEN = 9 * SQUARE_NB + 1,
|
||||||
|
PS_W_KING = 10 * SQUARE_NB + 1,
|
||||||
|
PS_END = PS_W_KING, // pieces without kings (pawns included)
|
||||||
|
PS_B_KING = 11 * SQUARE_NB + 1,
|
||||||
|
PS_END2 = 12 * SQUARE_NB + 1
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ExtPieceSquare {
|
||||||
|
PieceSquare from[COLOR_NB];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Array for finding the PieceSquare corresponding to the piece on the board
|
||||||
|
extern ExtPieceSquare kpp_board_index[PIECE_NB];
|
||||||
|
|
||||||
|
constexpr bool is_ok(PieceId pid);
|
||||||
|
constexpr Square rotate180(Square sq);
|
||||||
|
|
||||||
|
// Structure holding which tracked piece (PieceId) is where (PieceSquare)
|
||||||
|
class EvalList {
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Max. number of pieces without kings is 30 but must be a multiple of 4 in AVX2
|
||||||
|
static const int MAX_LENGTH = 32;
|
||||||
|
|
||||||
|
// Array that holds the piece id for the pieces on the board
|
||||||
|
PieceId piece_id_list[SQUARE_NB];
|
||||||
|
|
||||||
|
// List of pieces, separate from White and Black POV
|
||||||
|
PieceSquare* piece_list_fw() const { return const_cast<PieceSquare*>(pieceListFw); }
|
||||||
|
PieceSquare* piece_list_fb() const { return const_cast<PieceSquare*>(pieceListFb); }
|
||||||
|
|
||||||
|
// Place the piece pc with piece_id on the square sq on the board
|
||||||
|
void put_piece(PieceId piece_id, Square sq, Piece pc)
|
||||||
|
{
|
||||||
|
assert(is_ok(piece_id));
|
||||||
|
if (pc != NO_PIECE)
|
||||||
|
{
|
||||||
|
pieceListFw[piece_id] = PieceSquare(kpp_board_index[pc].from[WHITE] + sq);
|
||||||
|
pieceListFb[piece_id] = PieceSquare(kpp_board_index[pc].from[BLACK] + rotate180(sq));
|
||||||
|
piece_id_list[sq] = piece_id;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pieceListFw[piece_id] = PS_NONE;
|
||||||
|
pieceListFb[piece_id] = PS_NONE;
|
||||||
|
piece_id_list[sq] = piece_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the specified piece_id piece to ExtPieceSquare type and return it
|
||||||
|
ExtPieceSquare piece_with_id(PieceId piece_id) const
|
||||||
|
{
|
||||||
|
ExtPieceSquare eps;
|
||||||
|
eps.from[WHITE] = pieceListFw[piece_id];
|
||||||
|
eps.from[BLACK] = pieceListFb[piece_id];
|
||||||
|
return eps;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PieceSquare pieceListFw[MAX_LENGTH];
|
||||||
|
PieceSquare pieceListFb[MAX_LENGTH];
|
||||||
|
};
|
||||||
|
|
||||||
|
// For differential evaluation of pieces that changed since last turn
|
||||||
|
struct DirtyPiece {
|
||||||
|
|
||||||
|
// Number of changed pieces
|
||||||
|
int dirty_num;
|
||||||
|
|
||||||
|
// The ids of changed pieces, max. 2 pieces can change in one move
|
||||||
|
PieceId pieceId[2];
|
||||||
|
|
||||||
|
// What changed from the piece with that piece number
|
||||||
|
ExtPieceSquare old_piece[2];
|
||||||
|
ExtPieceSquare new_piece[2];
|
||||||
|
};
|
||||||
|
|
||||||
/// Score enum stores a middlegame and an endgame value in a single integer (enum).
|
/// Score enum stores a middlegame and an endgame value in a single integer (enum).
|
||||||
/// The least significant 16 bits are used to store the middlegame value and the
|
/// The least significant 16 bits are used to store the middlegame value and the
|
||||||
|
@ -280,10 +383,10 @@ inline Value mg_value(Score s) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ENABLE_BASE_OPERATORS_ON(T) \
|
#define ENABLE_BASE_OPERATORS_ON(T) \
|
||||||
constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \
|
constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \
|
||||||
constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \
|
constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \
|
||||||
constexpr T operator-(T d) { return T(-int(d)); } \
|
constexpr T operator-(T d) { return T(-int(d)); } \
|
||||||
inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \
|
inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \
|
||||||
inline T& operator-=(T& d1, int d2) { return d1 = d1 - d2; }
|
inline T& operator-=(T& d1, int d2) { return d1 = d1 - d2; }
|
||||||
|
|
||||||
#define ENABLE_INCR_OPERATORS_ON(T) \
|
#define ENABLE_INCR_OPERATORS_ON(T) \
|
||||||
|
@ -302,6 +405,9 @@ inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
|
||||||
ENABLE_FULL_OPERATORS_ON(Value)
|
ENABLE_FULL_OPERATORS_ON(Value)
|
||||||
ENABLE_FULL_OPERATORS_ON(Direction)
|
ENABLE_FULL_OPERATORS_ON(Direction)
|
||||||
|
|
||||||
|
ENABLE_INCR_OPERATORS_ON(Piece)
|
||||||
|
ENABLE_INCR_OPERATORS_ON(PieceSquare)
|
||||||
|
ENABLE_INCR_OPERATORS_ON(PieceId)
|
||||||
ENABLE_INCR_OPERATORS_ON(PieceType)
|
ENABLE_INCR_OPERATORS_ON(PieceType)
|
||||||
ENABLE_INCR_OPERATORS_ON(Square)
|
ENABLE_INCR_OPERATORS_ON(Square)
|
||||||
ENABLE_INCR_OPERATORS_ON(File)
|
ENABLE_INCR_OPERATORS_ON(File)
|
||||||
|
@ -390,6 +496,10 @@ inline Color color_of(Piece pc) {
|
||||||
return Color(pc >> 3);
|
return Color(pc >> 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr bool is_ok(PieceId pid) {
|
||||||
|
return pid < PIECE_ID_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr bool is_ok(Square s) {
|
constexpr bool is_ok(Square s) {
|
||||||
return s >= SQ_A1 && s <= SQ_H8;
|
return s >= SQ_A1 && s <= SQ_H8;
|
||||||
}
|
}
|
||||||
|
@ -426,6 +536,11 @@ constexpr Square to_sq(Move m) {
|
||||||
return Square(m & 0x3F);
|
return Square(m & 0x3F);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return relative square when turning the board 180 degrees
|
||||||
|
constexpr Square rotate180(Square sq) {
|
||||||
|
return (Square)(sq ^ 0x3F);
|
||||||
|
}
|
||||||
|
|
||||||
constexpr int from_to(Move m) {
|
constexpr int from_to(Move m) {
|
||||||
return m & 0xFFF;
|
return m & 0xFFF;
|
||||||
}
|
}
|
||||||
|
@ -455,6 +570,11 @@ constexpr bool is_ok(Move m) {
|
||||||
return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE
|
return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Based on a congruential pseudo random number generator
|
||||||
|
constexpr Key make_key(uint64_t seed) {
|
||||||
|
return seed * 6364136223846793005ULL + 1442695040888963407ULL;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // #ifndef TYPES_H_INCLUDED
|
#endif // #ifndef TYPES_H_INCLUDED
|
||||||
|
|
||||||
#include "tune.h" // Global visibility to tuning setup
|
#include "tune.h" // Global visibility to tuning setup
|
||||||
|
|
61
src/uci.cpp
61
src/uci.cpp
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -78,6 +77,20 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// trace_eval() prints the evaluation for the current position, consistent with the UCI
|
||||||
|
// options set so far.
|
||||||
|
|
||||||
|
void trace_eval(Position& pos) {
|
||||||
|
|
||||||
|
StateListPtr states(new std::deque<StateInfo>(1));
|
||||||
|
Position p;
|
||||||
|
p.set(pos.fen(), Options["UCI_Chess960"], &states->back(), Threads.main());
|
||||||
|
|
||||||
|
Eval::verify_NNUE();
|
||||||
|
|
||||||
|
sync_cout << "\n" << Eval::trace(p) << sync_endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// setoption() is called when engine receives the "setoption" UCI command. The
|
// setoption() is called when engine receives the "setoption" UCI command. The
|
||||||
// function updates the UCI option ("name") to the given value ("value").
|
// function updates the UCI option ("name") to the given value ("value").
|
||||||
|
@ -167,7 +180,7 @@ namespace {
|
||||||
nodes += Threads.nodes_searched();
|
nodes += Threads.nodes_searched();
|
||||||
}
|
}
|
||||||
else if (Cluster::is_root())
|
else if (Cluster::is_root())
|
||||||
sync_cout << "\n" << Eval::trace(pos) << sync_endl;
|
trace_eval(pos);
|
||||||
}
|
}
|
||||||
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);
|
||||||
|
@ -185,6 +198,28 @@ 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
|
||||||
|
// and a game-ply. The model fits rather accurately the LTC fishtest statistics.
|
||||||
|
int win_rate_model(Value v, int ply) {
|
||||||
|
|
||||||
|
// The model captures only up to 240 plies, so limit input (and rescale)
|
||||||
|
double m = std::min(240, ply) / 64.0;
|
||||||
|
|
||||||
|
// Coefficients of a 3rd order polynomial fit based on fishtest data
|
||||||
|
// for two parameters needed to transform eval to the argument of a
|
||||||
|
// logistic function.
|
||||||
|
double as[] = {-8.24404295, 64.23892342, -95.73056462, 153.86478679};
|
||||||
|
double bs[] = {-3.37154371, 28.44489198, -56.67657741, 72.05858751};
|
||||||
|
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];
|
||||||
|
|
||||||
|
// Transform eval to centipawns with limited range
|
||||||
|
double x = Utility::clamp(double(100 * v) / PawnValueEg, -1000.0, 1000.0);
|
||||||
|
|
||||||
|
// Return win rate in per mille (rounded to nearest)
|
||||||
|
return int(0.5 + 1000 / (1 + std::exp((a - x) / b)));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
@ -244,7 +279,7 @@ void UCI::loop(int argc, char* argv[]) {
|
||||||
else if (token == "d" && Cluster::is_root())
|
else if (token == "d" && Cluster::is_root())
|
||||||
sync_cout << pos << sync_endl;
|
sync_cout << pos << sync_endl;
|
||||||
else if (token == "eval" && Cluster::is_root())
|
else if (token == "eval" && Cluster::is_root())
|
||||||
sync_cout << Eval::trace(pos) << sync_endl;
|
trace_eval(pos);
|
||||||
else if (token == "compiler" && Cluster::is_root())
|
else if (token == "compiler" && Cluster::is_root())
|
||||||
sync_cout << compiler_info() << sync_endl;
|
sync_cout << compiler_info() << sync_endl;
|
||||||
else if (Cluster::is_root())
|
else if (Cluster::is_root())
|
||||||
|
@ -276,6 +311,22 @@ string UCI::value(Value v) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// UCI::wdl() report WDL statistics given an evaluation and a game ply, based on
|
||||||
|
/// data gathered for fishtest LTC games.
|
||||||
|
|
||||||
|
string UCI::wdl(Value v, int ply) {
|
||||||
|
|
||||||
|
stringstream ss;
|
||||||
|
|
||||||
|
int wdl_w = win_rate_model( v, ply);
|
||||||
|
int wdl_l = win_rate_model(-v, ply);
|
||||||
|
int wdl_d = 1000 - wdl_w - wdl_l;
|
||||||
|
ss << " wdl " << wdl_w << " " << wdl_d << " " << wdl_l;
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.)
|
/// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.)
|
||||||
|
|
||||||
std::string UCI::square(Square s) {
|
std::string UCI::square(Square s) {
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -73,6 +71,7 @@ std::string value(Value v);
|
||||||
std::string square(Square s);
|
std::string square(Square s);
|
||||||
std::string move(Move m, bool chess960);
|
std::string move(Move m, bool chess960);
|
||||||
std::string pv(const Position& pos, Depth depth, Value alpha, Value beta);
|
std::string pv(const Position& pos, Depth depth, Value alpha, Value beta);
|
||||||
|
std::string wdl(Value v, int ply);
|
||||||
Move to_move(const Position& pos, std::string& str);
|
Move to_move(const Position& pos, std::string& str);
|
||||||
|
|
||||||
} // namespace UCI
|
} // namespace UCI
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
|
||||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
|
||||||
|
|
||||||
Stockfish is free software: you can redistribute it and/or modify
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -42,7 +40,8 @@ void on_hash_size(const Option& o) { TT.resize(size_t(o)); }
|
||||||
void on_logger(const Option& o) { start_logger(o); }
|
void on_logger(const Option& o) { start_logger(o); }
|
||||||
void on_threads(const Option& o) { Threads.set(size_t(o)); }
|
void on_threads(const Option& o) { Threads.set(size_t(o)); }
|
||||||
void on_tb_path(const Option& o) { Tablebases::init(o); }
|
void on_tb_path(const Option& o) { Tablebases::init(o); }
|
||||||
|
void on_use_NNUE(const Option& ) { Eval::init_NNUE(); }
|
||||||
|
void on_eval_file(const Option& ) { Eval::init_NNUE(); }
|
||||||
|
|
||||||
/// Our case insensitive less() function as required by UCI protocol
|
/// Our case insensitive less() function as required by UCI protocol
|
||||||
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
|
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
|
||||||
|
@ -74,10 +73,15 @@ void init(OptionsMap& o) {
|
||||||
o["UCI_AnalyseMode"] << Option(false);
|
o["UCI_AnalyseMode"] << Option(false);
|
||||||
o["UCI_LimitStrength"] << Option(false);
|
o["UCI_LimitStrength"] << Option(false);
|
||||||
o["UCI_Elo"] << Option(1350, 1350, 2850);
|
o["UCI_Elo"] << Option(1350, 1350, 2850);
|
||||||
|
o["UCI_ShowWDL"] << Option(false);
|
||||||
o["SyzygyPath"] << Option("<empty>", on_tb_path);
|
o["SyzygyPath"] << Option("<empty>", on_tb_path);
|
||||||
o["SyzygyProbeDepth"] << Option(1, 1, 100);
|
o["SyzygyProbeDepth"] << Option(1, 1, 100);
|
||||||
o["Syzygy50MoveRule"] << Option(true);
|
o["Syzygy50MoveRule"] << Option(true);
|
||||||
o["SyzygyProbeLimit"] << Option(7, 0, 7);
|
o["SyzygyProbeLimit"] << Option(7, 0, 7);
|
||||||
|
o["Use NNUE"] << Option(false, on_use_NNUE);
|
||||||
|
// The default must follow the format nn-[SHA256 first 12 digits].nnue
|
||||||
|
// for the build process (profile-build and fishtest) to work.
|
||||||
|
o["EvalFile"] << Option("nn-82215d0fd0df.nnue", on_eval_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ for args in "eval" \
|
||||||
"go depth 10" \
|
"go depth 10" \
|
||||||
"go movetime 1000" \
|
"go movetime 1000" \
|
||||||
"go wtime 8000 btime 8000 winc 500 binc 500" \
|
"go wtime 8000 btime 8000 winc 500 binc 500" \
|
||||||
"bench 128 $threads 10 default depth"
|
"bench 128 $threads 8 default depth"
|
||||||
do
|
do
|
||||||
|
|
||||||
echo "$prefix $exeprefix ./stockfish $args $postfix"
|
echo "$prefix $exeprefix ./stockfish $args $postfix"
|
||||||
|
@ -80,7 +80,7 @@ done
|
||||||
|
|
||||||
# more general testing, following an uci protocol exchange
|
# more general testing, following an uci protocol exchange
|
||||||
cat << EOF > game.exp
|
cat << EOF > game.exp
|
||||||
set timeout 10
|
set timeout 240
|
||||||
spawn $exeprefix ./stockfish
|
spawn $exeprefix ./stockfish
|
||||||
|
|
||||||
send "uci\n"
|
send "uci\n"
|
||||||
|
@ -98,7 +98,7 @@ cat << EOF > game.exp
|
||||||
expect "bestmove"
|
expect "bestmove"
|
||||||
|
|
||||||
send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n"
|
send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n"
|
||||||
send "go depth 30\n"
|
send "go depth 20\n"
|
||||||
expect "bestmove"
|
expect "bestmove"
|
||||||
|
|
||||||
send "quit\n"
|
send "quit\n"
|
||||||
|
@ -121,7 +121,7 @@ cat << EOF > syzygy.exp
|
||||||
send "uci\n"
|
send "uci\n"
|
||||||
send "setoption name SyzygyPath value ../tests/syzygy/\n"
|
send "setoption name SyzygyPath value ../tests/syzygy/\n"
|
||||||
expect "info string Found 35 tablebases" {} timeout {exit 1}
|
expect "info string Found 35 tablebases" {} timeout {exit 1}
|
||||||
send "bench 128 1 10 default depth\n"
|
send "bench 128 1 8 default depth\n"
|
||||||
send "quit\n"
|
send "quit\n"
|
||||||
expect eof
|
expect eof
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue