Compare commits

..

211 Commits

Author SHA1 Message Date
Daylen Yang 81490ebc75 Enable NEON for armv8 2020-08-05 10:45:17 -07:00
Dominik Schlösser c402fe7d26 apple silicon platform with NEON
USE_NEON instead of IS_ARM
New platform apple-silicon with default USE_NEON
nnue_common.h includes arm_neon.h for USE_NEON
2020-08-05 16:45:07 +02:00
Joost VandeVondele 2b8bb8e226 Revert stray functional part of 6e2236c37a 2020-08-05 07:46:45 +02:00
Joost VandeVondele 8a3df0f92f Add and adjust copyright headers/authors.
Add missing copyright headers, and refer to the AUTHORS file for copyright owners.

Refer to 'The Stockfish developers (see AUTHORS file)' for authors.
2020-08-05 07:29:12 +02:00
Joost VandeVondele 6e2236c37a Makefile: small follow-up for comp=msys2 removal 2020-08-05 07:20:23 +02:00
Joost VandeVondele d8d1ecca8c Fixes Makefile issues, #2870 2020-08-04 22:25:01 +02:00
Joost VandeVondele bb9c6bc6a1 Update default net to nn-97f742aaefcd.nnue
uploaded by Sergio Vieri

NNUE signature: 4254913
Bench: 4746616
2020-08-04 08:12:43 +02:00
Joost VandeVondele 97724370e7 Merge branch 'master' into nnue-player-wip
Bench: 4746616
2020-08-04 08:03:52 +02:00
Joost VandeVondele b16db14c87 Merge branch 'netDownloadMakefile' into nnue-player-wip 2020-08-03 20:14:52 +02:00
Joost VandeVondele 2c51afdb14 A new make target to download the default net
```
make net
```

will download the default net as specified in ucioption.cpp file.

This target assumes that `curl` or `wget` is available (in addition to grep and sed).

Needs some testing on different systems (windows, mac, etc.) to see if the implementation is portable enough.

Note that this is not part executed as part of a `make build` to make sure that a build doesn't need a network connection.
2020-08-03 14:28:54 +02:00
Joost VandeVondele 1d01b275ce Small Makefile doc fix 2020-08-03 07:23:11 +02:00
Joost VandeVondele dbbb3fa477 Add info string showing which evaluation is in use 2020-08-02 17:22:19 +02:00
Joost VandeVondele 18686e29c7 Revisit NNUE initialization
this revisits the initialization of NNUE, basically only changing
the state on the UCI options 'Use NNUE' and 'EvalFile' calling init_NNUE(),
which sets the Eval::useNNUE variable, and loads the network if needed
(i.e. useNNUE is true and the same network is not yet loaded)

init_NNUE is silent (i.e. no info strings), so that it can be called at startup
without confusing certain GUIs.

An error message on wrong setting when asking for (i.e. the net failed to load),
is delayed to the point where everything must be consistent (start of search or eval).
The engine will stop if the settings are wrong at that point.

Also works if the default value of Use NNUE would become true.
2020-08-02 17:22:19 +02:00
Joost VandeVondele e45d4f1b65 Small whitespace changes 2020-08-02 16:30:00 +02:00
Joost VandeVondele f4ecc899d8 Minimal whitespace changes 2020-08-01 22:43:14 +02:00
Dariusz Orzechowski 122c78b521 [NNUE] More cleanup in nnue folder
No functional change.
2020-08-01 22:24:26 +02:00
Joost VandeVondele aa339506db Small target adjustments 2020-08-01 19:19:10 +02:00
Dariusz Orzechowski 292c9efb1d [NNUE] Remove not used network architecture
No functional change.
2020-08-01 17:31:20 +02:00
Joost VandeVondele 9f2f46c212 [NNUE] adjust Makefile targets
clearly differentiate between sse3 and ssse3.
assume popcnt from sse4.
2020-08-01 17:30:29 +02:00
Joost VandeVondele 61ab908db3 Some coding style changes, white space 2020-08-01 09:25:00 +02:00
Joost VandeVondele 6cd70676b4 Update README.md
Mostly restores the previous README.md with some info and new UCI options, retaining only the info needed for the player.
The valuable training documentation is best preserved elsewhere.

Comments / fixes welcome.
2020-08-01 08:27:59 +02:00
Joost VandeVondele 56c9b608c9 Remove unused variable 2020-08-01 08:18:35 +02:00
Joost VandeVondele dbab8b03cf Recreate Position object for eval
takes the current option settings into account.

Fixes #2859
Fixes #2579
2020-07-31 19:16:38 +02:00
Joost VandeVondele 8e28c99f79 Use a global instead of a variable in pos 2020-07-31 15:58:33 +02:00
Joost VandeVondele e42258db5a Merge branch 'nnue-notemplate2' of https://github.com/dorzechowski/Stockfish into dorzechowski-nnue-notemplate2 2020-07-31 12:19:26 +02:00
Dariusz Orzechowski 69fa1111e6 [NNUE] StateInfo handling speed improvement
Don't copy NNUE parts of StateInfo when not needed in do_null_move().
Measurement vs master at STC shows only ~3 Elo regression when NNUE
is not used, was ~5 Elo before.

https://tests.stockfishchess.org/tests/view/5f23a9052f7e63962b99f51b
ELO: -3.02 +-1.7 (95%) LOS: 0.0%
Total: 60000 W: 11145 L: 11666 D: 37189
Ptnml(0-2): 1018, 6945, 14494, 6626, 917

No functional change.
2020-07-31 11:58:13 +02:00
Dariusz Orzechowski ffae13edff Remove some code unused in the current network architecture
No functional change.
2020-07-30 05:05:27 +02:00
mstembera 21d43e9500 Remove some unnecessary declarations and headers.
bench: 4578298
2020-07-28 20:08:10 -07:00
NguyenPham 5c616bc46b Change data file extension of nnue networks
move from .bin (used for polyglot books etc) to .nnue
2020-07-28 14:02:35 +02:00
Joost VandeVondele 2f459fb161 Add authors to the AUTHORS file
add missing contributors based on git commit history
2020-07-28 10:27:07 +02:00
Joost VandeVondele c8f7fa6a02 [NNUE] update compiler info with flags
as several new flags are added document compilation specifics under the compiler command.

No functional change.
2020-07-28 09:28:10 +02:00
Dariusz Orzechowski 7bb14c2489 Clamp NNUE evaluation score
No functional change.
2020-07-28 04:30:47 +02:00
Joost VandeVondele 6349062d42 [NNUE] remove evalnn command
instead eval uses the evaluation according to the state of Use NNUE

No functional change.
2020-07-27 20:14:25 +02:00
erbsenzaehler 7182c55e5c Update appveyor to use MSVC 2019 2020-07-27 13:09:48 +02:00
Joost VandeVondele b536b0ac67 [NNUE] init networks also for cmdline use
`./stockfish go depth 10`

now works if `Use NNUE` defaults to true.

No functional change
2020-07-27 09:43:19 +02:00
mstembera 60497a85d6 Fix a crash on Use NNUE default true
This was because the UCI::use_nnue variable was never updated to true.

closes https://github.com/official-stockfish/Stockfish/pull/2843

bench: 4578298
NNUE: 3377227
2020-07-27 07:17:41 +02:00
Joost VandeVondele a8bdf69c71 Use _mm_malloc on _WIN32 2020-07-26 22:22:36 +02:00
Joost VandeVondele a6c614da03 Include header if _MSC_VER 2020-07-26 20:44:47 +02:00
Joost VandeVondele 98ffe0cd97 [NNUE] Wrap aligned_alloc
For some systems std::aligned_alloc is not available even if c++17 is specified.
Wrap the function and use specific solutions.

Update macosx-version-min to the required minimum.

No functional change.
2020-07-26 20:32:00 +02:00
Joost VandeVondele 2b0ba70436 [NNUE] update travis CI to use new toolchain
No functional change.
2020-07-26 18:03:54 +02:00
Joost VandeVondele 27b87ddf5d [NNUE] use_nue=false for getting the material key
No functional change.
2020-07-26 14:57:38 +02:00
Joost VandeVondele 319b8e8e7b Fix unused variable warning
for certain targets. Only define variable when needed.

No functional change.
2020-07-26 14:57:38 +02:00
Joost VandeVondele 44461911f7 [NNUE] Add C++17 to appveyor
update CMakeList.txt to add required C++ standard version.

Fix signature.

the code is up-to-date with master d89730d5c8 adjust signature

Bench: 4578298
2020-07-26 14:57:29 +02:00
Dariusz Orzechowski a285850bf6 Fix valgrind issue
No functional change.
2020-07-26 08:52:22 +02:00
Dariusz Orzechowski cae61bbb65 Fix memset/memcpy warnings
No functional change.
2020-07-26 08:52:22 +02:00
Joost VandeVondele ab09c74783 Revert "[NNUE] Update travis clang on linux."
This reverts commit e3367756b5.
2020-07-25 19:48:20 +02:00
Joost VandeVondele e3367756b5 [NNUE] Update travis clang on linux.
move from 6.0 to 7.0 (minimum version for std::aligned_alloc)
2020-07-25 19:13:59 +02:00
Joost VandeVondele 49d2cd8b13 [NNUE] update x86-64-modern target
slightly increase requirements on modern from sse3 to ssse3.
2020-07-25 17:23:07 +02:00
Dariusz Orzechowski beb956f823 NNUE: Fix debug build
No functional change
2020-07-25 17:22:25 +02:00
Joost VandeVondele faf08671ff [NNUE] default net
change default net to nn-c157e0a5755b.bin as available in https://github.com/official-stockfish/networks
2020-07-25 12:45:19 +02:00
Dariusz Orzechowski 458a920788 Fix makefile option x86-64-modern 2020-07-20 13:53:21 +02:00
Dariusz Orzechowski 871e6b8c83 Merge latest changes from nodchip repo 2020-07-20 13:19:25 +02:00
Dariusz Orzechowski 76d8f6128a Fix popcnt option in makefile 2020-07-20 11:39:52 +02:00
nodchip c0e1235fef Added a description to Makefile. 2020-07-20 17:36:09 +09:00
No name 74049a450c Add NNUE targets to the output of 'make help' 2020-07-20 17:29:20 +09:00
nodchip fbdb373b64 Changed to set the binary directory to the current working directory. 2020-07-20 17:17:50 +09:00
Dariusz Orzechowski c0bbce092b Restore ARCH=x86-64-modern in the makefile 2020-07-20 09:59:40 +02:00
Dariusz Orzechowski cf8a50e654 Don't use NNUE by default - changed for an attempt on fishtest 2020-07-20 08:39:21 +02:00
Dariusz Orzechowski 675672cfc1 Use std::aligned_alloc 2020-07-20 06:20:31 +02:00
Dariusz Orzechowski 4cceeb7380 Remove code unneeded for playing, refactor, update to latest master dev 2020-07-20 05:45:24 +02:00
mstembera 77018c77cc Fix profile builds for AVX512. 2020-07-19 21:25:50 +09:00
No name fd78fb05f6 Hide NNUE options if building without NNUE support
Also remove an unused option.
2020-07-19 20:17:01 +09:00
nodchip afd7d0ea4d Fixed a bug that Makefile specifies -mpopcnt for armv8-a. 2020-07-19 18:34:35 +09:00
nodchip 36092b855a Removed the x86-64-ssse3-popcnt architecture. 2020-07-19 14:17:35 +09:00
nodchip 3bbe4802b1 Removed the sse41-popcnt architecture. 2020-07-19 14:02:49 +09:00
nodchip c001a4e62d Revert "Removed x86-64-ssse3-popcnt and x86-64-sse41-popcnt."
This reverts commit 92c2167481.
2020-07-19 13:58:19 +09:00
No name 1536e31065 Load the parameter set on an `isready' as well
Unbreaks Scid vs. PC, which doesn't send `ucinewgame'.
2020-07-19 13:22:40 +09:00
nodchip 92c2167481 Removed x86-64-ssse3-popcnt and x86-64-sse41-popcnt. 2020-07-19 12:52:20 +09:00
nodchip a4786db4c2 Added support for architectures which supports SSE3+POPCNT, SSSE3+POPCNT and SSE41+POPCNT. 2020-07-19 12:41:50 +09:00
nodchip c24ad8d8b5 Supported sse3 build. 2020-07-19 12:26:37 +09:00
mstembera 961a4dad5c Add AVX512 support.
bench: 3909820
2020-07-19 12:07:28 +09:00
nodchip 7a13d4ed60 Changed the default eval file path so that more GUIs can use Stockfish NNUE. 2020-07-17 15:40:01 +09:00
xXH4CKST3RXx 4d4c80d7fd Update README.md
Added logo, reinforcement learning instructions, and resources list.
2020-07-17 12:40:47 +09:00
nodchip 2b821682aa Update README.md 2020-07-17 11:55:30 +09:00
xXH4CKST3RXx be754a2379 Update README.md 2020-07-16 13:21:14 +09:00
xXH4CKST3RXx ec5ef2b6df Update README.md 2020-07-16 13:21:14 +09:00
xXH4CKST3RXx df4da8dc41 Update README.md 2020-07-16 13:21:14 +09:00
xXH4CKST3RXx 6118151c66 Create README.md
Added and cleaned up Gekkehenker's training guide.
2020-07-16 13:21:14 +09:00
xXH4CKST3RXx 2fd1c48e60 Rename Readme.md to stockfish.md 2020-07-16 13:21:14 +09:00
No name 961047ed6e Experimental support for PGO builds of NNUE
Only 'nnue' target and only gcc/mingw.
(does not clean profile data generated by other compilers)
To use:
 make profile-nnue ARCH=arch
(see 'make help' for list of supported archs)
2020-07-16 08:53:03 +09:00
J. Oster e29499ee4b Use the path and filename for restoring parameter files. 2020-07-15 19:29:29 +09:00
nodchip 7f4b72cdfd Merge branch 'master' of github.com:nodchip/Stockfish 2020-07-13 22:25:56 +09:00
nodchip 686a5a0df9 Fixed a bug that gensfen command does not accept the use_draw_in_training_data_generation option. 2020-07-13 22:25:23 +09:00
Anson Hu df40de9486 game result bugfix 2020-07-12 15:56:03 +09:00
No name fcb391919f Disable EVAL_HASH for 'nnue' target
Gives a 7% speed gain for me, without any parameter set loaded
(all-zero).
2020-07-11 19:29:05 +09:00
nodchip ae4db5ebfd Merge pull request #45 from joergoster/sf-nnue-update
Sf nnue update
2020-07-11 19:17:38 +09:00
joergoster db0615eed9 Merge branch 'master' into sf-nnue-update 2020-07-11 12:03:17 +02:00
zz4032 d6e8089f50 Saving new network in correct path. 2020-07-11 17:46:43 +09:00
zz4032 b521e405d3 Default network path in Linux. 2020-07-11 00:07:15 +09:00
nodchip d7c358cf19 Fixed descriptions and sanity checks in Makefile. 2020-07-10 16:55:32 +09:00
nodchip bc6a8d09e9 Unified the nnue-learn and nnue-learn-use-blas targets into nnue-learn. 2020-07-10 16:17:35 +09:00
nodchip df05ecb1d5 Added halfkp_384x2-32-32. 2020-07-10 16:14:19 +09:00
nodchip 1de1eb2d0d Refactoring: Restructured the architecture list in Makefile. 2020-07-10 16:13:21 +09:00
No name 081761d084 Add support for SSSE3-only compiles
For Core 2 Duo.

To compile:
make ARCH=x86-64 ssse3=yes nnue

No observable difference in speed to SSE4.1 on my machine.
2020-07-10 15:21:50 +09:00
No name b9a32fe331 Define USE_SSE2 for any x86-64 target
Rather than only when popcnt=yes
x86-64 instruction set includes SSE2.
2020-07-10 14:56:33 +09:00
tttak a06234c639 enable convert_plain
learn convert_plain output_file_name xxx.txt xxx.bin
2020-07-09 09:51:00 +09:00
FireFather df9b2a87db Update misc.cpp
change name to Stockfish+NNUE
and add 3 more authors
2020-07-08 23:20:36 +09:00
FireFather 821aaf3836 Update misc.cpp
do not clutter console window
remove "Windows large pages not used."
only show message when/if successful
2020-07-08 23:20:36 +09:00
nodchip 272f0f88c3 Merge pull request #40 from FireFather/master
Update evaluate_nnue_learner.cpp
2020-07-08 20:52:39 +09:00
FireFather 76d124ed70 Update evaluate_nnue_learner.cpp
replace NNUE::kFileName with NNUE::fileName
2020-07-08 13:42:28 +02:00
FireFather ec3eaad64f update evaluate_nnue.cpp
rename kFileName and change to std:string
2020-07-08 11:59:18 +09:00
FireFather f7420652b7 UCI option EvalFile
Replace EvalDir with EvalFile
Can now browse filesystem for net (eval\nn.bin is default)
nn.bin no longer hard-coded
2020-07-08 11:59:18 +09:00
FireFather d1760a1f15 update evaluate_nnue.cpp
rename kFileName and change to std:string
2020-07-08 04:23:50 +02:00
FireFather c59583bbf0 UCI option EvalFile
Replace EvalDir with EvalFile
Can now browse filesystem for net (eval\nn.bin is default)
nn.bin no longer hard-coded
2020-07-07 23:25:20 +02:00
FireFather d61378cacb Merge pull request #1 from nodchip/master
Merge
2020-07-07 13:06:40 +02:00
joergoster cd55c268cb Bugfix.
Otherwise creating a new net fails.
2020-07-07 18:51:22 +09:00
joergoster a5af8510a5 Rework loading the net. 2020-07-07 15:13:59 +09:00
nodchip 288fdc5597 Added "nodes" option to the "gensfen" command to specify the number of the nodes to be searched. 2020-07-06 17:38:43 +09:00
nodchip 85c802d0b9 Revert "use winning_percentage_wdl in learn"
This reverts commit c964e902c5.

# Conflicts:
#	src/uci.cpp
2020-07-06 11:07:46 +09:00
nodchip 3b535b5ade Merge pull request #36 from tttak/WDL_20200703b
use winning_percentage_wdl in learn
2020-07-04 07:36:33 +09:00
nodchip cea5240909 Merge branch 'master' into WDL_20200703b 2020-07-04 07:36:16 +09:00
tttak 5dec3e547e merge "Provide WDL statistics"
https://github.com/official-stockfish/Stockfish/commit/110068808b51344ac59f8c6a0846f5dfdf670392
https://github.com/official-stockfish/Stockfish/pull/2778
https://github.com/official-stockfish/Stockfish/pull/2788
2020-07-04 07:35:15 +09:00
tttak c964e902c5 use winning_percentage_wdl in learn 2020-07-03 23:21:49 +09:00
tttak 9ce0ef3ac0 merge "Provide WDL statistics"
https://github.com/official-stockfish/Stockfish/commit/110068808b51344ac59f8c6a0846f5dfdf670392
https://github.com/official-stockfish/Stockfish/pull/2778
https://github.com/official-stockfish/Stockfish/pull/2788
2020-07-03 23:01:37 +09:00
FireFather 9c19021808 update translation
1 line src\eval\nnue\features\half_kp.cpp
1 line src\movegen.h
2020-07-03 09:24:15 +09:00
FireFather c679e8f360 Update search.h
1 line of translation
2020-07-03 09:24:15 +09:00
joergoster 13824d8b96 Explicitly specify SSE41. 2020-07-02 16:15:39 +09:00
joergoster 145e4c2a10 Add SSE41 switch.
This allows building modern compiles with SSE41 enabled,
which gives a nice speedup on my Bulldozer CPU.

For example:
make nnue ARCH=x86-64-modern sse41=yes -j
2020-07-02 16:15:39 +09:00
nodchip c8262f8aec Fixed a compile error. 2020-06-30 15:58:51 +09:00
tttak 486f72af54 バグ修正(learn convert_bin_from_pgn-extract) 2020-06-30 14:01:04 +09:00
tttak fda3945c07 learn convert_bin_from_pgn-extractコマンドを追加
http://rebel13.nl/download/data.html
Download Selected Lichess games
pgn-extract --fencomments -Wlalg --nochecks --nomovenumbers --noresults -w500000 -N -V -o comp-2019-06.txt comp-2019-06.pgn
stockfish.exe
learn convert_bin_from_pgn-extract pgn_eval_side_to_move 0 output_file_name fens_comp-2019-06.bin comp-2019-06.txt

https://github.com/glinscott/fishtest/wiki/PGN-files-of-games-played-on-fishtest
pgn-extract --fencomments -Wlalg --nochecks --nomovenumbers --noresults -w500000 -N -V -o fishtest.txt fishtest.pgn
stockfish.exe
learn convert_bin_from_pgn-extract pgn_eval_side_to_move 1 output_file_name fens_fishtest.bin fishtest.txt
2020-06-30 14:01:04 +09:00
FireFather 8f31d74cf6 More comment translation
including 11 files in /src
2020-06-30 00:45:32 +09:00
FireFather f5cc77bc7c EOL
add eol at eof
2020-06-29 08:27:41 +09:00
nodchip 9dc62809c8 Merge pull request #24 from FireFather/master
Comment translation
2020-06-28 11:43:53 +09:00
nodchip 123dd68452 Merge branch 'master' into master 2020-06-28 11:42:23 +09:00
nodchip f1a8580118 Merge branch 'master' of github.com:nodchip/Stockfish 2020-06-28 11:37:42 +09:00
nodchip 87c8b324f8 Simplified source code to estimate the winning ratio from an eval value.
We need to adjust the eta again after this commit is pushed.
2020-06-28 11:37:15 +09:00
nodchip 6324c2de75 Merge pull request #23 from joergoster/linux_sse41
Linux sse41
2020-06-28 10:29:14 +09:00
FireFather aea08de018 Translation
Files in /eval, /extra, & /learn - comments translated from Japanese to English
2020-06-28 03:12:55 +02:00
FireFather 2f8c692caa Merge pull request #2 from nodchip/master
merge
2020-06-28 01:37:01 +02:00
joergoster 96f2541191 Fix compilation under Linux with -DUSE_SSE41. 2020-06-27 19:41:13 +02:00
nodchip cb8accada2 Merge branch 'master' of github.com:nodchip/Stockfish 2020-06-27 22:19:33 +09:00
nodchip 13eb540020 Changed the formula to calculate winning ratio to 1/(1+10^(-Eval/4)). 2020-06-27 22:19:22 +09:00
joergoster 32c204fb56 Merge branch 'master' into sf-nnue-nodchip 2020-06-27 10:57:09 +02:00
tttak e229015127 learn convert_bin_from_pgn-extractコマンドを追加
http://rebel13.nl/download/data.html
Download Selected Lichess games
pgn-extract --fencomments -Wlalg --nochecks --nomovenumbers --noresults -w500000 -N -V -o comp-2019-06.txt comp-2019-06.pgn
stockfish.exe
setoption name SkipLoadingEval value true
isready
learn convert_bin_from_pgn-extract output_file_name fens_comp-2019-06.bin comp-2019-06.txt
2020-06-27 16:33:36 +09:00
nodchip aa2dc962f5 Added use_draw_in_training_data_generation option to write out draw games to the training data. 2020-06-27 14:00:12 +09:00
rqs 4c926b8eb4 add pgn_to_plain 2020-06-27 13:17:54 +09:00
rqs 0761d9504e add convert_bin and option for draw positions 2020-06-27 13:17:54 +09:00
joergoster 2af46deede Fix include. 2020-06-26 09:12:24 +09:00
joergoster a5fb69008c Bugfix. No legal move is either mate or stalemate. 2020-06-26 09:12:24 +09:00
nodchip 1c8a931309 Merge pull request #18 from joergoster/sf-nnue-nodchip
Update to SF master
2020-06-25 23:09:45 +09:00
joergoster 151a0dda91 Merge branch 'master' into sf-nnue-nodchip 2020-06-25 15:10:12 +02:00
FireFather 8c8a30233c Update evaluate_nnue.cpp 2020-06-25 12:41:32 +09:00
FireFather 86e3fedf7e Update evaluate_nnue.cpp 2020-06-25 04:38:39 +02:00
FireFather 7a3c3eacdf added header guards
5 include files in \eval\nnue\architectures
2020-06-25 10:39:29 +09:00
nodchip ff31d92b94 Merge pull request #14 from joergoster/sf-nnue-nodchip
Update to SF master
2020-06-25 10:38:16 +09:00
FireFather 08d8adbade added header guards
5 include files in \eval\nnue\architectures
2020-06-24 22:41:00 +02:00
joergoster 5e119f5139 Finally. 2020-06-24 20:22:56 +02:00
joergoster 0e932757e5 Re-enable increment operator for Piece.
No functional change.
2020-06-24 20:18:32 +02:00
joergoster 8ef6c837b7 Fix.
Bench: 4471740
2020-06-24 18:04:28 +02:00
joergoster 89bbe86800 Merge branch 'master' of https://github.com/nodchip/Stockfish into sf-nnue-nodchip 2020-06-24 17:47:55 +02:00
nodchip 7818d23afb Merge pull request #13 from zz4032/linux-identifier
Linux identifier corrected.
2020-06-24 09:35:44 +09:00
nodchip 0abd692543 Fixed a build error on Visual Studio. 2020-06-24 09:33:46 +09:00
zz4032 5ae64e2244 Adding mm_malloc.h
Otherwise compiling with 'modern' or 'avx2' architecture on Linux aborts with errors.
2020-06-24 09:28:45 +09:00
zz4032 5aa801e721 Update misc.h 2020-06-23 20:54:50 +02:00
zz4032 3102896a00 Linux identifier corrected. 2020-06-23 20:53:32 +02:00
zz4032 ccd2e602a0 Adding mm_malloc.h
Otherwise compiling with 'modern' or 'avx2' architecture on Linux aborts with errors.
2020-06-23 19:55:54 +02:00
nodchip 999f5ec446 COMP=msys2を指定できるようにした 2020-06-24 00:47:34 +09:00
nodchip 43e78187d7 ARCH=x86-64-avx2を指定できるようにした 2020-06-24 00:27:45 +09:00
nodchip 76b0de40a1 コンパイラーオプションを-std=c++14から-std=c++17に変更した 2020-06-23 23:47:59 +09:00
nodchip c7884470fb Merge branch 'master' of github.com:nodchip/Stockfish 2020-06-22 10:28:02 +09:00
nodchip 6c7a594362 Added "-Wl,-s" option. 2020-06-22 10:27:52 +09:00
tttak 2d5c50d85b eval_nnueコマンド追加 2020-06-22 09:50:22 +09:00
nodchip 33772a0418 コンパイルエラーを修正した 2020-06-08 23:46:06 +09:00
nodchip 5c936572e9 Merge branch 'master' of github.com:official-stockfish/Stockfish
# Conflicts:
#	src/Makefile
#	src/position.cpp
#	src/position.h
#	src/search.cpp
#	src/types.h
#	src/uci.cpp
2020-06-08 23:09:51 +09:00
tttak 91a7557ab4 test nnue test_features等の有効化 2020-06-08 15:07:48 +09:00
nodchip d23f96d156 No pruning at low plies.
This makes those very early depths a bit more reliable.

Thanks, joergoster!

https://github.com/joergoster/Stockfish-NNUE/commit/be7f37187b85b8093ae0741909cbfd7b2bc76871
2020-06-06 18:50:15 +09:00
nodchip 2523f72ff9 盤上から取り除かれた駒に関する差分計算を省き、高速化した 2020-06-03 23:32:08 +09:00
nodchip a85e3055f4 いくつかのターゲットでOpenMPを有効にした 2020-06-02 00:13:35 +09:00
tttak 78134b7641 OpenMPの有効化 2020-06-01 23:51:05 +09:00
nodchip 6703ec8ab0 nnue-gen-sfen-from-original-evalターゲットを追加した 2020-05-30 09:50:29 +09:00
nodchip dd9818c2c1 Added "-static" compiler option. 2020-05-29 09:36:24 +09:00
nodchip f18acf97ed Added the "nnue" target.
Fixed build errors on the "nnue-learn-use-blas" target.
2020-05-28 10:08:51 +09:00
Hisayori Noda e2165155d1 Enabled halfkp_256x2-32-32.h. 2019-07-11 22:47:55 +09:00
Hisayori Noda 1d5f79db1c Merge branch 'master' of github.com:nodchip/Stockfish into k-p_256x2-32-32 2019-07-10 08:18:57 +09:00
tttak c4d30f3649 set_from_packed_sfen()でmirrorをepSquareにも適用するように修正 2019-07-10 08:16:38 +09:00
Hisayori Noda 2d70487caa Enabled k-p_256x2-32-32. 2019-07-08 19:02:09 +09:00
tttak 0be41dbb67 nullmoveとpromotionの場合に評価値の差分計算と全計算の結果が異なっていたのを修正 2019-07-08 18:51:08 +09:00
HiraokaTakuya c643ee0b45 Fix a compile error. 2019-07-08 18:49:47 +09:00
HiraokaTakuya 10aa774d08 Fix a compile error. 2019-07-08 18:49:47 +09:00
HiraokaTakuya 8718438943 std::conditional_t can be used from C++14. 2019-07-08 18:49:47 +09:00
HiraokaTakuya fc5f64b383 Add targets nnue-learn, nnue-learn-use-blas 2019-07-08 18:49:47 +09:00
tttak 3dcd2bb69b Makefileのobjclean:に「./eval/nnue/*.o」等を追加 2019-07-08 18:46:24 +09:00
Hisayori Noda b300a9d43e Enabled halfkp_256x2-32-32. 2019-07-07 21:44:02 +09:00
Hisayori Noda 747d98bf1b Added halfkp-cr-ep_256x2-32-32 architecture. 2019-07-07 20:31:54 +09:00
Hisayori Noda df827ea7ee Added enpassant feature. Added k-p-cr-ep_256x2-32-32 architecture. 2019-07-07 19:24:46 +09:00
Hisayori Noda 92052bc16b Fixed build errors. 2019-07-07 17:22:07 +09:00
Hisayori Noda 5c0037de7f Added the castling right feature. Added k-p-cr_256x2-32-32 architecture. 2019-07-07 17:02:34 +09:00
Hisayori Noda 09e529edd3 Added a hack to avoid crash with binaries compiled by g++ on MSYS2. 2019-07-05 00:22:41 +09:00
Hisayori Noda 89e846c476 Fixed a bug that Learner::qsearch() recognizes stalemate as checkmated. 2019-07-04 23:44:58 +09:00
Hisayori Noda 81262320c3 Revert "Changed the constant value to calculate the winning percentage."
This reverts commit 00f84ed99a.
2019-06-30 11:29:43 +09:00
Hisayori Noda 00f84ed99a Changed the constant value to calculate the winning percentage. 2019-06-26 08:48:48 +09:00
Hisayori Noda 26271586cb Added #if to fix compile errors. 2019-06-24 23:18:17 +09:00
Hisayori Noda a413bf7aad Added hack to avoid crash during machine learning. 2019-06-24 23:17:46 +09:00
Hisayori Noda 9a73df7379 Added test commands. 2019-06-22 00:40:46 +09:00
Hisayori Noda 5772509e8b Disabled TT when EVAL_LEARN is enabled. 2019-06-22 00:40:25 +09:00
Hisayori Noda 641724e3a5 Added debug code. 2019-06-22 00:39:21 +09:00
Hisayori Noda 57ead90f18 Fixed a bug that the game play is invalid. 2019-06-22 00:38:24 +09:00
Hisayori Noda 07dc336b0f Added validation logic. 2019-06-22 00:37:59 +09:00
Hisayori Noda 84a96a3d9c Fixed a compilation error. 2019-06-22 00:37:31 +09:00
Hisayori Noda 998d8721bd Fixed a bug that White and Black are reversed. 2019-06-22 00:37:10 +09:00
Hisayori Noda 9dab4660ce Added source files. 2019-06-22 00:36:42 +09:00
Hisayori Noda 90ef97dcbd Fixed crash bugs. 2019-06-20 00:25:40 +09:00
Hisayori Noda 24576d77ab Merged uci parse logic. 2019-06-18 21:19:51 +09:00
Hisayori Noda f58d616198 Fixed compile errors when EVAL_LEARN or EVAL_NNUE are defined. 2019-06-18 20:28:50 +09:00
Hisayori Noda bcd6985871 Merged the training data generator and the machine learning logic from YaneuraOu. 2019-06-18 08:48:05 +09:00
Hisayori Noda 87445881ec Added #ifdef statements to switch the legacy evaluation function and NNUE evaluation function. 2019-06-16 11:11:16 +09:00
Hisayori Noda 48bfe86d27 Implemented the logic to update Eval List and Dirty Pieces. 2019-06-16 10:33:53 +09:00
Hisayori Noda b330602cdc Fixed compile errors. 2019-06-15 17:08:47 +09:00
Hisayori Noda 9964fbbe25 Reverted evaluate.cpp. 2019-06-15 11:46:54 +09:00
Hisayori Noda db02ddcc90 Added files for NNUE. 2019-06-09 10:40:12 +09:00
65 changed files with 2012 additions and 3044 deletions
-12
View File
@@ -1,12 +0,0 @@
# Files from build
**/*.o
**/*.s
src/.depend
# Built binary
src/stockfish*
src/-lstdc++.res
# Neural network for the NNUE evaluation
**/*.nnue
+8 -29
View File
@@ -43,47 +43,26 @@ 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 version string
- $COMPILER -v - $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-modern optimize=no debug=yes build && ../tests/signature.sh $benchref - make clean && make -j2 ARCH=x86-64 optimize=no debug=yes build && ../tests/signature.sh $benchref
- export CXXFLAGS="-Werror"
- 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 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=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
# workaround: exclude a custom version of llvm+clang, which doesn't find llvm-profdata on ubuntu
- 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
- make clean && make -j2 ARCH=x86-64-avx512 build
- make clean && make -j2 ARCH=x86-64-vnni512 build
- make clean && make -j2 ARCH=x86-64-vnni256 build
# #
# Check perft and reproducible search # Check perft and reproducible search
- make clean && make -j2 ARCH=x86-64-modern build - export CXXFLAGS="-Werror"
- make clean && make -j2 ARCH=x86-64 build
- ../tests/perft.sh - ../tests/perft.sh
- ../tests/reprosearch.sh - ../tests/reprosearch.sh
@@ -91,11 +70,11 @@ script:
# 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-modern 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 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
# #
- 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 [[ "$TRAVIS_OS_NAME" == "linux" ]]; 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 [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi
+2 -15
View File
@@ -19,7 +19,6 @@ Alain Savard (Rocky640)
Alayan Feh (Alayan-stk-2) Alayan Feh (Alayan-stk-2)
Alexander Kure Alexander Kure
Alexander Pagel (Lolligerhans) Alexander Pagel (Lolligerhans)
Alfredo Menezes (lonfom169)
Ali AlZhrani (Cooffe) Ali AlZhrani (Cooffe)
Andrew Grant (AndyGrant) Andrew Grant (AndyGrant)
Andrey Neporada (nepal) Andrey Neporada (nepal)
@@ -33,20 +32,16 @@ Bill Henry (VoyagerOne)
Bojun Guo (noobpwnftw, Nooby) Bojun Guo (noobpwnftw, Nooby)
braich braich
Brian Sheppard (SapphireBrand, briansheppard-toast) Brian Sheppard (SapphireBrand, briansheppard-toast)
Bruno de Melo Costa (BM123499)
Bryan Cross (crossbr) Bryan Cross (crossbr)
candirufish candirufish
Chess13234 Chess13234
Chris Cain (ceebo) Chris Cain (ceebo)
Dale Weiler (graphitemaster)
Dan Schmidt (dfannius) Dan Schmidt (dfannius)
Daniel Axtens (daxtens) Daniel Axtens (daxtens)
Daniel Dugovic (ddugovic) Daniel Dugovic (ddugovic)
Dariusz Orzechowski (dorzechowski) Dariusz Orzechowski
David Zar David Zar
Daylen Yang (daylen) Daylen Yang (daylen)
Deshawn Mohan-Smith (GoldenRare)
Dieter Dobbelaere (ddobbelaere)
DiscanX DiscanX
Dominik Schlösser (domschl) Dominik Schlösser (domschl)
double-beep double-beep
@@ -58,13 +53,11 @@ 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
gamander gamander
Gary Heckman (gheckman) Gary Heckman (gheckman)
George Sobala (gsobala)
gguliash gguliash
Gian-Carlo Pascutto (gcp) Gian-Carlo Pascutto (gcp)
Gontran Lemaire (gonlem) Gontran Lemaire (gonlem)
@@ -86,9 +79,8 @@ 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 Buladas Dumale (SFisGOD) Jonathan Dumale (SFisGOD)
Joost VandeVondele (vondele) Joost VandeVondele (vondele)
Jörg Oster (joergoster) Jörg Oster (joergoster)
Joseph Ellis (jhellis3) Joseph Ellis (jhellis3)
@@ -100,7 +92,6 @@ Ken Takusagawa
kinderchocolate kinderchocolate
Kiran Panditrao (Krgp) Kiran Panditrao (Krgp)
Kojirion Kojirion
Krystian Kuzniarek (kuzkry)
Leonardo Ljubičić (ICCF World Champion) Leonardo Ljubičić (ICCF World Champion)
Leonid Pechenik (lp--) Leonid Pechenik (lp--)
Linus Arver (listx) Linus Arver (listx)
@@ -113,10 +104,8 @@ Maciej Żenczykowski (zenczykowski)
Malcolm Campbell (xoto10) Malcolm Campbell (xoto10)
Mark Tenzer (31m059) Mark Tenzer (31m059)
marotear marotear
Matt Ginsberg (mattginsberg)
Matthew Lai (matthewlai) Matthew Lai (matthewlai)
Matthew Sullivan (Matt14916) Matthew Sullivan (Matt14916)
Maxim Molchanov (Maxim)
Michael An (man) Michael An (man)
Michael Byrne (MichaelB7) Michael Byrne (MichaelB7)
Michael Chaly (Vizvezdenec) Michael Chaly (Vizvezdenec)
@@ -136,7 +125,6 @@ Niklas Fiekas (niklasf)
Nikolay Kostov (NikolayIT) Nikolay Kostov (NikolayIT)
Nguyen Pham (nguyenpham) Nguyen Pham (nguyenpham)
Norman Schmidt (FireFather) Norman Schmidt (FireFather)
notruck
Ondrej Mosnáček (WOnder93) Ondrej Mosnáček (WOnder93)
Oskar Werkelin Ahlin Oskar Werkelin Ahlin
Pablo Vazquez Pablo Vazquez
@@ -163,7 +151,6 @@ 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) Shawn Varghese (xXH4CKST3RXx)
+67 -78
View File
@@ -4,17 +4,17 @@
[![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?branch=master&svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master) [![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?branch=master&svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master)
[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. Stockfish is not a complete chess program and requires a derived from Glaurung 2.1. It features two evaluation functions, the classical
UCI-compatible graphical user interface (GUI) (e.g. XBoard with PolyGlot, Scid, evaluation based on handcrafted terms, and the NNUE evaluation based on
Cute Chess, eboard, Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in order efficiently updateable neural networks. The classical evaluation runs efficiently
to be used comfortably. Read the documentation for your GUI of choice for information on most 64bit CPU architectures, while the NNUE evaluation benefits strongly from the
about how to use Stockfish with it. vector intrinsics available on modern CPUs (avx2 or similar).
The Stockfish engine features two evaluation functions for chess, the classical Stockfish is not a complete chess program and requires a
evaluation based on handcrafted terms, and the NNUE evaluation based on efficiently UCI-compatible GUI (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard, Arena,
updatable neural networks. The classical evaluation runs efficiently on almost all Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably.
CPU architectures, while the NNUE evaluation benefits from the vector Read the documentation for your GUI of choice for information about how to use
intrinsics available on most CPUs (sse2, avx2, neon, or similar). Stockfish with it.
## Files ## Files
@@ -25,13 +25,18 @@ This distribution of Stockfish consists of the following files:
* Copying.txt, a text file containing the GNU General Public License version 3. * Copying.txt, a text file containing the GNU General Public License version 3.
* AUTHORS, a text file with the list of authors for the project
* 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.
* a file with the .nnue extension, storing the neural network for the NNUE To use the NNUE evaluation an additional data file with neural network parameters
evaluation. Binary distributions will have this file embedded. 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 options ## UCI options
@@ -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.
@@ -56,14 +58,19 @@ Currently, Stockfish has the following UCI options:
* #### Use NNUE * #### Use NNUE
Toggle between the NNUE and classical evaluation functions. If set to "true", Toggle between the NNUE and classical evaluation functions. If set to "true",
the network parameters must be available to load from file (see also EvalFile), the network parameters must be availabe to load from file (see also EvalFile).
if they are not embedded in the binary.
* #### EvalFile * #### EvalFile
The name of the file of the NNUE evaluation parameters. Depending on the GUI the The name of the file of the NNUE evaluation parameters. Depending on the GUI the
filename might have to include the full path to the folder/directory that contains the file. filename should include the full path to the folder/directory that contains the file.
Other locations, such as the directory that contains the binary and the working directory,
are also searched. * #### 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 * #### UCI_AnalyseMode
An option handled by your GUI. An option handled by your GUI.
@@ -102,8 +109,8 @@ Currently, Stockfish has the following UCI options:
* #### SyzygyProbeDepth * #### SyzygyProbeDepth
Minimum remaining search depth for which a position is probed. Set this option Minimum remaining search depth for which a position is probed. Set this option
to a higher value to probe less aggressively if you experience too much slowdown to a higher value to probe less agressively if you experience too much slowdown
(in terms of nps) due to tablebase probing. (in terms of nps) due to TB probing.
* #### Syzygy50MoveRule * #### Syzygy50MoveRule
Disable to let fifty-move rule draws detected by Syzygy tablebase probes count Disable to let fifty-move rule draws detected by Syzygy tablebase probes count
@@ -113,14 +120,6 @@ 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).
* #### 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.
* #### Move Overhead * #### Move Overhead
Assume a time delay of x ms due to network and GUI overheads. This is useful to Assume a time delay of x ms due to network and GUI overheads. This is useful to
avoid losses on time in those cases. avoid losses on time in those cases.
@@ -133,50 +132,42 @@ Currently, Stockfish has the following UCI options:
Tells the engine to use nodes searched instead of wall time to account for Tells the engine to use nodes searched instead of wall time to account for
elapsed time. Useful for engine testing. elapsed time. Useful for engine testing.
* #### Clear Hash
Clear the hash table.
* #### Debug Log File * #### Debug Log File
Write all communication to and from the engine into a text file. Write all communication to and from the engine into a text file.
## A note on classical evaluation versus NNUE evaluation ## classical and NNUE evaluation
Both approaches assign a value to a position that is used in alpha-beta (PVS) search 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 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. 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 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 inputs (e.g. piece positions only). The network is optimized and trained
on the evaluations of millions of positions at moderate search depth. on the evalutions of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward. The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move. of the neural network need to be updated after a typical chess move.
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional [The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
tools to train and develop the NNUE networks. On CPUs supporting modern vector instructions tools to train and develop the NNUE networks.
(avx2 and similar), the NNUE evaluation results in much stronger playing strength, even
if the nodes per second computed by the engine is somewhat lower (roughly 80% of nps
is typical).
Notes: 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).
1) the NNUE evaluation depends on the Stockfish binary and the network parameter Note that the NNUE evaluation depends on the Stockfish binary and the network parameter
file (see the EvalFile UCI option). Not every parameter file is compatible with a given file (see EvalFile). Not every parameter file is compatible with a given Stockfish binary.
Stockfish binary, but the default value of the EvalFile UCI option is the name of a network The default value of the EvalFile UCI option is the name of a network that is guaranteed
that is guaranteed to be compatible with that binary. to be compatible with that binary.
2) to use the NNUE evaluation, the additional data file with neural network parameters ## What to expect from Syzygybases?
needs to be available. Normally, this file is already embedded in the binary or it
can be downloaded. The filename for the default (recommended) net can be found as the default
value of the `EvalFile` UCI option, with the format `nn-[SHA256 first 12 digits].nnue`
(for instance, `nn-c157e0a5755b.nnue`). This file can be downloaded from
```
https://tests.stockfishchess.org/api/nn/[filename]
```
replacing `[filename]` as needed.
## What to expect from the Syzygy tablebases?
If the engine is searching a position that is not in the tablebases (e.g. If the engine is searching a position that is not in the tablebases (e.g.
a position with 8 pieces), it will access the tablebases during the search. a position with 8 pieces), it will access the tablebases during the search.
If the engine reports a very large score (typically 153.xx), this means If the engine reports a very large score (typically 153.xx), this means
it has found a winning line into a tablebase position. that it has found a winning line into a tablebase position.
If the engine is given a position to search that is in the tablebases, it If the engine is given a position to search that is in the tablebases, it
will use the tablebases at the beginning of the search to preselect all will use the tablebases at the beginning of the search to preselect all
@@ -184,14 +175,14 @@ good moves, i.e. all moves that preserve the win or preserve the draw while
taking into account the 50-move rule. taking into account the 50-move rule.
It will then perform a search only on those moves. **The engine will not move It will then perform a search only on those moves. **The engine will not move
immediately**, unless there is only a single good move. **The engine likely immediately**, unless there is only a single good move. **The engine likely
will not report a mate score, even if the position is known to be won.** will not report a mate score even if the position is known to be won.**
It is therefore clear that this behaviour is not identical to what one might It is therefore clear that this behaviour is not identical to what one might
be used to with Nalimov tablebases. There are technical reasons for this be used to with Nalimov tablebases. There are technical reasons for this
difference, the main technical reason being that Nalimov tablebases use the difference, the main technical reason being that Nalimov tablebases use the
DTM metric (distance-to-mate), while the Syzygy tablebases use a variation of the DTM metric (distance-to-mate), while Syzygybases use a variation of the
DTZ metric (distance-to-zero, zero meaning any move that resets the 50-move DTZ metric (distance-to-zero, zero meaning any move that resets the 50-move
counter). This special metric is one of the reasons that the Syzygy tablebases are counter). This special metric is one of the reasons that Syzygybases are
more compact than Nalimov tablebases, while still storing all information more compact than Nalimov tablebases, while still storing all information
needed for optimal play and in addition being able to take into account needed for optimal play and in addition being able to take into account
the 50-move rule. the 50-move rule.
@@ -200,8 +191,8 @@ the 50-move rule.
Stockfish supports large pages on Linux and Windows. Large pages make Stockfish supports large pages on Linux and Windows. Large pages make
the hash access more efficient, improving the engine speed, especially the hash access more efficient, improving the engine speed, especially
on large hash sizes. Typical increases are 5..10% in terms of nodes per on large hash sizes. Typical increases are 5..10% in terms of nps, but
second, but speed increases up to 30% have been measured. The support is speed increases up to 30% have been measured. The support is
automatic. Stockfish attempts to use large pages when available and automatic. Stockfish attempts to use large pages when available and
will fall back to regular memory allocation when this is not the case. will fall back to regular memory allocation when this is not the case.
@@ -209,17 +200,17 @@ will fall back to regular memory allocation when this is not the case.
Large page support on Linux is obtained by the Linux kernel Large page support on Linux is obtained by the Linux kernel
transparent huge pages functionality. Typically, transparent huge pages transparent huge pages functionality. Typically, transparent huge pages
are already enabled, and no configuration is needed. are already enabled and no configuration is needed.
### Support on Windows ### Support on Windows
The use of large pages requires "Lock Pages in Memory" privilege. See The use of large pages requires "Lock Pages in Memory" privilege. See
[Enable the Lock Pages in Memory Option (Windows)](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows) [Enable the Lock Pages in Memory Option (Windows)](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows)
on how to enable this privilege, then run [RAMMap](https://docs.microsoft.com/en-us/sysinternals/downloads/rammap) on how to enable this privilege. Logout/login may be needed
to double-check that large pages are used. We suggest that you reboot afterwards. Due to memory fragmentation, it may not always be
your computer after you have enabled large pages, because long Windows possible to allocate large pages even when enabled. A reboot
sessions suffer from memory fragmentation, which may prevent Stockfish might alleviate this problem. To determine whether large pages
from getting large pages: a fresh session is better in this regard. are in use, see the engine log.
## Compiling Stockfish yourself from the sources ## Compiling Stockfish yourself from the sources
@@ -234,11 +225,10 @@ targets with corresponding descriptions.
``` ```
cd src cd src
make help make help
make net
make build ARCH=x86-64-modern make build ARCH=x86-64-modern
``` ```
When not using the Makefile to compile (for instance, with Microsoft MSVC) you When not using the Makefile to compile (for instance with Microsoft MSVC) you
need to manually set/unset some switches in the compiler command line; see need to manually set/unset some switches in the compiler command line; see
file *types.h* for a quick reference. file *types.h* for a quick reference.
@@ -247,7 +237,8 @@ compiler you used to create your executable. These informations can
be found by typing the following commands in a console: be found by typing the following commands in a console:
``` ```
./stockfish compiler ./stockfish
compiler
``` ```
## Understanding the code base and participating in the project ## Understanding the code base and participating in the project
@@ -274,9 +265,8 @@ generic rather than being focused on Stockfish's precise implementation.
Nevertheless, a helpful resource. Nevertheless, a helpful resource.
* The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish). * The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish).
Discussions about Stockfish take place these days mainly in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking) Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking)
group and on the [Stockfish Discord channel](https://discord.gg/nv8gDtt). group and engine testing is done on [Fishtest](https://tests.stockfishchess.org/tests).
The engine testing is done on [Fishtest](https://tests.stockfishchess.org/tests).
If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test) If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test)
first, where the basics of Stockfish development are explained. first, where the basics of Stockfish development are explained.
@@ -284,17 +274,16 @@ first, where the basics of Stockfish development are explained.
## Terms of use ## Terms of use
Stockfish is free, and distributed under the **GNU General Public License version 3** Stockfish is free, and distributed under the **GNU General Public License version 3**
(GPL v3). Essentially, this means you are free to do almost exactly (GPL v3). Essentially, this means that you are free to do almost exactly
what you want with the program, including distributing it among your what you want with the program, including distributing it among your
friends, making it available for download from your website, selling friends, making it available for download from your web site, selling
it (either by itself or as part of some bigger software package), or it (either by itself or as part of some bigger software package), or
using it as the starting point for a software project of your own. using it as the starting point for a software project of your own.
The only real limitation is that whenever you distribute Stockfish in The only real limitation is that whenever you distribute Stockfish in
some way, you MUST always include the full source code, or a pointer some way, you must always include the full source code, or a pointer
to where the source code can be found, to generate the exact binary to where the source code can be found. If you make any changes to the
you are distributing. If you make any changes to the source code, source code, these changes must also be made available under the GPL.
these changes must also be made available under the GPL.
For full details, read the copy of the GPL v3 found in the file named For full details, read the copy of the GPL v3 found in the file named
*Copying.txt*. *Copying.txt*.
+152 -187
View File
@@ -1,189 +1,154 @@
Contributors to Fishtest with >10,000 CPU hours, as of Feb 15, 2021. Contributors with >10,000 CPU hours as of January 7, 2020
Thank you! Thank you!
Username CPU Hours Games played Username CPU Hours Games played
---------------------------------------------------- --------------------------------------------------
noobpwnftw 23930906 1560559941 noobpwnftw 9305707 695548021
dew 1169948 70333008 mlang 780050 61648867
mlang 957168 61657446 dew 621626 43921547
mibere 703840 46867607 mibere 524702 42238645
tvijlbrief 517888 33379462 crunchy 354587 27344275
JojoM 515404 30334272 cw 354495 27274181
cw 443276 29385549 fastgm 332801 22804359
crunchy 427035 27344275 JojoM 295750 20437451
grandphish2 425794 26347253 CSU_Dynasty 262015 21828122
fastgm 414133 24519696 Fisherman 232181 18939229
gvreuls 377843 24708884 ctoks 218866 17622052
CSU_Dynasty 338718 23030006 glinscott 201989 13780820
Fisherman 326795 21820747 tvijlbrief 201204 15337115
TueRens 313730 19490246 velislav 188630 14348485
ctoks 298442 20052551 gvreuls 187164 15149976
velislav 270519 17355456 bking_US 180289 11876016
bcross 241064 17196165 nordlandia 172076 13467830
glinscott 217799 13780820 leszek 157152 11443978
nordlandia 211692 13484886 Thanar 148021 12365359
bking_US 198894 11876016 spams 141975 10319326
drabel 191096 13129722 drabel 138073 11121749
leszek 189170 11446821 vdv 137850 9394330
mgrabiak 187153 12013300 mgrabiak 133578 10454324
robal 181389 11539242 TueRens 132485 10878471
Thanar 179852 12365359 bcross 129683 11557084
vdv 175274 9889046 marrco 126078 9356740
spams 157128 10319326 sqrt2 125830 9724586
marrco 150292 9401741 robal 122873 9593418
sqrt2 147963 9724586 vdbergh 120766 8926915
CoffeeOne 137086 5022516 malala 115926 8002293
vdbergh 137041 8926915 CoffeeOne 114241 5004100
malala 136182 8002293 dsmith 113189 7570238
mhoram 132780 8398229 BrunoBanani 104644 7436849
xoto 124729 8652088 Data 92328 8220352
davar 122092 7960001 mhoram 89333 6695109
dsmith 122059 7570238 davar 87924 7009424
Data 113305 8220352 xoto 81094 6869316
BrunoBanani 112960 7436849 ElbertoOne 80899 7023771
pemo 109598 5036441 grandphish2 78067 6160199
Dantist 106768 6431396 brabos 77212 6186135
MaZePallas 102741 6630419 psk 75733 5984901
ElbertoOne 99028 7023771 BRAVONE 73875 5054681
brabos 92118 6186135 sunu 70771 5597972
linrock 90903 6708639 sterni1971 70605 5590573
psk 89957 5984901 MaZePallas 66886 5188978
sunu 88614 6020673 Vizvezdenec 63708 4967313
sterni1971 86948 5613788 nssy 63462 5259388
Vizvezdenec 83761 5344740 jromang 61634 4940891
BRAVONE 81239 5054681 teddybaer 61231 5407666
nssy 76497 5259388 Pking_cda 60099 5293873
cuistot 76366 4370584 solarlight 57469 5028306
racerschmacer 75753 5442626 dv8silencer 56913 3883992
teddybaer 75125 5407666 tinker 54936 4086118
Pking_cda 73776 5293873 renouve 49732 3501516
0x3C33 73133 4670293 Freja 49543 3733019
jromang 72117 5054915 robnjr 46972 4053117
solarlight 70517 5028306 rap 46563 3219146
dv8silencer 70287 3883992 Bobo1239 46036 3817196
Bobo1239 68515 4652287 ttruscott 45304 3649765
manap 66273 4121774 racerschmacer 44881 3975413
tinker 64321 4268390 finfish 44764 3370515
robnjr 57262 4053117 eva42 41783 3599691
Freja 56938 3733019 biffhero 40263 3111352
ttruscott 56010 3680085 bigpen0r 39817 3291647
rkl 54986 4150767 mhunt 38871 2691355
renouve 53811 3501516 ronaldjerum 38820 3240695
finfish 51360 3370515 Antihistamine 38785 2761312
eva42 51272 3599691 pb00067 38038 3086320
rap 49985 3219146 speedycpu 37591 3003273
pb00067 49727 3298270 rkl 37207 3289580
amicic 49691 3042481 VoyagerOne 37050 3441673
ronaldjerum 47654 3240695 jbwiebe 35320 2805433
bigpen0r 47278 3291647 cuistot 34191 2146279
biffhero 46564 3111352 homyur 33927 2850481
VoyagerOne 45476 3452465 manap 32873 2327384
eastorwest 45033 3071805 gri 32538 2515779
speedycpu 43842 3003273 oryx 31267 2899051
jbwiebe 43305 2805433 EthanOConnor 30959 2090311
Antihistamine 41788 2761312 SC 30832 2730764
mhunt 41735 2691355 csnodgrass 29505 2688994
homyur 39893 2850481 jmdana 29458 2205261
gri 39871 2515779 strelock 28219 2067805
oryx 38282 2944400 jkiiski 27832 1904470
Spprtr 38157 2470529 Pyafue 27533 1902349
SC 37290 2731014 Garf 27515 2747562
csnodgrass 36207 2688994 eastorwest 27421 2317535
jmdana 36157 2210661 slakovv 26903 2021889
strelock 34716 2074055 Prcuvu 24835 2170122
Garf 33800 2747562 anst 24714 2190091
skiminki 33515 2055584 hyperbolic.tom 24319 2017394
EthanOConnor 33370 2090311 Patrick_G 23687 1801617
slakovv 32915 2021889 Sharaf_DG 22896 1786697
yurikvelo 32600 2255966 nabildanial 22195 1519409
Prcuvu 30377 2170122 chriswk 21931 1868317
manapbk 30326 1770143 achambord 21665 1767323
anst 30301 2190091 Zirie 20887 1472937
jkiiski 30136 1904470 team-oh 20217 1636708
hyperbolic.tom 29840 2017394 Isidor 20096 1680691
Pyafue 29650 1902349 ncfish1 19931 1520927
qurashee 27758 1509620 nesoneg 19875 1463031
OuaisBla 27636 1578800 Spprtr 19853 1548165
chriswk 26902 1868317 JanErik 19849 1703875
achambord 26582 1767323 agg177 19478 1395014
Fifis 26376 1776853 SFTUser 19231 1567999
Patrick_G 26276 1801617 xor12 19017 1680165
yorkman 26193 1992080 sg4032 18431 1641865
SFTUser 25182 1675689 rstoesser 18118 1293588
nabildanial 24942 1519409 MazeOfGalious 17917 1629593
Sharaf_DG 24765 1786697 j3corre 17743 941444
ncfish1 24411 1520927 cisco2015 17725 1690126
agg177 23890 1395014 ianh2105 17706 1632562
JanErik 23408 1703875 dex 17678 1467203
Isidor 23388 1680691 jundery 17194 1115855
Norabor 23164 1591830 iisiraider 17019 1101015
cisco2015 22895 1762069 horst.prack 17012 1465656
Zirie 22542 1472937 Adrian.Schmidt123 16563 1281436
team-oh 22272 1636708 purplefishies 16342 1092533
MazeOfGalious 21978 1629593 wei 16274 1745989
sg4032 21945 1643065 ville 16144 1384026
ianh2105 21725 1632562 eudhan 15712 1283717
xor12 21628 1680365 OuaisBla 15581 972000
dex 21612 1467203 DragonLord 15559 1162790
nesoneg 21494 1463031 dju 14716 875569
jjoshua2 20997 1422689 chris 14479 1487385
horst.prack 20878 1465656 0xB00B1ES 14079 1001120
0xB00B1ES 20590 1208666 OssumOpossum 13776 1007129
sphinx 20515 1352368 enedene 13460 905279
j3corre 20405 941444 bpfliegel 13346 884523
Adrian.Schmidt123 20316 1281436 Ente 13198 1156722
Ente 20017 1432602 IgorLeMasson 13087 1147232
wei 19973 1745989 jpulman 13000 870599
rstoesser 19569 1293588 ako027ako 12775 1173203
eudhan 19274 1283717 Nikolay.IT 12352 1068349
jundery 18445 1115855 Andrew Grant 12327 895539
iisiraider 18247 1101015 joster 12008 950160
ville 17883 1384026 AdrianSA 11996 804972
chris 17698 1487385 Nesa92 11455 1111993
purplefishies 17595 1092533 fatmurphy 11345 853210
DMBK 17357 1279152 Dark_wizzie 11108 1007152
DragonLord 17014 1162790 modolief 10869 896470
dju 16515 929427 mschmidt 10757 803401
IgorLeMasson 16064 1147232 infinity 10594 727027
ako027ako 15671 1173203 mabichito 10524 749391
Nikolay.IT 15154 1068349 Thomas A. Anderson 10474 732094
Andrew Grant 15114 895539 thijsk 10431 719357
OssumOpossum 14857 1007129 Flopzee 10339 894821
enedene 14476 905279 crocogoat 10104 1013854
bpfliegel 14298 884523 SapphireBrand 10104 969604
jpulman 13982 870599 stocky 10017 699440
joster 13794 950160
Nesa92 13786 1114691
crocogoat 13753 1114622
Hjax 13535 915487
Dark_wizzie 13422 1007152
mpx86 12941 693640
mabichito 12903 749391
thijsk 12886 722107
AdrianSA 12860 804972
Flopzee 12698 894821
fatmurphy 12547 853210
scuzzi 12511 845761
Karby 12429 735880
SapphireBrand 12416 969604
modolief 12386 896470
pgontarz 12151 848794
stocky 11954 699440
mschmidt 11941 803401
infinity 11470 727027
torbjo 11395 729145
Thomas A. Anderson 11372 732094
d64 11263 789184
Maxim 11129 804704
snicolet 11106 869170
MooTheCow 11008 694942
savage84 10965 641068
Rudolphous 10915 741268
Wolfgang 10809 580032
rpngn 10712 688203
basepi 10637 744851
michaelrpg 10409 735127
dzjp 10343 732529
ali-al-zhrani 10324 726502
ols 10259 570669
lbraesch 10252 647825
+1 -14
View File
@@ -4,6 +4,7 @@ clone_depth: 50
branches: branches:
only: only:
- master - master
- nnue-player-wip
# Operating system (build VM template) # Operating system (build VM template)
os: Visual Studio 2019 os: Visual Studio 2019
@@ -61,20 +62,6 @@ before_build:
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\evaluate.h | Select-String -CaseSensitive -Pattern "EvalFileDefaultName" | 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%
+183 -338
View File
@@ -1,5 +1,7 @@
# 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-2021 The Stockfish developers (see AUTHORS file) # Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
# 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
@@ -65,16 +67,14 @@ 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
# pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction
# sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions # sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions
# mmx = yes/no --- -mmmx --- Use Intel MMX instructions # sse3 = yes/no --- -msse3 --- Use Intel Streaming SIMD Extensions 3
# sse2 = yes/no --- -msse2 --- Use Intel Streaming SIMD Extensions 2
# ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3 # ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3
# sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1 # sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1
# sse42 = yes/no --- -msse4.2 --- Use Intel Streaming SIMD Extensions 4.2
# avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2 # avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2
# pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction
# avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512 # avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512
# vnni256 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 256
# vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512
# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture # neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture
# #
# Note that Makefile is space sensitive, so when adding new architectures # Note that Makefile is space sensitive, so when adding new architectures
@@ -82,188 +82,151 @@ 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),)
ARCH = x86-64-modern
help_skip_sanity = yes
endif
# explicitly check for the list of supported architectures (as listed with make help),
# the user can override with `make ARCH=x86-32-vnni256 SUPPORTED_ARCH=true`
ifeq ($(ARCH), $(filter $(ARCH), \
x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-bmi2 x86-64-avx2 \
x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \
x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 \
armv7 armv7-neon armv8 apple-silicon general-64 general-32))
SUPPORTED_ARCH=true
else
SUPPORTED_ARCH=false
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
pext = no
sse = no sse = no
mmx = no sse3 = no
sse2 = no
ssse3 = no ssse3 = no
sse41 = no sse41 = no
sse42 = no
avx2 = no avx2 = no
pext = no
avx512 = no avx512 = no
vnni256 = no
vnni512 = no
neon = no neon = no
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 -vnni256,$(ARCH)),-vnni256)
popcnt = yes
sse = yes
sse2 = yes
ssse3 = yes
sse41 = yes
avx2 = yes
pext = yes
vnni256 = yes
endif
ifeq ($(findstring -vnni512,$(ARCH)),-vnni512)
popcnt = yes
sse = yes
sse2 = yes
ssse3 = yes
sse41 = yes
avx2 = yes
pext = yes
avx512 = yes
vnni512 = 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-sse3)
arch = x86_64
prefetch = yes
sse = yes
sse3 = yes
endif
ifeq ($(ARCH),x86-64-sse3-popcnt)
arch = x86_64
prefetch = yes
sse = yes
sse3 = yes
popcnt = yes
endif
ifeq ($(ARCH),x86-64-ssse3)
arch = x86_64
prefetch = yes
sse = yes
sse3 = yes
ssse3 = yes
endif
ifeq ($(ARCH),x86-64-sse41)
arch = x86_64
prefetch = yes
popcnt = yes
sse = yes
sse3 = yes
ssse3 = yes
sse41 = yes
endif
ifeq ($(ARCH),x86-64-modern)
arch = x86_64
prefetch = yes
popcnt = yes
sse = yes
sse3 = yes
ssse3 = yes
sse41 = yes
endif
ifeq ($(ARCH),x86-64-sse42)
arch = x86_64
prefetch = yes
popcnt = yes
sse = yes
sse3 = yes
ssse3 = yes
sse41 = yes
sse42 = yes
endif
ifeq ($(ARCH),x86-64-avx2)
arch = x86_64
prefetch = yes
popcnt = yes
sse = yes
sse3 = yes
ssse3 = yes
sse41 = yes
sse42 = yes
avx2 = yes
endif
ifeq ($(ARCH),x86-64-bmi2)
arch = x86_64
prefetch = yes
popcnt = yes
sse = yes
sse3 = yes
ssse3 = yes
sse41 = yes
sse42 = yes
avx2 = yes
pext = yes
endif
ifeq ($(ARCH),x86-64-avx512)
arch = x86_64
prefetch = yes
popcnt = yes
sse = yes
sse3 = yes
ssse3 = yes
sse41 = yes
sse42 = yes
avx2 = yes
pext = yes
avx512 = 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 arch = armv8-a
prefetch = yes prefetch = yes
popcnt = yes popcnt = yes
neon = yes neon = yes
@@ -287,8 +250,6 @@ ifeq ($(ARCH),ppc-64)
prefetch = yes prefetch = yes
endif endif
endif
### ========================================================================== ### ==========================================================================
### Section 3. Low-level Configuration ### Section 3. Low-level Configuration
### ========================================================================== ### ==========================================================================
@@ -307,7 +268,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))
ifeq ($(OS),Android) ifeq ($(OS),Android)
CXXFLAGS += -m$(bits) CXXFLAGS += -m$(bits)
LDFLAGS += -m$(bits) LDFLAGS += -m$(bits)
@@ -317,10 +278,6 @@ 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
@@ -364,13 +321,11 @@ ifeq ($(COMP),clang)
ifneq ($(KERNEL),Darwin) ifneq ($(KERNEL),Darwin)
ifneq ($(KERNEL),OpenBSD) ifneq ($(KERNEL),OpenBSD)
ifneq ($(KERNEL),FreeBSD)
LDFLAGS += -latomic LDFLAGS += -latomic
endif 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)
@@ -381,40 +336,23 @@ ifeq ($(COMP),clang)
endif endif
endif endif
ifeq ($(KERNEL),Darwin)
CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.14
LDFLAGS += -arch $(arch) -mmacosx-version-min=10.14
XCRUN = xcrun
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
comp=clang
ifeq ($(arch),armv7)
CXX=armv7a-linux-androideabi16-clang++
CXXFLAGS += -mthumb -march=armv7-a -mfloat-abi=softfp -mfpu=neon
STRIP=arm-linux-androideabi-strip
endif
ifeq ($(arch),armv8)
CXX=aarch64-linux-android21-clang++
STRIP=aarch64-linux-android-strip
endif
LDFLAGS += -static-libstdc++ -pie -lm -latomic
endif
ifeq ($(comp),icc) ifeq ($(comp),icc)
profile_make = icc-profile-make profile_make = icc-profile-make
profile_use = icc-profile-use profile_use = icc-profile-use
else ifeq ($(comp),clang) else
ifeq ($(comp),clang)
profile_make = clang-profile-make profile_make = clang-profile-make
profile_use = clang-profile-use profile_use = clang-profile-use
else else
profile_make = gcc-profile-make profile_make = gcc-profile-make
profile_use = gcc-profile-use profile_use = gcc-profile-use
endif endif
endif
ifeq ($(KERNEL),Darwin)
CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.15
LDFLAGS += -arch $(arch) -mmacosx-version-min=10.15
endif
### Travis CI script uses COMPILER to overwrite CXX ### Travis CI script uses COMPILER to overwrite CXX
ifdef COMPILER ifdef COMPILER
@@ -426,26 +364,13 @@ ifdef COMPCXX
CXX=$(COMPCXX) CXX=$(COMPCXX)
endif endif
### Sometimes gcc is really clang
ifeq ($(COMP),gcc)
gccversion = $(shell $(CXX) --version)
gccisclang = $(findstring clang,$(gccversion))
ifneq ($(gccisclang),)
profile_make = clang-profile-make
profile_use = clang-profile-use
endif
endif
### On mingw use Windows threads, otherwise POSIX ### On mingw use Windows threads, otherwise POSIX
ifneq ($(comp),mingw) ifneq ($(comp),mingw)
CXXFLAGS += -DUSE_PTHREADS
# On Android Bionic's C library comes with its own pthread implementation bundled in # On Android Bionic's C library comes with its own pthread implementation bundled in
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)
ifneq ($(COMP),ndk) LDFLAGS += -lpthread
LDFLAGS += -lpthread
endif
endif endif
endif endif
endif endif
@@ -479,10 +404,6 @@ ifeq ($(optimize),yes)
CXXFLAGS += -mdynamic-no-pic CXXFLAGS += -mdynamic-no-pic
endif endif
endif endif
ifeq ($(comp),clang)
CXXFLAGS += -fexperimental-new-pass-manager
endif
endif endif
### 3.4 Bits ### 3.4 Bits
@@ -494,6 +415,7 @@ 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
@@ -501,7 +423,7 @@ endif
### 3.6 popcnt ### 3.6 popcnt
ifeq ($(popcnt),yes) ifeq ($(popcnt),yes)
ifeq ($(arch),$(filter $(arch),ppc64 armv7 armv8 arm64)) ifeq ($(arch),$(filter $(arch),ppc64 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
@@ -510,7 +432,6 @@ ifeq ($(popcnt),yes)
endif endif
endif endif
ifeq ($(avx2),yes) ifeq ($(avx2),yes)
CXXFLAGS += -DUSE_AVX2 CXXFLAGS += -DUSE_AVX2
ifeq ($(comp),$(filter $(comp),gcc clang mingw)) ifeq ($(comp),$(filter $(comp),gcc clang mingw))
@@ -521,21 +442,14 @@ endif
ifeq ($(avx512),yes) ifeq ($(avx512),yes)
CXXFLAGS += -DUSE_AVX512 CXXFLAGS += -DUSE_AVX512
ifeq ($(comp),$(filter $(comp),gcc clang mingw)) ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -mavx512f -mavx512bw CXXFLAGS += -mavx512bw
endif endif
endif endif
ifeq ($(vnni256),yes) ifeq ($(sse42),yes)
CXXFLAGS += -DUSE_VNNI CXXFLAGS += -DUSE_SSE42
ifeq ($(comp),$(filter $(comp),gcc clang mingw)) ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -mavx512f -mavx512bw -mavx512vnni -mavx512dq -mavx512vl -mprefer-vector-width=256 CXXFLAGS += -msse4.2
endif
endif
ifeq ($(vnni512),yes)
CXXFLAGS += -DUSE_VNNI
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -mavx512vnni -mavx512dq -mavx512vl
endif endif
endif endif
@@ -553,29 +467,19 @@ ifeq ($(ssse3),yes)
endif endif
endif endif
ifeq ($(sse2),yes) ifeq ($(sse3),yes)
CXXFLAGS += -DUSE_SSE2 CXXFLAGS += -DUSE_SSE3
ifeq ($(comp),$(filter $(comp),gcc clang mingw)) ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -msse2 CXXFLAGS += -msse3
endif
endif
ifeq ($(mmx),yes)
CXXFLAGS += -DUSE_MMX
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -mmmx
endif endif
endif endif
ifeq ($(neon),yes) ifeq ($(neon),yes)
CXXFLAGS += -DUSE_NEON CXXFLAGS += -DUSE_NEON
ifeq ($(KERNEL),Linux) endif
ifneq ($(COMP),ndk)
ifneq ($(arch),armv8) ifeq ($(arch),x86_64)
CXXFLAGS += -mfpu=neon CXXFLAGS += -DUSE_SSE2
endif
endif
endif
endif endif
### 3.7 pext ### 3.7 pext
@@ -591,40 +495,18 @@ 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),clang) ifeq ($(comp),$(filter $(comp),gcc clang))
CXXFLAGS += -flto
ifneq ($(findstring MINGW,$(KERNEL)),)
CXXFLAGS += -fuse-ld=lld
else ifneq ($(findstring MSYS,$(KERNEL)),)
CXXFLAGS += -fuse-ld=lld
endif
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
LDFLAGS += $(CXXFLAGS) -flto=jobserver
ifneq ($(findstring MINGW,$(KERNEL)),)
LDFLAGS += -save-temps
else ifneq ($(findstring MSYS,$(KERNEL)),)
LDFLAGS += -save-temps
endif
else
CXXFLAGS += -flto CXXFLAGS += -flto
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 known to work, older might not. # gcc version 10.1 in msys2 or TDM-GCC version 9.2 are know 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.
else ifeq ($(comp),mingw) ifeq ($(comp),mingw)
ifeq ($(KERNEL),Linux) ifeq ($(KERNEL),Linux)
ifneq ($(arch),i386)
CXXFLAGS += -flto CXXFLAGS += -flto
LDFLAGS += $(CXXFLAGS) -flto=jobserver LDFLAGS += $(CXXFLAGS)
endif
endif endif
endif endif
endif endif
@@ -641,7 +523,6 @@ endif
### Section 4. Public Targets ### Section 4. Public Targets
### ========================================================================== ### ==========================================================================
help: help:
@echo "" @echo ""
@echo "To compile stockfish, type: " @echo "To compile stockfish, type: "
@@ -650,34 +531,31 @@ 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 "net > Download the default nnue net" @echo "profile-build > Standard build with PGO"
@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 "net > Download the default nnue net"
@echo "" @echo ""
@echo "Supported archs:" @echo "Supported archs:"
@echo "" @echo ""
@echo "x86-64-vnni512 > x86 64-bit with vnni support 512bit wide"
@echo "x86-64-vnni256 > x86 64-bit with vnni support 256bit wide"
@echo "x86-64-avx512 > x86 64-bit with avx512 support" @echo "x86-64-avx512 > x86 64-bit with avx512 support"
@echo "x86-64-bmi2 > x86 64-bit with bmi2 support" @echo "x86-64-bmi2 > x86 64-bit with bmi2 support"
@echo "x86-64-avx2 > x86 64-bit with avx2 support" @echo "x86-64-avx2 > x86 64-bit with avx2 support"
@echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support" @echo "x86-64-sse42 > x86 64-bit with sse42 support"
@echo "x86-64-modern > common modern CPU, currently x86-64-sse41-popcnt" @echo "x86-64-modern > x86 64-bit with sse41 support (x86-64-sse41)"
@echo "x86-64-sse41 > x86 64-bit with sse41 support"
@echo "x86-64-ssse3 > x86 64-bit with ssse3 support" @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-sse3-popcnt > x86 64-bit with sse3 and popcnt support"
@echo "x86-64 > x86 64-bit generic (with sse2 support)" @echo "x86-64-sse3 > x86 64-bit with sse3 support"
@echo "x86-32-sse41-popcnt > x86 32-bit with sse41 and popcnt support" @echo "x86-64 > x86 64-bit generic"
@echo "x86-32-sse2 > x86 32-bit with sse2 support" @echo "x86-32 > x86 32-bit (also enables SSE)"
@echo "x86-32 > x86 32-bit generic (with mmx and sse support)" @echo "x86-32-old > x86 32-bit fall back for old hardware"
@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 "armv7-neon > ARMv7 32-bit with popcnt and neon" @echo "armv8 > ARMv8 64-bit"
@echo "armv8 > ARMv8 64-bit with popcnt and neon"
@echo "apple-silicon > Apple silicon ARM64" @echo "apple-silicon > Apple silicon ARM64"
@echo "general-64 > unspecified 64-bit" @echo "general-64 > unspecified 64-bit"
@echo "general-32 > unspecified 32-bit" @echo "general-32 > unspecified 32-bit"
@@ -688,37 +566,27 @@ 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 -j build ARCH=x86-64 (A portable, slow compile for 64-bit systems)" @echo "make build ARCH=x86-64 (This is for 64-bit systems)"
@echo "make -j build ARCH=x86-32 (A portable, slow compile for 32-bit systems)" @echo "make build ARCH=x86-32 (This is for 32-bit systems)"
@echo "" @echo ""
@echo "Advanced examples, for experienced users looking for performance: " @echo "Advanced examples, for experienced users: "
@echo "" @echo ""
@echo "make help ARCH=x86-64-bmi2" @echo "make build ARCH=x86-64 COMP=clang"
@echo "make -j profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-9.0" @echo "make profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-4.8"
@echo "make -j build ARCH=x86-64-ssse3 COMP=clang"
@echo "" @echo ""
@echo "-------------------------------"
ifeq ($(SUPPORTED_ARCH)$(help_skip_sanity), true)
@echo "The selected architecture $(ARCH) will enable the following configuration: "
@$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity
else
@echo "Specify a supported architecture with the ARCH option for more details"
@echo ""
endif
.PHONY: help build profile-build strip install clean net 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: net config-sanity build: config-sanity
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) all $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all
profile-build: net config-sanity objclean profileclean profile-build: 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)
@@ -734,40 +602,23 @@ profile-build: net 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)
-cp $(EXE) $(BINDIR) -cp $(EXE) $(BINDIR)
-strip $(BINDIR)/$(EXE) -strip $(BINDIR)/$(EXE)
# clean all #clean all
clean: objclean profileclean clean: objclean profileclean
@rm -f .depend *~ core @rm -f .depend *~ core
# evaluation network (nnue)
net: net:
$(eval nnuenet := $(shell grep EvalFileDefaultName evaluate.h | grep define | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/')) $(eval nnuenet := $(shell grep EvalFile ucioption.cpp | grep Option | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/'))
@echo "Default net: $(nnuenet)" @echo "Default net: $(nnuenet)"
$(eval nnuedownloadurl := https://tests.stockfishchess.org/api/nn/$(nnuenet)) $(eval 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)) $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -sL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi))
@if test -f "$(nnuenet)"; then \ @if test -f "$(nnuenet)"; then echo "Already available."; else echo "Downloading $(nnuedownloadurl)"; $(curl_or_wget) $(nnuedownloadurl) > $(nnuenet); fi
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:
@@ -776,7 +627,7 @@ objclean:
# clean auxiliary profiling files # clean auxiliary profiling files
profileclean: profileclean:
@rm -rf profdir @rm -rf profdir
@rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda *.s @rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda
@rm -f stockfish.profdata *.profraw @rm -f stockfish.profdata *.profraw
default: default:
@@ -788,7 +639,7 @@ default:
all: $(EXE) .depend all: $(EXE) .depend
config-sanity: net config-sanity:
@echo "" @echo ""
@echo "Config:" @echo "Config:"
@echo "debug: '$(debug)'" @echo "debug: '$(debug)'"
@@ -800,16 +651,14 @@ config-sanity: net
@echo "os: '$(OS)'" @echo "os: '$(OS)'"
@echo "prefetch: '$(prefetch)'" @echo "prefetch: '$(prefetch)'"
@echo "popcnt: '$(popcnt)'" @echo "popcnt: '$(popcnt)'"
@echo "pext: '$(pext)'"
@echo "sse: '$(sse)'" @echo "sse: '$(sse)'"
@echo "mmx: '$(mmx)'" @echo "sse3: '$(sse3)'"
@echo "sse2: '$(sse2)'"
@echo "ssse3: '$(ssse3)'" @echo "ssse3: '$(ssse3)'"
@echo "sse41: '$(sse41)'" @echo "sse41: '$(sse41)'"
@echo "sse42: '$(sse42)'"
@echo "avx2: '$(avx2)'" @echo "avx2: '$(avx2)'"
@echo "pext: '$(pext)'"
@echo "avx512: '$(avx512)'" @echo "avx512: '$(avx512)'"
@echo "vnni256: '$(vnni256)'"
@echo "vnni512: '$(vnni512)'"
@echo "neon: '$(neon)'" @echo "neon: '$(neon)'"
@echo "" @echo ""
@echo "Flags:" @echo "Flags:"
@@ -822,29 +671,25 @@ config-sanity: net
@test "$(debug)" = "yes" || test "$(debug)" = "no" @test "$(debug)" = "yes" || test "$(debug)" = "no"
@test "$(sanitize)" = "undefined" || test "$(sanitize)" = "thread" || test "$(sanitize)" = "address" || test "$(sanitize)" = "no" @test "$(sanitize)" = "undefined" || test "$(sanitize)" = "thread" || test "$(sanitize)" = "address" || test "$(sanitize)" = "no"
@test "$(optimize)" = "yes" || test "$(optimize)" = "no" @test "$(optimize)" = "yes" || test "$(optimize)" = "no"
@test "$(SUPPORTED_ARCH)" = "true"
@test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || \ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || \
test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" 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 "$(pext)" = "yes" || test "$(pext)" = "no"
@test "$(sse)" = "yes" || test "$(sse)" = "no" @test "$(sse)" = "yes" || test "$(sse)" = "no"
@test "$(mmx)" = "yes" || test "$(mmx)" = "no" @test "$(sse3)" = "yes" || test "$(sse3)" = "no"
@test "$(sse2)" = "yes" || test "$(sse2)" = "no"
@test "$(ssse3)" = "yes" || test "$(ssse3)" = "no" @test "$(ssse3)" = "yes" || test "$(ssse3)" = "no"
@test "$(sse41)" = "yes" || test "$(sse41)" = "no" @test "$(sse41)" = "yes" || test "$(sse41)" = "no"
@test "$(sse42)" = "yes" || test "$(sse42)" = "no"
@test "$(avx2)" = "yes" || test "$(avx2)" = "no" @test "$(avx2)" = "yes" || test "$(avx2)" = "no"
@test "$(pext)" = "yes" || test "$(pext)" = "no"
@test "$(avx512)" = "yes" || test "$(avx512)" = "no" @test "$(avx512)" = "yes" || test "$(avx512)" = "no"
@test "$(vnni256)" = "yes" || test "$(vnni256)" = "no"
@test "$(vnni512)" = "yes" || test "$(vnni512)" = "no"
@test "$(neon)" = "yes" || test "$(neon)" = "no" @test "$(neon)" = "yes" || test "$(neon)" = "no"
@test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" \ @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) \
@@ -853,7 +698,7 @@ clang-profile-make:
all all
clang-profile-use: clang-profile-use:
$(XCRUN) llvm-profdata merge -output=stockfish.profdata *.profraw llvm-profdata merge -output=stockfish.profdata *.profraw
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-fprofile-instr-use=stockfish.profdata' \ EXTRACXXFLAGS='-fprofile-instr-use=stockfish.profdata' \
EXTRALDFLAGS='-fprofile-use ' \ EXTRALDFLAGS='-fprofile-use ' \
+3 -14
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -95,9 +95,8 @@ 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, the type of the limit: /// where to look for positions in FEN format and the type of the limit:
/// depth, perft, nodes and movetime (in millisecs), and evaluation type /// depth, perft, nodes and movetime (in millisecs).
/// 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)
@@ -116,7 +115,6 @@ 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,23 +146,14 @@ 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;
} }
list.emplace_back("setoption name Use NNUE value true");
return list; return list;
} }
+1 -1
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
+2 -12
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -39,16 +39,6 @@ namespace {
Bitboard BishopTable[0x1480]; // To store bishop attacks Bitboard BishopTable[0x1480]; // To store bishop attacks
void init_magics(PieceType pt, Bitboard table[], Magic magics[]); void init_magics(PieceType pt, Bitboard table[], Magic magics[]);
}
/// safe_destination() returns the bitboard of target square for the given step
/// from the given square. If the step is off the board, returns empty bitboard.
inline Bitboard safe_destination(Square s, int step) {
Square to = Square(s + step);
return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
} }
@@ -120,7 +110,7 @@ namespace {
Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST}; Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST};
Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST}; Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
for (Direction d : (pt == ROOK ? RookDirections : BishopDirections)) for(Direction d : (pt == ROOK ? RookDirections : BishopDirections))
{ {
Square s = sq; Square s = sq;
while(safe_destination(s, d) && !(occupied & s)) while(safe_destination(s, d) && !(occupied & s))
+11 -1
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -279,6 +279,16 @@ inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); }
inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); } inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); }
/// safe_destination() returns the bitboard of target square for the given step
/// from the given square. If the step is off the board, returns empty bitboard.
inline Bitboard safe_destination(Square s, int step)
{
Square to = Square(s + step);
return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
}
/// attacks_bb(Square) returns the pseudo attacks of the give piece type /// attacks_bb(Square) returns the pseudo attacks of the give piece type
/// assuming an empty board. /// assuming an empty board.
+5 -5
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -553,8 +553,8 @@ ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 2)); assert(verify_material(pos, strongSide, RookValueMg, 2));
assert(verify_material(pos, weakSide, RookValueMg, 1)); assert(verify_material(pos, weakSide, RookValueMg, 1));
Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN)); Square strongPawn1 = pos.squares<PAWN>(strongSide)[0];
Square strongPawn2 = msb(pos.pieces(strongSide, PAWN)); Square strongPawn2 = pos.squares<PAWN>(strongSide)[1];
Square weakKing = pos.square<KING>(weakSide); Square weakKing = pos.square<KING>(weakSide);
// Does the stronger side have a passed pawn? // Does the stronger side have a passed pawn?
@@ -638,8 +638,8 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
Square weakKing = pos.square<KING>(weakSide); Square weakKing = pos.square<KING>(weakSide);
Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN)); Square strongPawn1 = pos.squares<PAWN>(strongSide)[0];
Square strongPawn2 = msb(pos.pieces(strongSide, PAWN)); Square strongPawn2 = pos.squares<PAWN>(strongSide)[1];
Square blockSq1, blockSq2; Square blockSq1, blockSq2;
if (relative_rank(strongSide, strongPawn1) > relative_rank(strongSide, strongPawn2)) if (relative_rank(strongSide, strongPawn1) > relative_rank(strongSide, strongPawn2))
+1 -1
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
+140 -286
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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,127 +20,46 @@
#include <cassert> #include <cassert>
#include <cstdlib> #include <cstdlib>
#include <cstring> // For std::memset #include <cstring> // For std::memset
#include <fstream>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>
#include <streambuf>
#include <vector>
#include "bitboard.h" #include "bitboard.h"
#include "evaluate.h" #include "evaluate.h"
#include "material.h" #include "material.h"
#include "misc.h"
#include "pawns.h" #include "pawns.h"
#include "thread.h" #include "thread.h"
#include "uci.h" #include "uci.h"
#include "incbin/incbin.h"
// Macro to embed the default efficiently updatable neural network (NNUE) file
// data in the engine binary (using incbin.h, by Dale Weiler).
// This macro invocation will declare the following three variables
// const unsigned char gEmbeddedNNUEData[]; // a pointer to the embedded data
// const unsigned char *const gEmbeddedNNUEEnd; // a marker to the end
// const unsigned int gEmbeddedNNUESize; // the size of the embedded file
// Note that this does not work in Microsoft Visual Studio.
#if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF)
INCBIN(EmbeddedNNUE, EvalFileDefaultName);
#else
const unsigned char gEmbeddedNNUEData[1] = {0x0};
const unsigned char *const gEmbeddedNNUEEnd = &gEmbeddedNNUEData[1];
const unsigned int gEmbeddedNNUESize = 1;
#endif
using namespace std;
using namespace Eval::NNUE;
namespace Eval { namespace Eval {
bool useNNUE; bool useNNUE;
string eval_file_loaded = "None"; std::string eval_file_loaded="None";
/// NNUE::init() tries to load a NNUE network at startup time, or when the engine void init_NNUE() {
/// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue"
/// The name of the NNUE network is always retrieved from the EvalFile option.
/// We search the given network in three locations: internally (the default
/// network may be embedded in the binary), in the active working directory and
/// in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY
/// variable to have the engine search in a special directory in their distro.
void NNUE::init() {
useNNUE = Options["Use NNUE"]; useNNUE = Options["Use NNUE"];
if (!useNNUE) std::string eval_file = std::string(Options["EvalFile"]);
return; if (useNNUE && eval_file_loaded != eval_file)
if (Eval::NNUE::load_eval_file(eval_file))
string eval_file = string(Options["EvalFile"]); eval_file_loaded = eval_file;
#if defined(DEFAULT_NNUE_DIRECTORY)
#define stringify2(x) #x
#define stringify(x) stringify2(x)
vector<string> dirs = { "<internal>" , "" , CommandLine::binaryDirectory , stringify(DEFAULT_NNUE_DIRECTORY) };
#else
vector<string> dirs = { "<internal>" , "" , CommandLine::binaryDirectory };
#endif
for (string directory : dirs)
if (eval_file_loaded != eval_file)
{
if (directory != "<internal>")
{
ifstream stream(directory + eval_file, ios::binary);
if (load_eval(eval_file, stream))
eval_file_loaded = eval_file;
}
if (directory == "<internal>" && eval_file == EvalFileDefaultName)
{
// C++ way to prepare a buffer for a memory stream
class MemoryBuffer : public basic_streambuf<char> {
public: MemoryBuffer(char* p, size_t n) { setg(p, p, p + n); setp(p, p + n); }
};
MemoryBuffer buffer(const_cast<char*>(reinterpret_cast<const char*>(gEmbeddedNNUEData)),
size_t(gEmbeddedNNUESize));
istream stream(&buffer);
if (load_eval(eval_file, stream))
eval_file_loaded = eval_file;
}
}
} }
/// NNUE::verify() verifies that the last net used was loaded successfully void verify_NNUE() {
void NNUE::verify() {
string eval_file = string(Options["EvalFile"]);
std::string eval_file = std::string(Options["EvalFile"]);
if (useNNUE && eval_file_loaded != eval_file) if (useNNUE && eval_file_loaded != eval_file)
{ {
UCI::OptionsMap defaults; std::cerr << "Use of NNUE evaluation, but the file " << eval_file << " was not loaded successfully. "
UCI::init(defaults); << "These network evaluation parameters must be available, 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." << std::endl;
string msg1 = "If the UCI option \"Use NNUE\" is set to true, network evaluation parameters compatible with the engine must be available."; std::exit(EXIT_FAILURE);
string msg2 = "The option is set to true, but the network file " + eval_file + " was not loaded successfully.";
string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file.";
string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + string(defaults["EvalFile"]);
string msg5 = "The engine will be terminated now.";
sync_cout << "info string ERROR: " << msg1 << sync_endl;
sync_cout << "info string ERROR: " << msg2 << sync_endl;
sync_cout << "info string ERROR: " << msg3 << sync_endl;
sync_cout << "info string ERROR: " << msg4 << sync_endl;
sync_cout << "info string ERROR: " << msg5 << sync_endl;
exit(EXIT_FAILURE);
} }
if (useNNUE) if (useNNUE)
sync_cout << "info string NNUE evaluation using " << eval_file << " enabled" << sync_endl; sync_cout << "info string NNUE evaluation using " << eval_file << " enabled." << sync_endl;
else else
sync_cout << "info string classical evaluation enabled" << sync_endl; sync_cout << "info string classical evaluation enabled." << sync_endl;
} }
} }
@@ -188,11 +107,9 @@ using namespace Trace;
namespace { namespace {
// Threshold for lazy and space evaluation // Threshold for lazy and space evaluation
constexpr Value LazyThreshold1 = Value(1565); constexpr Value LazyThreshold1 = Value(1400);
constexpr Value LazyThreshold2 = Value(1102); constexpr Value LazyThreshold2 = Value(1300);
constexpr Value SpaceThreshold = Value(11551); constexpr Value SpaceThreshold = Value(12222);
constexpr Value NNUEThreshold1 = Value(682);
constexpr Value NNUEThreshold2 = Value(176);
// 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 };
@@ -200,7 +117,7 @@ namespace {
// SafeCheck[PieceType][single/multiple] contains safe check bonus by piece type, // SafeCheck[PieceType][single/multiple] contains safe check bonus by piece type,
// higher if multiple safe checks are possible for that piece type. // higher if multiple safe checks are possible for that piece type.
constexpr int SafeCheck[][2] = { constexpr int SafeCheck[][2] = {
{}, {}, {803, 1292}, {639, 974}, {1087, 1878}, {759, 1132} {}, {}, {792, 1283}, {645, 967}, {1084, 1897}, {772, 1119}
}; };
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
@@ -208,25 +125,19 @@ namespace {
// MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game, // MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game,
// indexed by piece type and number of attacked squares in the mobility area. // indexed by piece type and number of attacked squares in the mobility area.
constexpr Score MobilityBonus[][32] = { constexpr Score MobilityBonus[][32] = {
{ S(-62,-79), S(-53,-57), S(-12,-31), S( -3,-17), S( 3, 7), S( 12, 13), // Knight { S(-62,-81), S(-53,-56), S(-12,-31), S( -4,-16), S( 3, 5), S( 13, 11), // Knight
S( 21, 16), S( 28, 21), S( 37, 26) }, S( 22, 17), S( 28, 20), S( 33, 25) },
{ S(-47,-59), S(-20,-25), S( 14, -8), S( 29, 12), S( 39, 21), S( 53, 40), // Bishop { S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishop
S( 53, 56), S( 60, 58), S( 62, 65), S( 69, 72), S( 78, 78), S( 83, 87), S( 55, 54), S( 63, 57), S( 63, 65), S( 68, 73), S( 81, 78), S( 81, 86),
S( 91, 88), S( 96, 98) }, S( 91, 88), S( 98, 97) },
{ S(-60,-82), S(-24,-15), S( 0, 17) ,S( 3, 43), S( 4, 72), S( 14,100), // Rook { S(-60,-78), S(-20,-17), S( 2, 23), S( 3, 39), S( 3, 70), S( 11, 99), // Rook
S( 20,102), S( 30,122), S( 41,133), S(41 ,139), S( 41,153), S( 45,160), S( 22,103), S( 31,121), S( 40,134), S( 40,139), S( 41,158), S( 48,164),
S( 57,165), S( 58,170), S( 67,175) }, S( 57,168), S( 57,169), S( 62,172) },
{ S(-29,-49), S(-16,-29), S( -8, -8), S( -8, 17), S( 18, 39), S( 25, 54), // Queen { S(-30,-48), S(-12,-30), S( -8, -7), S( -9, 19), S( 20, 40), S( 23, 55), // Queen
S( 23, 59), S( 37, 73), S( 41, 76), S( 54, 95), S( 65, 95) ,S( 68,101), S( 23, 59), S( 35, 75), S( 38, 78), S( 53, 96), S( 64, 96), S( 65,100),
S( 69,124), S( 70,128), S( 70,132), S( 70,133) ,S( 71,136), S( 72,140), S( 65,121), S( 66,127), S( 67,131), S( 67,133), S( 72,136), S( 72,141),
S( 74,147), S( 76,149), S( 90,153), S(104,169), S(105,171), S(106,171), S( 77,147), S( 79,150), S( 93,151), S(108,168), S(108,168), S(108,171),
S(112,178), S(114,185), S(114,187), S(119,221) } S(110,182), S(114,182), S(114,192), S(116,219) }
};
// BishopPawns[distance from edge] contains a file-dependent penalty for pawns on
// squares of the same color as our bishop.
constexpr Score BishopPawns[int(FILE_NB) / 2] = {
S(3, 8), S(3, 9), S(2, 8), S(3, 8)
}; };
// KingProtector[knight/bishop] contains penalty for each distance unit to own king // KingProtector[knight/bishop] contains penalty for each distance unit to own king
@@ -234,30 +145,32 @@ namespace {
// Outpost[knight/bishop] contains bonuses for each knight or bishop occupying a // 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. // pawn protected square on rank 4 to 6 which is also safe from a pawn attack.
constexpr Score Outpost[] = { S(57, 38), S(31, 24) }; constexpr Score Outpost[] = { S(56, 36), S(30, 23) };
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn // PassedRank[Rank] contains a bonus according to the rank of a passed pawn
constexpr Score PassedRank[RANK_NB] = { constexpr Score PassedRank[RANK_NB] = {
S(0, 0), S(7, 27), S(16, 32), S(17, 40), S(64, 71), S(170, 174), S(278, 262) S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
}; };
constexpr Score RookOnClosedFile = S(10, 5); // RookOnFile[semiopen/open] contains bonuses for each rook when there is
constexpr Score RookOnOpenFile[] = { S(19, 6), S(47, 26) }; // no (friendly) pawn on the rook file.
constexpr Score RookOnFile[] = { S(19, 7), S(48, 29) };
// ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to // ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to
// which piece type attacks which one. Attacks on lesser pieces which are // which piece type attacks which one. Attacks on lesser pieces which are
// pawn-defended are not considered. // pawn-defended are not considered.
constexpr Score ThreatByMinor[PIECE_TYPE_NB] = { constexpr Score ThreatByMinor[PIECE_TYPE_NB] = {
S(0, 0), S(5, 32), S(55, 41), S(77, 56), S(89, 119), S(79, 162) S(0, 0), S(5, 32), S(57, 41), S(77, 56), S(88, 119), S(79, 161)
}; };
constexpr Score ThreatByRook[PIECE_TYPE_NB] = { constexpr Score ThreatByRook[PIECE_TYPE_NB] = {
S(0, 0), S(3, 44), S(37, 68), S(42, 60), S(0, 39), S(58, 43) S(0, 0), S(3, 46), S(37, 68), S(42, 60), S(0, 38), S(58, 41)
}; };
// Assorted bonuses and penalties // Assorted bonuses and penalties
constexpr Score UncontestedOutpost = S( 1, 10); constexpr Score BadOutpost = S( -7, 36);
constexpr Score BishopOnKingRing = S( 24, 0); constexpr Score BishopOnKingRing = S( 24, 0);
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);
@@ -267,9 +180,11 @@ namespace {
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);
constexpr Score RookOnQueenFile = S( 6, 11);
constexpr Score SliderOnQueen = S( 60, 18); constexpr Score SliderOnQueen = S( 60, 18);
constexpr Score ThreatByKing = S( 24, 89); constexpr Score ThreatByKing = S( 24, 89);
constexpr Score ThreatByPawnPush = S( 48, 39); constexpr Score ThreatByPawnPush = S( 48, 39);
@@ -367,8 +282,8 @@ namespace {
attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]); attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
// Init our king safety tables // Init our king safety tables
Square s = make_square(std::clamp(file_of(ksq), FILE_B, FILE_G), Square s = make_square(Utility::clamp(file_of(ksq), FILE_B, FILE_G),
std::clamp(rank_of(ksq), RANK_2, RANK_7)); Utility::clamp(rank_of(ksq), RANK_2, RANK_7));
kingRing[Us] = attacks_bb<KING>(s) | s; kingRing[Us] = attacks_bb<KING>(s) | s;
kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them)); kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
@@ -388,15 +303,15 @@ namespace {
constexpr Direction Down = -pawn_push(Us); constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
: Rank5BB | Rank4BB | Rank3BB); : Rank5BB | Rank4BB | Rank3BB);
Bitboard b1 = pos.pieces(Us, Pt); const Square* pl = pos.squares<Pt>(Us);
Bitboard b, bb; Bitboard b, bb;
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
attackedBy[Us][Pt] = 0; attackedBy[Us][Pt] = 0;
while (b1) { for (Square s = *pl; s != SQ_NONE; s = *++pl)
Square s = pop_lsb(&b1); {
// Find attacked squares, including x-ray attacks for bishops and rooks // Find attacked squares, including x-ray attacks for bishops and rooks
b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN)) b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN))
: Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK)) : Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK))
@@ -423,21 +338,21 @@ namespace {
score += BishopOnKingRing; score += BishopOnKingRing;
int mob = popcount(b & mobilityArea[Us]); int mob = popcount(b & mobilityArea[Us]);
mobility[Us] += MobilityBonus[Pt - 2][mob]; mobility[Us] += MobilityBonus[Pt - 2][mob];
if (Pt == BISHOP || Pt == KNIGHT) if (Pt == BISHOP || Pt == KNIGHT)
{ {
// Bonus if the piece is on an outpost square or can reach one // Bonus if the piece is on an outpost square or can reach one
// Bonus for knights (UncontestedOutpost) if few relevant targets // Reduced bonus for knights (BadOutpost) if few relevant targets
bb = OutpostRanks & (attackedBy[Us][PAWN] | shift<Down>(pos.pieces(PAWN))) bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them);
& ~pe->pawn_attacks_span(Them);
Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN); Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN);
if ( Pt == KNIGHT if ( Pt == KNIGHT
&& bb & s & ~CenterFiles // on a side outpost && bb & s & ~CenterFiles // on a side outpost
&& !(b & targets) // no relevant attacks && !(b & targets) // no relevant attacks
&& (!more_than_one(targets & (s & QueenSide ? QueenSide : KingSide)))) && (!more_than_one(targets & (s & QueenSide ? QueenSide : KingSide))))
score += UncontestedOutpost * popcount(pos.pieces(PAWN) & (s & QueenSide ? QueenSide : KingSide)); score += BadOutpost;
else if (bb & s) else if (bb & s)
score += Outpost[Pt == BISHOP]; score += Outpost[Pt == BISHOP];
else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us)) else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
@@ -450,14 +365,14 @@ namespace {
// Penalty if the piece is far from the king // Penalty if the piece is far from the king
score -= KingProtector[Pt == BISHOP] * distance(pos.square<KING>(Us), s); score -= KingProtector[Pt == BISHOP] * distance(pos.square<KING>(Us), s);
if constexpr (Pt == BISHOP) if (Pt == BISHOP)
{ {
// Penalty according to the number of our pawns on the same color square as the // Penalty according to the number of our pawns on the same color square as the
// bishop, bigger when the center files are blocked with pawns and smaller // bishop, bigger when the center files are blocked with pawns and smaller
// when the bishop is outside the pawn chain. // when the bishop is outside the pawn chain.
Bitboard blocked = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces()); Bitboard blocked = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces());
score -= BishopPawns[edge_distance(file_of(s))] * pos.pawns_on_same_color_squares(Us, s) score -= BishopPawns * pos.pawns_on_same_color_squares(Us, s)
* (!(attackedBy[Us][PAWN] & s) + popcount(blocked & CenterFiles)); * (!(attackedBy[Us][PAWN] & s) + popcount(blocked & CenterFiles));
// Penalty for all enemy pawns x-rayed // Penalty for all enemy pawns x-rayed
@@ -482,42 +397,38 @@ namespace {
} }
} }
if constexpr (Pt == ROOK) if (Pt == ROOK)
{ {
// Bonuses for rook on a (semi-)open or closed file // Bonus for rook on the same file as a queen
if (pos.is_on_semiopen_file(Us, s)) if (file_bb(s) & pos.pieces(QUEEN))
{ score += RookOnQueenFile;
score += RookOnOpenFile[pos.is_on_semiopen_file(Them, s)];
}
else
{
// If our pawn on this file is blocked, increase penalty
if ( pos.pieces(Us, PAWN)
& shift<Down>(pos.pieces())
& file_bb(s))
{
score -= RookOnClosedFile;
}
// Penalty when trapped by the king, even more if the king cannot castle // Bonus for rook on an open or semi-open file
if (mob <= 3) if (pos.is_on_semiopen_file(Us, s))
{ score += RookOnFile[pos.is_on_semiopen_file(Them, s)];
File kf = file_of(pos.square<KING>(Us));
if ((kf < FILE_E) == (file_of(s) < kf)) // Penalty when trapped by the king, even more if the king cannot castle
score -= TrappedRook * (1 + !pos.castling_rights(Us)); else if (mob <= 3)
} {
File kf = file_of(pos.square<KING>(Us));
if ((kf < FILE_E) == (file_of(s) < kf))
score -= TrappedRook * (1 + !pos.castling_rights(Us));
} }
} }
if constexpr (Pt == QUEEN) if (Pt == QUEEN)
{ {
// Penalty if any relative pin or discovered attack against the queen // Penalty if any relative pin or discovered attack against the queen
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 constexpr (T) if (T)
Trace::add(Pt, Us, score); Trace::add(Pt, Us, score);
return score; return score;
@@ -593,18 +504,18 @@ namespace {
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] // (~10 Elo) kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
+ 183 * popcount(kingRing[Us] & weak) // (~15 Elo) + 185 * popcount(kingRing[Us] & weak)
+ 148 * popcount(unsafeChecks) // (~4 Elo) + 148 * popcount(unsafeChecks)
+ 98 * popcount(pos.blockers_for_king(Us)) // (~2 Elo) + 98 * popcount(pos.blockers_for_king(Us))
+ 69 * kingAttacksCount[Them] // (~0.5 Elo) + 69 * kingAttacksCount[Them]
+ 3 * kingFlankAttack * kingFlankAttack / 8 // (~0.5 Elo) + 3 * kingFlankAttack * kingFlankAttack / 8
+ mg_value(mobility[Them] - mobility[Us]) // (~0.5 Elo) + mg_value(mobility[Them] - mobility[Us])
- 873 * !pos.count<QUEEN>(Them) // (~24 Elo) - 873 * !pos.count<QUEEN>(Them)
- 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) // (~5 Elo) - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
- 6 * mg_value(score) / 8 // (~8 Elo) - 6 * mg_value(score) / 8
- 4 * kingFlankDefense // (~5 Elo) - 4 * kingFlankDefense
+ 37; // (~0.5 Elo) + 37;
// Transform the kingDanger units into a Score, and subtract it from the evaluation // Transform the kingDanger units into a Score, and subtract it from the evaluation
if (kingDanger > 100) if (kingDanger > 100)
@@ -617,7 +528,7 @@ namespace {
// Penalty if king flank is under attack, potentially moving toward the king // Penalty if king flank is under attack, potentially moving toward the king
score -= FlankAttacks * kingFlankAttack; score -= FlankAttacks * kingFlankAttack;
if constexpr (T) if (T)
Trace::add(KING, Us, score); Trace::add(KING, Us, score);
return score; return score;
@@ -718,7 +629,7 @@ namespace {
score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance); score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance);
} }
if constexpr (T) if (T)
Trace::add(THREAT, Us, score); Trace::add(THREAT, Us, score);
return score; return score;
@@ -772,8 +683,8 @@ namespace {
Square blockSq = s + Up; Square blockSq = s + Up;
// Adjust bonus based on the king's proximity // Adjust bonus based on the king's proximity
bonus += make_score(0, ( king_proximity(Them, blockSq) * 19 / 4 bonus += make_score(0, ( (king_proximity(Them, blockSq) * 19) / 4
- king_proximity(Us, blockSq) * 2) * w); - king_proximity(Us, blockSq) * 2) * w);
// If blockSq is not the queening square then consider also a second push // If blockSq is not the queening square then consider also a second push
if (r != RANK_7) if (r != RANK_7)
@@ -788,16 +699,14 @@ namespace {
bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN); bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN);
if (!(pos.pieces(Them) & bb)) if (!(pos.pieces(Them) & bb))
unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them); unsafeSquares &= attackedBy[Them][ALL_PIECES];
// If there are no enemy pieces or attacks on passed pawn span, assign a big bonus. // If there are no enemy attacks on passed pawn span, assign a big bonus.
// Or if there is some, but they are all attacked by our pawns, assign a bit smaller bonus.
// Otherwise assign a smaller bonus if the path to queen is not attacked // Otherwise assign a smaller bonus if the path to queen is not attacked
// and even smaller bonus if it is attacked but block square is not. // and even smaller bonus if it is attacked but block square is not.
int k = !unsafeSquares ? 36 : int k = !unsafeSquares ? 35 :
!(unsafeSquares & ~attackedBy[Us][PAWN]) ? 30 : !(unsafeSquares & squaresToQueen) ? 20 :
!(unsafeSquares & squaresToQueen) ? 17 : !(unsafeSquares & blockSq) ? 9 :
!(unsafeSquares & blockSq) ? 7 :
0 ; 0 ;
// Assign a larger bonus if the block square is defended // Assign a larger bonus if the block square is defended
@@ -811,7 +720,7 @@ namespace {
score += bonus - PassedFile * edge_distance(file_of(s)); score += bonus - PassedFile * edge_distance(file_of(s));
} }
if constexpr (T) if (T)
Trace::add(PASSED, Us, score); Trace::add(PASSED, Us, score);
return score; return score;
@@ -819,7 +728,7 @@ namespace {
// Evaluation::space() computes a space evaluation for a given side, aiming to improve game // Evaluation::space() computes a space evaluation for a given side, aiming to improve game
// play in the opening. It is based on the number of safe squares on the four central files // play in the opening. It is based on the number of safe squares on the 4 central files
// on ranks 2 to 4. Completely safe squares behind a friendly pawn are counted twice. // on ranks 2 to 4. Completely safe squares behind a friendly pawn are counted twice.
// Finally, the space bonus is multiplied by a weight which decreases according to occupancy. // Finally, the space bonus is multiplied by a weight which decreases according to occupancy.
@@ -846,13 +755,11 @@ namespace {
behind |= shift<Down>(behind); behind |= shift<Down>(behind);
behind |= shift<Down+Down>(behind); behind |= shift<Down+Down>(behind);
// Compute space score based on the number of safe squares and number of our pieces
// increased with number of total blocked pawns in position.
int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]); int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]);
int weight = pos.count<ALL_PIECES>(Us) - 3 + std::min(pe->blocked_count(), 9); int weight = pos.count<ALL_PIECES>(Us) - 3 + std::min(pe->blocked_count(), 9);
Score score = make_score(bonus * weight * weight / 16, 0); Score score = make_score(bonus * weight * weight / 16, 0);
if constexpr (T) if (T)
Trace::add(SPACE, Us, score); Trace::add(SPACE, Us, score);
return score; return score;
@@ -867,7 +774,7 @@ namespace {
Value Evaluation<T>::winnable(Score score) const { Value Evaluation<T>::winnable(Score score) const {
int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK)) int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
+ int(rank_of(pos.square<KING>(WHITE)) - rank_of(pos.square<KING>(BLACK))); - distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide) bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide)
&& (pos.pieces(PAWN) & KingSide); && (pos.pieces(PAWN) & KingSide);
@@ -894,7 +801,7 @@ namespace {
// Now apply the bonus: note that we find the attacking side by extracting the // Now apply the bonus: note that we find the attacking side by extracting the
// sign of the midgame or endgame values, and that we carefully cap the bonus // sign of the midgame or endgame values, and that we carefully cap the bonus
// so that the midgame and endgame scores do not change sign after the bonus. // so that the midgame and endgame scores do not change sign after the bonus.
int u = ((mg > 0) - (mg < 0)) * std::clamp(complexity + 50, -abs(mg), 0); int u = ((mg > 0) - (mg < 0)) * Utility::clamp(complexity + 50, -abs(mg), 0);
int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg)); int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg));
mg += u; mg += u;
@@ -909,37 +816,23 @@ namespace {
{ {
if (pos.opposite_bishops()) if (pos.opposite_bishops())
{ {
// For pure opposite colored bishops endgames use scale factor
// based on the number of passed pawns of the strong side.
if ( pos.non_pawn_material(WHITE) == BishopValueMg if ( pos.non_pawn_material(WHITE) == BishopValueMg
&& pos.non_pawn_material(BLACK) == BishopValueMg) && pos.non_pawn_material(BLACK) == BishopValueMg)
sf = 18 + 4 * popcount(pe->passed_pawns(strongSide)); sf = 18 + 4 * popcount(pe->passed_pawns(strongSide));
// For every other opposite colored bishops endgames use scale factor
// based on the number of all pieces of the strong side.
else else
sf = 22 + 3 * pos.count<ALL_PIECES>(strongSide); sf = 22 + 3 * pos.count<ALL_PIECES>(strongSide);
} }
// For rook endgames with strong side not having overwhelming pawn number advantage
// and its pawns being on one flank and weak side protecting its pieces with a king
// use lower scale factor.
else if ( pos.non_pawn_material(WHITE) == RookValueMg else if ( pos.non_pawn_material(WHITE) == RookValueMg
&& pos.non_pawn_material(BLACK) == RookValueMg && pos.non_pawn_material(BLACK) == RookValueMg
&& pos.count<PAWN>(strongSide) - pos.count<PAWN>(~strongSide) <= 1 && pos.count<PAWN>(strongSide) - pos.count<PAWN>(~strongSide) <= 1
&& bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN)) && bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN))
&& (attacks_bb<KING>(pos.square<KING>(~strongSide)) & pos.pieces(~strongSide, PAWN))) && (attacks_bb<KING>(pos.square<KING>(~strongSide)) & pos.pieces(~strongSide, PAWN)))
sf = 36; sf = 36;
// For queen vs no queen endgames use scale factor
// based on number of minors of side that doesn't have queen.
else if (pos.count<QUEEN>() == 1) else if (pos.count<QUEEN>() == 1)
sf = 37 + 3 * (pos.count<QUEEN>(WHITE) == 1 ? pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK) sf = 37 + 3 * (pos.count<QUEEN>(WHITE) == 1 ? pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK)
: pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE)); : pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE));
// In every other case use scale factor based on
// the number of pawns of the strong side reduced if pawns are on a single flank.
else else
sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide)) - 4 * !pawnsOnBothFlanks; sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide));
// Reduce scale factor in case of pawns being on a single flank
sf -= 4 * !pawnsOnBothFlanks;
} }
// Interpolate between the middlegame and (scaled by 'sf') endgame score // Interpolate between the middlegame and (scaled by 'sf') endgame score
@@ -947,7 +840,7 @@ namespace {
+ eg * int(PHASE_MIDGAME - me->game_phase()) * ScaleFactor(sf) / SCALE_FACTOR_NORMAL; + eg * int(PHASE_MIDGAME - me->game_phase()) * ScaleFactor(sf) / SCALE_FACTOR_NORMAL;
v /= PHASE_MIDGAME; v /= PHASE_MIDGAME;
if constexpr (T) if (T)
{ {
Trace::add(WINNABLE, make_score(u, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL - eg_value(score))); Trace::add(WINNABLE, make_score(u, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL - eg_value(score)));
Trace::add(TOTAL, make_score(mg, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL)); Trace::add(TOTAL, make_score(mg, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL));
@@ -1019,7 +912,7 @@ make_v:
Value 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 constexpr (T) if (T)
{ {
Trace::add(MATERIAL, pos.psq_score()); Trace::add(MATERIAL, pos.psq_score());
Trace::add(IMBALANCE, me->imbalance()); Trace::add(IMBALANCE, me->imbalance());
@@ -1033,6 +926,9 @@ make_v:
// 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;
} }
@@ -1044,48 +940,10 @@ make_v:
Value Eval::evaluate(const Position& pos) { Value Eval::evaluate(const Position& pos) {
Value v; if (Eval::useNNUE)
return NNUE::evaluate(pos);
if (!Eval::useNNUE)
v = Evaluation<NO_TRACE>(pos).value();
else else
{ return Evaluation<NO_TRACE>(pos).value();
// Scale and shift NNUE for compatibility with search and classical evaluation
auto adjusted_NNUE = [&](){
int mat = pos.non_pawn_material() + 2 * PawnValueMg * pos.count<PAWN>();
return NNUE::evaluate(pos) * (641 + mat / 32 - 4 * pos.rule50_count()) / 1024 + Tempo;
};
// If there is PSQ imbalance use classical eval, with small probability if it is small
Value psq = Value(abs(eg_value(pos.psq_score())));
int r50 = 16 + pos.rule50_count();
bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50;
bool classical = largePsq || (psq > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB));
// Use classical evaluation for really low piece endgames.
// The most critical case is a bishop + A/H file pawn vs naked king draw.
bool strongClassical = pos.non_pawn_material() < 2 * RookValueMg && pos.count<PAWN>() < 2;
v = classical || strongClassical ? Evaluation<NO_TRACE>(pos).value() : adjusted_NNUE();
// If the classical eval is small and imbalance large, use NNUE nevertheless.
// For the case of opposite colored bishops, switch to NNUE eval with
// small probability if the classical eval is less than the threshold.
if ( largePsq && !strongClassical
&& ( abs(v) * 16 < NNUEThreshold2 * r50
|| ( pos.opposite_bishops()
&& abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50
&& !(pos.this_thread()->nodes & 0xB))))
v = adjusted_NNUE();
}
// Damp down the evaluation linearly when shuffling
v = v * (100 - pos.rule50_count()) / 100;
// Guarantee evaluation does not hit the tablebase range
v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
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
@@ -1103,46 +961,42 @@ std::string Eval::trace(const Position& pos) {
Value v; Value v;
std::memset(scores, 0, sizeof(scores));
pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt
v = Evaluation<TRACE>(pos).value();
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
<< " Term | White | Black | Total \n"
<< " | 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 << "\nClassical evaluation: " << to_cp(v) << " (white side)\n";
if (Eval::useNNUE) if (Eval::useNNUE)
{ {
v = NNUE::evaluate(pos); v = NNUE::evaluate(pos);
v = pos.side_to_move() == WHITE ? v : -v; }
ss << "\nNNUE evaluation: " << to_cp(v) << " (white side)\n"; else
{
std::memset(scores, 0, sizeof(scores));
pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt
v = Evaluation<TRACE>(pos).value();
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
<< " Term | White | Black | Total \n"
<< " | 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 = evaluate(pos);
v = pos.side_to_move() == WHITE ? v : -v; 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";
return ss.str(); return ss.str();
} }
+6 -9
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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,18 +32,15 @@ namespace Eval {
extern bool useNNUE; extern bool useNNUE;
extern std::string eval_file_loaded; extern std::string eval_file_loaded;
void init_NNUE();
// The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue void verify_NNUE();
// for the build process (profile-build and fishtest) to work. Do not change the
// name of the macro, as it is used in the Makefile.
#define EvalFileDefaultName "nn-62ef826d1a6d.nnue"
namespace NNUE { namespace NNUE {
Value evaluate(const Position& pos); Value evaluate(const Position& pos);
bool load_eval(std::string name, std::istream& stream); Value compute_eval(const Position& pos);
void init(); void update_eval(const Position& pos);
void verify(); bool load_eval_file(const std::string& evalFile);
} // namespace NNUE } // namespace NNUE
-26
View File
@@ -1,26 +0,0 @@
The file "incbin.h" is free and unencumbered software released into
the public domain by Dale Weiler, see:
<https://github.com/graphitemaster/incbin>
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
-368
View File
@@ -1,368 +0,0 @@
/**
* @file incbin.h
* @author Dale Weiler
* @brief Utility for including binary files
*
* Facilities for including binary files into the current translation unit and
* making use from them externally in other translation units.
*/
#ifndef INCBIN_HDR
#define INCBIN_HDR
#include <limits.h>
#if defined(__AVX512BW__) || \
defined(__AVX512CD__) || \
defined(__AVX512DQ__) || \
defined(__AVX512ER__) || \
defined(__AVX512PF__) || \
defined(__AVX512VL__) || \
defined(__AVX512F__)
# define INCBIN_ALIGNMENT_INDEX 6
#elif defined(__AVX__) || \
defined(__AVX2__)
# define INCBIN_ALIGNMENT_INDEX 5
#elif defined(__SSE__) || \
defined(__SSE2__) || \
defined(__SSE3__) || \
defined(__SSSE3__) || \
defined(__SSE4_1__) || \
defined(__SSE4_2__) || \
defined(__neon__)
# define INCBIN_ALIGNMENT_INDEX 4
#elif ULONG_MAX != 0xffffffffu
# define INCBIN_ALIGNMENT_INDEX 3
# else
# define INCBIN_ALIGNMENT_INDEX 2
#endif
/* Lookup table of (1 << n) where `n' is `INCBIN_ALIGNMENT_INDEX' */
#define INCBIN_ALIGN_SHIFT_0 1
#define INCBIN_ALIGN_SHIFT_1 2
#define INCBIN_ALIGN_SHIFT_2 4
#define INCBIN_ALIGN_SHIFT_3 8
#define INCBIN_ALIGN_SHIFT_4 16
#define INCBIN_ALIGN_SHIFT_5 32
#define INCBIN_ALIGN_SHIFT_6 64
/* Actual alignment value */
#define INCBIN_ALIGNMENT \
INCBIN_CONCATENATE( \
INCBIN_CONCATENATE(INCBIN_ALIGN_SHIFT, _), \
INCBIN_ALIGNMENT_INDEX)
/* Stringize */
#define INCBIN_STR(X) \
#X
#define INCBIN_STRINGIZE(X) \
INCBIN_STR(X)
/* Concatenate */
#define INCBIN_CAT(X, Y) \
X ## Y
#define INCBIN_CONCATENATE(X, Y) \
INCBIN_CAT(X, Y)
/* Deferred macro expansion */
#define INCBIN_EVAL(X) \
X
#define INCBIN_INVOKE(N, ...) \
INCBIN_EVAL(N(__VA_ARGS__))
/* Green Hills uses a different directive for including binary data */
#if defined(__ghs__)
# if (__ghs_asm == 2)
# define INCBIN_MACRO ".file"
/* Or consider the ".myrawdata" entry in the ld file */
# else
# define INCBIN_MACRO "\tINCBIN"
# endif
#else
# define INCBIN_MACRO ".incbin"
#endif
#ifndef _MSC_VER
# define INCBIN_ALIGN \
__attribute__((aligned(INCBIN_ALIGNMENT)))
#else
# define INCBIN_ALIGN __declspec(align(INCBIN_ALIGNMENT))
#endif
#if defined(__arm__) || /* GNU C and RealView */ \
defined(__arm) || /* Diab */ \
defined(_ARM) /* ImageCraft */
# define INCBIN_ARM
#endif
#ifdef __GNUC__
/* Utilize .balign where supported */
# define INCBIN_ALIGN_HOST ".balign " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
# define INCBIN_ALIGN_BYTE ".balign 1\n"
#elif defined(INCBIN_ARM)
/*
* On arm assemblers, the alignment value is calculated as (1 << n) where `n' is
* the shift count. This is the value passed to `.align'
*/
# define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT_INDEX) "\n"
# define INCBIN_ALIGN_BYTE ".align 0\n"
#else
/* We assume other inline assembler's treat `.align' as `.balign' */
# define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
# define INCBIN_ALIGN_BYTE ".align 1\n"
#endif
/* INCBIN_CONST is used by incbin.c generated files */
#if defined(__cplusplus)
# define INCBIN_EXTERNAL extern "C"
# define INCBIN_CONST extern const
#else
# define INCBIN_EXTERNAL extern
# define INCBIN_CONST const
#endif
/**
* @brief Optionally override the linker section into which data is emitted.
*
* @warning If you use this facility, you'll have to deal with platform-specific linker output
* section naming on your own
*
* Overriding the default linker output section, e.g for esp8266/Arduino:
* @code
* #define INCBIN_OUTPUT_SECTION ".irom.text"
* #include "incbin.h"
* INCBIN(Foo, "foo.txt");
* // Data is emitted into program memory that never gets copied to RAM
* @endcode
*/
#if !defined(INCBIN_OUTPUT_SECTION)
# if defined(__APPLE__)
# define INCBIN_OUTPUT_SECTION ".const_data"
# else
# define INCBIN_OUTPUT_SECTION ".rodata"
# endif
#endif
#if defined(__APPLE__)
/* The directives are different for Apple branded compilers */
# define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n"
# define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
# define INCBIN_INT ".long "
# define INCBIN_MANGLE "_"
# define INCBIN_BYTE ".byte "
# define INCBIN_TYPE(...)
#else
# define INCBIN_SECTION ".section " INCBIN_OUTPUT_SECTION "\n"
# define INCBIN_GLOBAL(NAME) ".global " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
# if defined(__ghs__)
# define INCBIN_INT ".word "
# else
# define INCBIN_INT ".int "
# endif
# if defined(__USER_LABEL_PREFIX__)
# define INCBIN_MANGLE INCBIN_STRINGIZE(__USER_LABEL_PREFIX__)
# else
# define INCBIN_MANGLE ""
# endif
# if defined(INCBIN_ARM)
/* On arm assemblers, `@' is used as a line comment token */
# define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", %object\n"
# elif defined(__MINGW32__) || defined(__MINGW64__)
/* Mingw doesn't support this directive either */
# define INCBIN_TYPE(NAME)
# else
/* It's safe to use `@' on other architectures */
# define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", @object\n"
# endif
# define INCBIN_BYTE ".byte "
#endif
/* List of style types used for symbol names */
#define INCBIN_STYLE_CAMEL 0
#define INCBIN_STYLE_SNAKE 1
/**
* @brief Specify the prefix to use for symbol names.
*
* By default this is `g', producing symbols of the form:
* @code
* #include "incbin.h"
* INCBIN(Foo, "foo.txt");
*
* // Now you have the following symbols:
* // const unsigned char gFooData[];
* // const unsigned char *const gFooEnd;
* // const unsigned int gFooSize;
* @endcode
*
* If however you specify a prefix before including: e.g:
* @code
* #define INCBIN_PREFIX incbin
* #include "incbin.h"
* INCBIN(Foo, "foo.txt");
*
* // Now you have the following symbols instead:
* // const unsigned char incbinFooData[];
* // const unsigned char *const incbinFooEnd;
* // const unsigned int incbinFooSize;
* @endcode
*/
#if !defined(INCBIN_PREFIX)
# define INCBIN_PREFIX g
#endif
/**
* @brief Specify the style used for symbol names.
*
* Possible options are
* - INCBIN_STYLE_CAMEL "CamelCase"
* - INCBIN_STYLE_SNAKE "snake_case"
*
* Default option is *INCBIN_STYLE_CAMEL* producing symbols of the form:
* @code
* #include "incbin.h"
* INCBIN(Foo, "foo.txt");
*
* // Now you have the following symbols:
* // const unsigned char <prefix>FooData[];
* // const unsigned char *const <prefix>FooEnd;
* // const unsigned int <prefix>FooSize;
* @endcode
*
* If however you specify a style before including: e.g:
* @code
* #define INCBIN_STYLE INCBIN_STYLE_SNAKE
* #include "incbin.h"
* INCBIN(foo, "foo.txt");
*
* // Now you have the following symbols:
* // const unsigned char <prefix>foo_data[];
* // const unsigned char *const <prefix>foo_end;
* // const unsigned int <prefix>foo_size;
* @endcode
*/
#if !defined(INCBIN_STYLE)
# define INCBIN_STYLE INCBIN_STYLE_CAMEL
#endif
/* Style lookup tables */
#define INCBIN_STYLE_0_DATA Data
#define INCBIN_STYLE_0_END End
#define INCBIN_STYLE_0_SIZE Size
#define INCBIN_STYLE_1_DATA _data
#define INCBIN_STYLE_1_END _end
#define INCBIN_STYLE_1_SIZE _size
/* Style lookup: returning identifier */
#define INCBIN_STYLE_IDENT(TYPE) \
INCBIN_CONCATENATE( \
INCBIN_STYLE_, \
INCBIN_CONCATENATE( \
INCBIN_EVAL(INCBIN_STYLE), \
INCBIN_CONCATENATE(_, TYPE)))
/* Style lookup: returning string literal */
#define INCBIN_STYLE_STRING(TYPE) \
INCBIN_STRINGIZE( \
INCBIN_STYLE_IDENT(TYPE)) \
/* Generate the global labels by indirectly invoking the macro with our style
* type and concatenating the name against them. */
#define INCBIN_GLOBAL_LABELS(NAME, TYPE) \
INCBIN_INVOKE( \
INCBIN_GLOBAL, \
INCBIN_CONCATENATE( \
NAME, \
INCBIN_INVOKE( \
INCBIN_STYLE_IDENT, \
TYPE))) \
INCBIN_INVOKE( \
INCBIN_TYPE, \
INCBIN_CONCATENATE( \
NAME, \
INCBIN_INVOKE( \
INCBIN_STYLE_IDENT, \
TYPE)))
/**
* @brief Externally reference binary data included in another translation unit.
*
* Produces three external symbols that reference the binary data included in
* another translation unit.
*
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
* "Data", as well as "End" and "Size" after. An example is provided below.
*
* @param NAME The name given for the binary data
*
* @code
* INCBIN_EXTERN(Foo);
*
* // Now you have the following symbols:
* // extern const unsigned char <prefix>FooData[];
* // extern const unsigned char *const <prefix>FooEnd;
* // extern const unsigned int <prefix>FooSize;
* @endcode
*/
#define INCBIN_EXTERN(NAME) \
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char \
INCBIN_CONCATENATE( \
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
INCBIN_STYLE_IDENT(DATA))[]; \
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char *const \
INCBIN_CONCATENATE( \
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
INCBIN_STYLE_IDENT(END)); \
INCBIN_EXTERNAL const unsigned int \
INCBIN_CONCATENATE( \
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
INCBIN_STYLE_IDENT(SIZE))
/**
* @brief Include a binary file into the current translation unit.
*
* Includes a binary file into the current translation unit, producing three symbols
* for objects that encode the data and size respectively.
*
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
* "Data", as well as "End" and "Size" after. An example is provided below.
*
* @param NAME The name to associate with this binary data (as an identifier.)
* @param FILENAME The file to include (as a string literal.)
*
* @code
* INCBIN(Icon, "icon.png");
*
* // Now you have the following symbols:
* // const unsigned char <prefix>IconData[];
* // const unsigned char *const <prefix>IconEnd;
* // const unsigned int <prefix>IconSize;
* @endcode
*
* @warning This must be used in global scope
* @warning The identifiers may be different if INCBIN_STYLE is not default
*
* To externally reference the data included by this in another translation unit
* please @see INCBIN_EXTERN.
*/
#ifdef _MSC_VER
#define INCBIN(NAME, FILENAME) \
INCBIN_EXTERN(NAME)
#else
#define INCBIN(NAME, FILENAME) \
__asm__(INCBIN_SECTION \
INCBIN_GLOBAL_LABELS(NAME, DATA) \
INCBIN_ALIGN_HOST \
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) ":\n" \
INCBIN_MACRO " \"" FILENAME "\"\n" \
INCBIN_GLOBAL_LABELS(NAME, END) \
INCBIN_ALIGN_BYTE \
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) ":\n" \
INCBIN_BYTE "1\n" \
INCBIN_GLOBAL_LABELS(NAME, SIZE) \
INCBIN_ALIGN_HOST \
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(SIZE) ":\n" \
INCBIN_INT INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) " - " \
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) "\n" \
INCBIN_ALIGN_HOST \
".text\n" \
); \
INCBIN_EXTERN(NAME)
#endif
#endif
+7 -5
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -21,18 +21,20 @@
#include "bitboard.h" #include "bitboard.h"
#include "endgame.h" #include "endgame.h"
#include "position.h" #include "position.h"
#include "psqt.h"
#include "search.h" #include "search.h"
#include "syzygy/tbprobe.h"
#include "thread.h" #include "thread.h"
#include "tt.h" #include "tt.h"
#include "uci.h" #include "uci.h"
#include "syzygy/tbprobe.h"
namespace PSQT {
void init();
}
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
std::cout << engine_info() << std::endl; std::cout << engine_info() << std::endl;
CommandLine::init(argc, argv);
UCI::init(Options); UCI::init(Options);
Tune::init(); Tune::init();
PSQT::init(); PSQT::init();
@@ -42,7 +44,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::NNUE::init(); Eval::init_NNUE();
UCI::loop(argc, argv); UCI::loop(argc, argv);
+23 -28
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -25,36 +25,31 @@
using namespace std; using namespace std;
namespace { namespace {
#define S(mg, eg) make_score(mg, eg)
// Polynomial material imbalance parameters // Polynomial material imbalance parameters
// One Score parameter for each pair (our piece, another of our pieces) constexpr int QuadraticOurs[][PIECE_TYPE_NB] = {
constexpr Score QuadraticOurs[][PIECE_TYPE_NB] = { // OUR PIECES
// OUR PIECE 2 // pair pawn knight bishop rook queen
// bishop pair pawn knight bishop rook queen {1438 }, // Bishop pair
{S(1419, 1455) }, // Bishop pair { 40, 38 }, // Pawn
{S( 101, 28), S( 37, 39) }, // Pawn { 32, 255, -62 }, // Knight OUR PIECES
{S( 57, 64), S(249, 187), S(-49, -62) }, // Knight OUR PIECE 1 { 0, 104, 4, 0 }, // Bishop
{S( 0, 0), S(118, 137), S( 10, 27), S( 0, 0) }, // Bishop { -26, -2, 47, 105, -208 }, // Rook
{S( -63, -68), S( -5, 3), S(100, 81), S(132, 118), S(-246, -244) }, // Rook {-189, 24, 117, 133, -134, -6 } // Queen
{S(-210, -211), S( 37, 14), S(147, 141), S(161, 105), S(-158, -174), S(-9,-31) } // Queen
}; };
// One Score parameter for each pair (our piece, their piece) constexpr int QuadraticTheirs[][PIECE_TYPE_NB] = {
constexpr Score QuadraticTheirs[][PIECE_TYPE_NB] = { // THEIR PIECES
// THEIR PIECE // pair pawn knight bishop rook queen
// bishop pair pawn knight bishop rook queen { }, // Bishop pair
{ }, // Bishop pair { 36, }, // Pawn
{S( 33, 30) }, // Pawn { 9, 63, }, // Knight OUR PIECES
{S( 46, 18), S(106, 84) }, // Knight OUR PIECE { 59, 65, 42, }, // Bishop
{S( 75, 35), S( 59, 44), S( 60, 15) }, // Bishop { 46, 39, 24, -24, }, // Rook
{S( 26, 35), S( 6, 22), S( 38, 39), S(-12, -2) }, // Rook { 97, 100, -42, 137, 268, } // Queen
{S( 97, 93), S(100, 163), S(-58, -91), S(112, 192), S(276, 225) } // Queen
}; };
#undef S
// Endgame evaluation and scaling functions are accessed directly and not through // Endgame evaluation and scaling functions are accessed directly and not through
// the function maps because they correspond to more than one material hash key. // the function maps because they correspond to more than one material hash key.
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) }; Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
@@ -87,11 +82,11 @@ namespace {
/// piece type for both colors. /// piece type for both colors.
template<Color Us> template<Color Us>
Score imbalance(const int pieceCount[][PIECE_TYPE_NB]) { int imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
constexpr Color Them = ~Us; constexpr Color Them = ~Us;
Score bonus = SCORE_ZERO; int bonus = 0;
// Second-degree polynomial material imbalance, by Tord Romstad // Second-degree polynomial material imbalance, by Tord Romstad
for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1) for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
@@ -135,7 +130,7 @@ Entry* probe(const Position& pos) {
Value npm_w = pos.non_pawn_material(WHITE); Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK); Value npm_b = pos.non_pawn_material(BLACK);
Value npm = std::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit); Value npm = Utility::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
// Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME] // Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME]
e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit)); e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
@@ -218,7 +213,7 @@ Entry* probe(const Position& pos) {
{ pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK), { pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK),
pos.count<BISHOP>(BLACK) , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } }; pos.count<BISHOP>(BLACK) , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } };
e->score = (imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16; e->value = int16_t((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
return e; return e;
} }
+5 -5
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -37,8 +37,8 @@ namespace Material {
struct Entry { struct Entry {
Score imbalance() const { return score; } Score imbalance() const { return make_score(value, value); }
Phase game_phase() const { return (Phase)gamePhase; } Phase game_phase() const { return gamePhase; }
bool specialized_eval_exists() const { return evaluationFunction != nullptr; } bool specialized_eval_exists() const { return evaluationFunction != nullptr; }
Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); } Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); }
@@ -57,9 +57,9 @@ struct Entry {
const EndgameBase<Value>* evaluationFunction; const EndgameBase<Value>* evaluationFunction;
const EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each const EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each
// side (e.g. KPKP, KBPsK) // side (e.g. KPKP, KBPsK)
Score score; int16_t value;
int16_t gamePhase;
uint8_t factor[COLOR_NB]; uint8_t factor[COLOR_NB];
Phase gamePhase;
}; };
typedef HashTable<Entry, 8192> Table; typedef HashTable<Entry, 8192> Table;
+58 -113
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -51,11 +51,6 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
#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"
@@ -65,7 +60,7 @@ namespace {
/// Version number. If Version is left empty, then compile date in the format /// Version number. If Version is left empty, then compile date in the format
/// DD-MM-YY and show in engine_info. /// DD-MM-YY and show in engine_info.
const string Version = "13"; const string Version = "";
/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
@@ -132,7 +127,6 @@ public:
} // namespace } // namespace
/// engine_info() returns the full name of the current Stockfish version. This /// engine_info() returns the full name of the current Stockfish version. This
/// will be either "Stockfish <Tag> DD-MM-YY" (where DD-MM-YY is the date when /// will be either "Stockfish <Tag> DD-MM-YY" (where DD-MM-YY is the date when
/// the program was compiled) or "Stockfish <Version>", depending on whether /// the program was compiled) or "Stockfish <Version>", depending on whether
@@ -220,33 +214,26 @@ const std::string compiler_info() {
compiler += "\nCompilation settings include: "; compiler += "\nCompilation settings include: ";
compiler += (Is64Bit ? " 64bit" : " 32bit"); compiler += (Is64Bit ? " 64bit" : " 32bit");
#if defined(USE_VNNI)
compiler += " VNNI";
#endif
#if defined(USE_AVX512) #if defined(USE_AVX512)
compiler += " AVX512"; compiler += " AVX512";
#endif #endif
compiler += (HasPext ? " BMI2" : "");
#if defined(USE_AVX2) #if defined(USE_AVX2)
compiler += " AVX2"; compiler += " AVX2";
#endif #endif
#if defined(USE_SSE42)
compiler += " SSE42";
#endif
#if defined(USE_SSE41) #if defined(USE_SSE41)
compiler += " SSE41"; compiler += " SSE41";
#endif #endif
#if defined(USE_SSSE3) #if defined(USE_SSSE3)
compiler += " SSSE3"; compiler += " SSSE3";
#endif #endif
#if defined(USE_SSE2) #if defined(USE_SSE3)
compiler += " SSE2"; compiler += " SSE3";
#endif #endif
compiler += (HasPopCnt ? " POPCNT" : ""); compiler += (HasPext ? " BMI2" : "");
#if defined(USE_MMX) compiler += (HasPopCnt ? " POPCNT" : "");
compiler += " MMX";
#endif
#if defined(USE_NEON)
compiler += " NEON";
#endif
#if !defined(NDEBUG) #if !defined(NDEBUG)
compiler += " DEBUG"; compiler += " DEBUG";
#endif #endif
@@ -329,16 +316,13 @@ void prefetch(void* addr) {
#endif #endif
/// Wrappers for systems where the c++17 implementation doesn't guarantee the availability of aligned_alloc.
/// std_aligned_alloc() is our wrapper for systems where the c++17 implementation /// Memory allocated with std_aligned_alloc must be freed with std_aligned_free.
/// does not 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) { void* std_aligned_alloc(size_t alignment, size_t size) {
#if defined(__APPLE__)
#if defined(POSIXALIGNEDALLOC) return aligned_alloc(alignment, size);
void *mem;
return posix_memalign(&mem, alignment, size) ? nullptr : mem;
#elif defined(_WIN32) #elif defined(_WIN32)
return _mm_malloc(size, alignment); return _mm_malloc(size, alignment);
#else #else
@@ -347,8 +331,7 @@ void* std_aligned_alloc(size_t alignment, size_t size) {
} }
void std_aligned_free(void* ptr) { void std_aligned_free(void* ptr) {
#if defined(__APPLE__)
#if defined(POSIXALIGNEDALLOC)
free(ptr); free(ptr);
#elif defined(_WIN32) #elif defined(_WIN32)
_mm_free(ptr); _mm_free(ptr);
@@ -357,11 +340,25 @@ void std_aligned_free(void* ptr) {
#endif #endif
} }
/// aligned_large_pages_alloc() will return suitably aligned memory, if possible using 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
/// to be passed to free. With c++17 some of this functionality could be simplified.
#if defined(_WIN32) #if defined(__linux__) && !defined(__ANDROID__)
static void* aligned_large_pages_alloc_win(size_t allocSize) { void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page sizes
size_t size = ((allocSize + alignment - 1) / alignment) * alignment; // multiple of alignment
if (posix_memalign(&mem, alignment, size))
mem = nullptr;
madvise(mem, allocSize, MADV_HUGEPAGE);
return mem;
}
#elif defined(_WIN64)
static void* aligned_ttmem_alloc_large_pages(size_t allocSize) {
HANDLE hProcessToken { }; HANDLE hProcessToken { };
LUID luid { }; LUID luid { };
@@ -406,10 +403,23 @@ static void* aligned_large_pages_alloc_win(size_t allocSize) {
return mem; return mem;
} }
void* aligned_large_pages_alloc(size_t allocSize) { void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
static bool firstCall = true;
// Try to allocate large pages // Try to allocate large pages
void* mem = aligned_large_pages_alloc_win(allocSize); mem = aligned_ttmem_alloc_large_pages(allocSize);
// Suppress info strings on the first call. The first call occurs before 'uci'
// is received and in that case this output confuses some GUIs.
if (!firstCall)
{
if (mem)
sync_cout << "info string Hash table allocation: Windows large pages used." << sync_endl;
else
sync_cout << "info string Hash table allocation: Windows large pages not used." << sync_endl;
}
firstCall = false;
// Fall back to regular, page aligned, allocation if necessary // Fall back to regular, page aligned, allocation if necessary
if (!mem) if (!mem)
@@ -420,31 +430,23 @@ void* aligned_large_pages_alloc(size_t allocSize) {
#else #else
void* aligned_large_pages_alloc(size_t allocSize) { void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
#if defined(__linux__) constexpr size_t alignment = 64; // assumed cache line size
constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page size size_t size = allocSize + alignment - 1; // allocate some extra space
#else mem = malloc(size);
constexpr size_t alignment = 4096; // assumed small page size void* ret = reinterpret_cast<void*>((uintptr_t(mem) + alignment - 1) & ~uintptr_t(alignment - 1));
#endif return ret;
// round up to multiples of alignment
size_t size = ((allocSize + alignment - 1) / alignment) * alignment;
void *mem = std_aligned_alloc(alignment, size);
#if defined(MADV_HUGEPAGE)
madvise(mem, size, MADV_HUGEPAGE);
#endif
return mem;
} }
#endif #endif
/// aligned_large_pages_free() will free the previously allocated ttmem /// aligned_ttmem_free() will free the previously allocated ttmem
#if defined(_WIN32) #if defined(_WIN64)
void aligned_large_pages_free(void* mem) { void aligned_ttmem_free(void* mem) {
if (mem && !VirtualFree(mem, 0, MEM_RELEASE)) if (mem && !VirtualFree(mem, 0, MEM_RELEASE))
{ {
@@ -457,8 +459,8 @@ void aligned_large_pages_free(void* mem) {
#else #else
void aligned_large_pages_free(void *mem) { void aligned_ttmem_free(void *mem) {
std_aligned_free(mem); free(mem);
} }
#endif #endif
@@ -569,60 +571,3 @@ void bindThisThread(size_t idx) {
#endif #endif
} // namespace WinProcGroup } // namespace WinProcGroup
#ifdef _WIN32
#include <direct.h>
#define GETCWD _getcwd
#else
#include <unistd.h>
#define GETCWD getcwd
#endif
namespace CommandLine {
string argv0; // path+name of the executable binary, as given by argv[0]
string binaryDirectory; // path of the executable directory
string workingDirectory; // path of the working directory
void init(int argc, char* argv[]) {
(void)argc;
string pathSeparator;
// extract the path+name of the executable binary
argv0 = argv[0];
#ifdef _WIN32
pathSeparator = "\\";
#ifdef _MSC_VER
// Under windows argv[0] may not have the extension. Also _get_pgmptr() had
// issues in some windows 10 versions, so check returned values carefully.
char* pgmptr = nullptr;
if (!_get_pgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr)
argv0 = pgmptr;
#endif
#else
pathSeparator = "/";
#endif
// extract the working directory
workingDirectory = "";
char buff[40000];
char* cwd = GETCWD(buff, 40000);
if (cwd)
workingDirectory = cwd;
// extract the binary directory path from argv0
binaryDirectory = argv0;
size_t pos = binaryDirectory.find_last_of("\\/");
if (pos == std::string::npos)
binaryDirectory = "." + pathSeparator;
else
binaryDirectory.resize(pos + 1);
// pattern replacement: "./" at the start of path is replaced by the working directory
if (binaryDirectory.find("." + pathSeparator) == 0)
binaryDirectory.replace(0, 1, workingDirectory);
}
} // namespace CommandLine
+11 -20
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -24,7 +24,6 @@
#include <ostream> #include <ostream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <cstdint>
#include "types.h" #include "types.h"
@@ -34,8 +33,8 @@ 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_alloc(size_t alignment, size_t size);
void std_aligned_free(void* ptr); void std_aligned_free(void* ptr);
void* aligned_large_pages_alloc(size_t size); // memory aligned by page size, min alignment: 4096 bytes void* aligned_ttmem_alloc(size_t size, void*& mem);
void aligned_large_pages_free(void* mem); // nop if mem == nullptr void aligned_ttmem_free(void* mem); // nop if mem == nullptr
void dbg_hit_on(bool b); void dbg_hit_on(bool b);
void dbg_hit_on(bool c, bool b); void dbg_hit_on(bool c, bool b);
@@ -43,7 +42,9 @@ void dbg_mean_of(int v);
void dbg_print(); void dbg_print();
typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds
static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits"); static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits");
inline TimePoint now() { inline TimePoint now() {
return std::chrono::duration_cast<std::chrono::milliseconds> return std::chrono::duration_cast<std::chrono::milliseconds>
(std::chrono::steady_clock::now().time_since_epoch()).count(); (std::chrono::steady_clock::now().time_since_epoch()).count();
@@ -64,16 +65,13 @@ std::ostream& operator<<(std::ostream&, SyncCout);
#define sync_cout std::cout << IO_LOCK #define sync_cout std::cout << IO_LOCK
#define sync_endl std::endl << IO_UNLOCK #define sync_endl std::endl << IO_UNLOCK
// `ptr` must point to an array of size at least namespace Utility {
// `sizeof(T) * N + alignment` bytes, where `N` is the
// number of elements in the array. /// Clamp a value between lo and hi. Available in c++17.
template <uintptr_t Alignment, typename T> template<class T> constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
T* align_ptr_up(T* ptr) return v < lo ? lo : v > hi ? hi : v;
{ }
static_assert(alignof(T) < Alignment);
const uintptr_t ptrint = reinterpret_cast<uintptr_t>(reinterpret_cast<char*>(ptr));
return reinterpret_cast<T*>(reinterpret_cast<char*>((ptrint + (Alignment - 1)) / Alignment * Alignment));
} }
/// xorshift64star Pseudo-Random Number Generator /// xorshift64star Pseudo-Random Number Generator
@@ -136,11 +134,4 @@ namespace WinProcGroup {
void bindThisThread(size_t idx); void bindThisThread(size_t idx);
} }
namespace CommandLine {
void init(int argc, char* argv[]);
extern std::string binaryDirectory; // path of the executable directory
extern std::string workingDirectory; // path of the working directory
}
#endif // #ifndef MISC_H_INCLUDED #endif // #ifndef MISC_H_INCLUDED
+37 -34
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -85,7 +85,7 @@ namespace {
// Add pawn pushes which give discovered check. This is possible only // Add pawn pushes which give discovered check. This is possible only
// if the pawn is not on the same file as the enemy king, because we // if the pawn is not on the same file as the enemy king, because we
// don't generate captures. Note that a possible discovered check // don't generate captures. Note that a possible discovery check
// promotion has been already generated amongst the captures. // promotion has been already generated amongst the captures.
Bitboard dcCandidateQuiets = pos.blockers_for_king(Them) & pawnsNotOn7; Bitboard dcCandidateQuiets = pos.blockers_for_king(Them) & pawnsNotOn7;
if (dcCandidateQuiets) if (dcCandidateQuiets)
@@ -134,7 +134,7 @@ namespace {
moveList = make_promotions<Type, Up >(moveList, pop_lsb(&b3), ksq); moveList = make_promotions<Type, Up >(moveList, pop_lsb(&b3), ksq);
} }
// Standard and en passant captures // Standard and en-passant captures
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{ {
Bitboard b1 = shift<UpRight>(pawnsNotOn7) & enemies; Bitboard b1 = shift<UpRight>(pawnsNotOn7) & enemies;
@@ -156,8 +156,10 @@ namespace {
{ {
assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6)); assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6));
// An en passant capture cannot resolve a discovered check. // An en passant capture can be an evasion only if the checking piece
if (Type == EVASIONS && (target & (pos.ep_square() + Up))) // is the double pushed pawn and so is in the target. Otherwise this
// is a discovery check and we are forced to do otherwise.
if (Type == EVASIONS && !(target & (pos.ep_square() - Up)))
return moveList; return moveList;
b1 = pawnsNotOn7 & pawn_attacks_bb(Them, pos.ep_square()); b1 = pawnsNotOn7 & pawn_attacks_bb(Them, pos.ep_square());
@@ -165,7 +167,7 @@ namespace {
assert(b1); assert(b1);
while (b1) while (b1)
*moveList++ = make<EN_PASSANT>(pop_lsb(&b1), pos.ep_square()); *moveList++ = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
} }
} }
@@ -173,24 +175,29 @@ namespace {
} }
template<PieceType Pt, bool Checks> template<Color Us, PieceType Pt, bool Checks>
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard piecesToMove, Bitboard target) { ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()");
Bitboard bb = piecesToMove & pos.pieces(Pt); const Square* pl = pos.squares<Pt>(Us);
if (!bb) for (Square from = *pl; from != SQ_NONE; from = *++pl)
return moveList; {
if (Checks)
{
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
&& !(attacks_bb<Pt>(from) & target & pos.check_squares(Pt)))
continue;
[[maybe_unused]] const Bitboard checkSquares = pos.check_squares(Pt); if (pos.blockers_for_king(~Us) & from)
continue;
while (bb) { }
Square from = pop_lsb(&bb);
Bitboard b = attacks_bb<Pt>(from, pos.pieces()) & target; Bitboard b = attacks_bb<Pt>(from, pos.pieces()) & target;
if constexpr (Checks)
b &= checkSquares; if (Checks)
b &= pos.check_squares(Pt);
while (b) while (b)
*moveList++ = make_move(from, pop_lsb(&b)); *moveList++ = make_move(from, pop_lsb(&b));
@@ -202,14 +209,8 @@ namespace {
template<Color Us, GenType Type> template<Color Us, GenType Type>
ExtMove* generate_all(const Position& pos, ExtMove* moveList) { ExtMove* generate_all(const Position& pos, ExtMove* moveList) {
constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations
static_assert(Type != LEGAL, "Unsupported type in generate_all()"); Bitboard target;
constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations
Bitboard target, piecesToMove = pos.pieces(Us);
if(Type == QUIET_CHECKS)
piecesToMove &= ~pos.blockers_for_king(~Us);
switch (Type) switch (Type)
{ {
@@ -229,13 +230,15 @@ namespace {
case NON_EVASIONS: case NON_EVASIONS:
target = ~pos.pieces(Us); target = ~pos.pieces(Us);
break; break;
default:
static_assert(true, "Unsupported type in generate_all()");
} }
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target); moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
moveList = generate_moves<KNIGHT, Checks>(pos, moveList, piecesToMove, target); moveList = generate_moves<Us, KNIGHT, Checks>(pos, moveList, target);
moveList = generate_moves<BISHOP, Checks>(pos, moveList, piecesToMove, target); moveList = generate_moves<Us, BISHOP, Checks>(pos, moveList, target);
moveList = generate_moves< ROOK, Checks>(pos, moveList, piecesToMove, target); moveList = generate_moves<Us, ROOK, Checks>(pos, moveList, target);
moveList = generate_moves< QUEEN, Checks>(pos, moveList, piecesToMove, target); moveList = generate_moves<Us, QUEEN, Checks>(pos, moveList, target);
if (Type != QUIET_CHECKS && Type != EVASIONS) if (Type != QUIET_CHECKS && Type != EVASIONS)
{ {
@@ -245,7 +248,7 @@ namespace {
*moveList++ = make_move(ksq, pop_lsb(&b)); *moveList++ = make_move(ksq, pop_lsb(&b));
if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING)) if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING))
for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } ) for(CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } )
if (!pos.castling_impeded(cr) && pos.can_castle(cr)) if (!pos.castling_impeded(cr) && pos.can_castle(cr))
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(cr)); *moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(cr));
} }
@@ -257,7 +260,7 @@ namespace {
/// <CAPTURES> Generates all pseudo-legal captures plus queen and checking knight promotions /// <CAPTURES> Generates all pseudo-legal captures plus queen and checking knight promotions
/// <QUIETS> Generates all pseudo-legal non-captures and underpromotions (except checking knight) /// <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.
@@ -280,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 giving check, /// generate<QUIET_CHECKS> generates all pseudo-legal non-captures.
/// except castling. 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) {
@@ -354,7 +357,7 @@ ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
moveList = pos.checkers() ? generate<EVASIONS >(pos, moveList) moveList = pos.checkers() ? generate<EVASIONS >(pos, moveList)
: generate<NON_EVASIONS>(pos, moveList); : generate<NON_EVASIONS>(pos, moveList);
while (cur != moveList) while (cur != moveList)
if ( ((pinned && pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT) if ( (pinned || from_sq(*cur) == ksq || type_of(*cur) == ENPASSANT)
&& !pos.legal(*cur)) && !pos.legal(*cur))
*cur = (--moveList)->move; *cur = (--moveList)->move;
else else
+1 -1
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
+17 -18
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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,9 +73,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
assert(d <= 0); assert(d <= 0);
stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) + stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) +
!( ttm !(ttm && (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare)
&& (pos.checkers() || depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) && pos.pseudo_legal(ttm));
&& pos.pseudo_legal(ttm));
} }
/// MovePicker constructor for ProbCut: we generate captures with SEE greater /// MovePicker constructor for ProbCut: we generate captures with SEE greater
@@ -99,15 +98,15 @@ void MovePicker::score() {
static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
for (auto& m : *this) for (auto& m : *this)
if constexpr (Type == CAPTURES) if (Type == CAPTURES)
m.value = int(PieceValue[MG][pos.piece_on(to_sq(m))]) * 6 m.value = int(PieceValue[MG][pos.piece_on(to_sq(m))]) * 6
+ (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]; + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))];
else if constexpr (Type == QUIETS) else if (Type == QUIETS)
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
+ 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
+ (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)] + 2 * (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
+ (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)] + 2 * (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]
+ (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)]
+ (ply < MAX_LPH ? std::min(4, depth / 3) * (*lowPlyHistory)[ply][from_to(m)] : 0); + (ply < MAX_LPH ? std::min(4, depth / 3) * (*lowPlyHistory)[ply][from_to(m)] : 0);
@@ -117,8 +116,8 @@ void MovePicker::score() {
m.value = PieceValue[MG][pos.piece_on(to_sq(m))] m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
- Value(type_of(pos.moved_piece(m))); - Value(type_of(pos.moved_piece(m)));
else else
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
+ 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
- (1 << 28); - (1 << 28);
} }
} }
@@ -142,7 +141,7 @@ Move MovePicker::select(Pred filter) {
} }
/// MovePicker::next_move() is the most important method of the MovePicker class. It /// MovePicker::next_move() is the most important method of the MovePicker class. It
/// returns a new pseudo-legal move every time it is called until there are no more /// returns a new pseudo legal move every time it is called until there are no more
/// moves left, picking the move with the highest score from a list of generated moves. /// moves left, picking the move with the highest score from a list of generated moves.
Move MovePicker::next_move(bool skipQuiets) { Move MovePicker::next_move(bool skipQuiets) {
@@ -183,7 +182,7 @@ top:
--endMoves; --endMoves;
++stage; ++stage;
[[fallthrough]]; /* fallthrough */
case REFUTATION: case REFUTATION:
if (select<Next>([&](){ return *cur != MOVE_NONE if (select<Next>([&](){ return *cur != MOVE_NONE
@@ -191,7 +190,7 @@ top:
&& pos.pseudo_legal(*cur); })) && pos.pseudo_legal(*cur); }))
return *(cur - 1); return *(cur - 1);
++stage; ++stage;
[[fallthrough]]; /* fallthrough */
case QUIET_INIT: case QUIET_INIT:
if (!skipQuiets) if (!skipQuiets)
@@ -204,7 +203,7 @@ top:
} }
++stage; ++stage;
[[fallthrough]]; /* fallthrough */
case QUIET: case QUIET:
if ( !skipQuiets if ( !skipQuiets
@@ -218,7 +217,7 @@ top:
endMoves = endBadCaptures; endMoves = endBadCaptures;
++stage; ++stage;
[[fallthrough]]; /* fallthrough */
case BAD_CAPTURE: case BAD_CAPTURE:
return select<Next>([](){ return true; }); return select<Next>([](){ return true; });
@@ -229,7 +228,7 @@ top:
score<EVASIONS>(); score<EVASIONS>();
++stage; ++stage;
[[fallthrough]]; /* fallthrough */
case EVASION: case EVASION:
return select<Best>([](){ return true; }); return select<Best>([](){ return true; });
@@ -247,14 +246,14 @@ top:
return MOVE_NONE; return MOVE_NONE;
++stage; ++stage;
[[fallthrough]]; /* fallthrough */
case QCHECK_INIT: case QCHECK_INIT:
cur = moves; cur = moves;
endMoves = generate<QUIET_CHECKS>(pos, cur); endMoves = generate<QUIET_CHECKS>(pos, cur);
++stage; ++stage;
[[fallthrough]]; /* fallthrough */
case QCHECK: case QCHECK:
return select<Next>([](){ return true; }); return select<Next>([](){ return true; });
+10 -10
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -84,11 +84,11 @@ enum StatsType { NoCaptures, Captures };
/// unsuccessful during the current search, and is used for reduction and move /// unsuccessful during the current search, and is used for reduction and move
/// ordering decisions. It uses 2 tables (one for each color) indexed by /// ordering decisions. It uses 2 tables (one for each color) indexed by
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
typedef Stats<int16_t, 13365, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory; typedef Stats<int16_t, 10692, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
/// At higher depths LowPlyHistory records successful quiet moves near the root /// At higher depths LowPlyHistory records successful quiet moves near the root and quiet
/// and quiet moves which are/were in the PV (ttPv). It is cleared with each new /// moves which are/were in the PV (ttPv)
/// search and filled during iterative deepening. /// It is cleared with each new search and filled during iterative deepening
constexpr int MAX_LPH = 4; constexpr int MAX_LPH = 4;
typedef Stats<int16_t, 10692, MAX_LPH, int(SQUARE_NB) * int(SQUARE_NB)> LowPlyHistory; typedef Stats<int16_t, 10692, MAX_LPH, int(SQUARE_NB) * int(SQUARE_NB)> LowPlyHistory;
@@ -108,12 +108,12 @@ typedef Stats<int16_t, 29952, PIECE_NB, SQUARE_NB> PieceToHistory;
typedef Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB> ContinuationHistory; typedef Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB> ContinuationHistory;
/// MovePicker class is used to pick one pseudo-legal move at a time from the /// MovePicker class is used to pick one pseudo legal move at a time from the
/// current position. The most important method is next_move(), which returns a /// current position. The most important method is next_move(), which returns a
/// new pseudo-legal move each time it is called, until there are no moves left, /// new pseudo legal move each time it is called, until there are no moves left,
/// when MOVE_NONE is returned. In order to improve the efficiency of the /// when MOVE_NONE is returned. In order to improve the efficiency of the alpha
/// alpha-beta algorithm, MovePicker attempts to return the moves which are most /// beta algorithm, MovePicker attempts to return the moves which are most likely
/// likely to get a cut-off first. /// to get a cut-off first.
class MovePicker { class MovePicker {
enum PickType { Next, Best }; enum PickType { Next, Best };
+1 -1
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
+86 -52
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -18,6 +18,7 @@
// Code for calculating NNUE evaluation function // Code for calculating NNUE evaluation function
#include <fstream>
#include <iostream> #include <iostream>
#include <set> #include <set>
@@ -25,14 +26,35 @@
#include "../position.h" #include "../position.h"
#include "../misc.h" #include "../misc.h"
#include "../uci.h" #include "../uci.h"
#include "../types.h"
#include "evaluate_nnue.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 { namespace Eval::NNUE {
// Input feature converter // Input feature converter
LargePagePtr<FeatureTransformer> feature_transformer; AlignedPtr<FeatureTransformer> feature_transformer;
// Evaluation function // Evaluation function
AlignedPtr<Network> network; AlignedPtr<Network> network;
@@ -50,22 +72,14 @@ namespace Eval::NNUE {
std::memset(pointer.get(), 0, sizeof(T)); std::memset(pointer.get(), 0, sizeof(T));
} }
template <typename T>
void Initialize(LargePagePtr<T>& pointer) {
static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T");
pointer.reset(reinterpret_cast<T*>(aligned_large_pages_alloc(sizeof(T))));
std::memset(pointer.get(), 0, sizeof(T));
}
// Read evaluation function parameters // Read evaluation function parameters
template <typename T> template <typename T>
bool ReadParameters(std::istream& stream, T& reference) { bool ReadParameters(std::istream& stream, const AlignedPtr<T>& pointer) {
std::uint32_t header; std::uint32_t header;
header = read_little_endian<std::uint32_t>(stream); stream.read(reinterpret_cast<char*>(&header), sizeof(header));
if (!stream || header != T::GetHashValue()) return false; if (!stream || header != T::GetHashValue()) return false;
return reference.ReadParameters(stream); return pointer->ReadParameters(stream);
} }
} // namespace Detail } // namespace Detail
@@ -78,13 +92,13 @@ namespace Eval::NNUE {
} }
// Read network header // Read network header
bool ReadHeader(std::istream& stream, std::uint32_t* hash_value, std::string* architecture) 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); std::uint32_t version, size;
*hash_value = read_little_endian<std::uint32_t>(stream); stream.read(reinterpret_cast<char*>(&version), sizeof(version));
size = read_little_endian<std::uint32_t>(stream); stream.read(reinterpret_cast<char*>(hash_value), sizeof(*hash_value));
stream.read(reinterpret_cast<char*>(&size), sizeof(size));
if (!stream || version != kVersion) return false; if (!stream || version != kVersion) return false;
architecture->resize(size); architecture->resize(size);
stream.read(&(*architecture)[0], size); stream.read(&(*architecture)[0], size);
@@ -98,47 +112,67 @@ namespace Eval::NNUE {
std::string architecture; std::string architecture;
if (!ReadHeader(stream, &hash_value, &architecture)) return false; if (!ReadHeader(stream, &hash_value, &architecture)) return false;
if (hash_value != kHashValue) return false; if (hash_value != kHashValue) return false;
if (!Detail::ReadParameters(stream, *feature_transformer)) return false; if (!Detail::ReadParameters(stream, feature_transformer)) return false;
if (!Detail::ReadParameters(stream, *network)) return false; if (!Detail::ReadParameters(stream, network)) return false;
return stream && stream.peek() == std::ios::traits_type::eof(); 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. // Evaluation function. Perform differential calculation.
Value evaluate(const Position& pos) { Value evaluate(const Position& pos) {
Value v = ComputeScore(pos, false);
v = Utility::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
// We manually align the arrays on the stack because with gcc < 9.3 return v;
// overaligning stack variables with alignas() doesn't work correctly.
constexpr uint64_t alignment = kCacheLineSize;
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
TransformedFeatureType transformed_features_unaligned[
FeatureTransformer::kBufferSize + alignment / sizeof(TransformedFeatureType)];
char buffer_unaligned[Network::kBufferSize + alignment];
auto* transformed_features = align_ptr_up<alignment>(&transformed_features_unaligned[0]);
auto* buffer = align_ptr_up<alignment>(&buffer_unaligned[0]);
#else
alignas(alignment)
TransformedFeatureType transformed_features[FeatureTransformer::kBufferSize];
alignas(alignment) char buffer[Network::kBufferSize];
#endif
ASSERT_ALIGNED(transformed_features, alignment);
ASSERT_ALIGNED(buffer, alignment);
feature_transformer->Transform(pos, transformed_features);
const auto output = network->Propagate(transformed_features, buffer);
return static_cast<Value>(output[0] / FV_SCALE);
} }
// Load eval, from a file stream or a memory stream // Evaluation function. Perform full calculation.
bool load_eval(std::string name, std::istream& stream) { Value compute_eval(const Position& pos) {
return ComputeScore(pos, true);
}
Initialize(); // Proceed with the difference calculation if possible
fileName = name; void update_eval(const Position& pos) {
return ReadParameters(stream); UpdateAccumulatorIfPossible(pos);
} }
} // namespace Eval::NNUE } // namespace Eval::NNUE
+1 -12
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -40,20 +40,9 @@ namespace Eval::NNUE {
} }
}; };
template <typename T>
struct LargePageDeleter {
void operator()(T* ptr) const {
ptr->~T();
aligned_large_pages_free(ptr);
}
};
template <typename T> template <typename T>
using AlignedPtr = std::unique_ptr<T, AlignedDeleter<T>>; using AlignedPtr = std::unique_ptr<T, AlignedDeleter<T>>;
template <typename T>
using LargePagePtr = std::unique_ptr<T, LargePageDeleter<T>>;
} // namespace Eval::NNUE } // namespace Eval::NNUE
#endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED #endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED
+67 -1
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -43,6 +43,48 @@ namespace Eval::NNUE::Features {
template <typename Derived> template <typename Derived>
class FeatureSetBase { 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 // Class template that represents the feature set
@@ -62,6 +104,30 @@ namespace Eval::NNUE::Features {
CompileTimeList<TriggerEvent, FeatureType::kRefreshTrigger>; CompileTimeList<TriggerEvent, FeatureType::kRefreshTrigger>;
static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues; 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 } // namespace Eval::NNUE::Features
+1 -1
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
+44 -20
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -23,14 +23,25 @@
namespace Eval::NNUE::Features { namespace Eval::NNUE::Features {
// Orient a square according to perspective (rotates by 180 for black) // Find the index of the feature quantity from the king position and PieceSquare
inline Square orient(Color perspective, Square s) { template <Side AssociatedKing>
return Square(int(s) ^ (bool(perspective) * 63)); inline IndexType HalfKP<AssociatedKing>::MakeIndex(Square sq_k, PieceSquare p) {
return static_cast<IndexType>(PS_END) * static_cast<IndexType>(sq_k) + p;
} }
// Index of a feature for a given king position and another piece on some square // Get pieces information
inline IndexType make_index(Color perspective, Square s, Piece pc, Square ksq) { template <Side AssociatedKing>
return IndexType(orient(perspective, s) + kpp_board_index[perspective][pc] + PS_END * ksq); 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 // Get a list of indices for active features
@@ -38,28 +49,41 @@ namespace Eval::NNUE::Features {
void HalfKP<AssociatedKing>::AppendActiveIndices( void HalfKP<AssociatedKing>::AppendActiveIndices(
const Position& pos, Color perspective, IndexList* active) { const Position& pos, Color perspective, IndexList* active) {
Square ksq = orient(perspective, pos.square<KING>(perspective)); // Do nothing if array size is small to avoid compiler warning
Bitboard bb = pos.pieces() & ~pos.pieces(KING); if (RawFeatures::kMaxActiveDimensions < kMaxActiveDimensions) return;
while (bb) {
Square s = pop_lsb(&bb); PieceSquare* pieces;
active->push_back(make_index(perspective, s, pos.piece_on(s), ksq)); 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 // Get a list of indices for recently changed features
template <Side AssociatedKing> template <Side AssociatedKing>
void HalfKP<AssociatedKing>::AppendChangedIndices( void HalfKP<AssociatedKing>::AppendChangedIndices(
const Position& pos, const DirtyPiece& dp, Color perspective, const Position& pos, Color perspective,
IndexList* removed, IndexList* added) { IndexList* removed, IndexList* added) {
Square ksq = orient(perspective, pos.square<KING>(perspective)); 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) { for (int i = 0; i < dp.dirty_num; ++i) {
Piece pc = dp.piece[i]; if (dp.pieceId[i] >= PIECE_ID_KING) continue;
if (type_of(pc) == KING) continue; const auto old_p = static_cast<PieceSquare>(
if (dp.from[i] != SQ_NONE) dp.old_piece[i].from[perspective]);
removed->push_back(make_index(perspective, dp.from[i], pc, ksq)); if (old_p != PS_NONE) {
if (dp.to[i] != SQ_NONE) removed->push_back(MakeIndex(sq_target_k, old_p));
added->push_back(make_index(perspective, dp.to[i], pc, ksq)); }
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));
}
} }
} }
+11 -3
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -41,7 +41,7 @@ namespace Eval::NNUE::Features {
static constexpr IndexType kDimensions = static constexpr IndexType kDimensions =
static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_END); static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_END);
// Maximum number of simultaneously active features // Maximum number of simultaneously active features
static constexpr IndexType kMaxActiveDimensions = 30; // Kings don't count static constexpr IndexType kMaxActiveDimensions = PIECE_ID_KING;
// Trigger for full calculation instead of difference calculation // Trigger for full calculation instead of difference calculation
static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved; static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved;
@@ -50,8 +50,16 @@ namespace Eval::NNUE::Features {
IndexList* active); IndexList* active);
// Get a list of indices for recently changed features // Get a list of indices for recently changed features
static void AppendChangedIndices(const Position& pos, const DirtyPiece& dp, Color perspective, static void AppendChangedIndices(const Position& pos, Color perspective,
IndexList* removed, IndexList* added); 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 } // namespace Eval::NNUE::Features
+1 -1
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
+96 -345
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -41,11 +41,6 @@ namespace Eval::NNUE::Layers {
static constexpr IndexType kOutputDimensions = OutputDimensions; static constexpr IndexType kOutputDimensions = OutputDimensions;
static constexpr IndexType kPaddedInputDimensions = static constexpr IndexType kPaddedInputDimensions =
CeilToMultiple<IndexType>(kInputDimensions, kMaxSimdWidth); CeilToMultiple<IndexType>(kInputDimensions, kMaxSimdWidth);
#if defined (USE_AVX512)
static constexpr const IndexType kOutputSimdWidth = kSimdWidth / 2;
#elif defined (USE_SSSE3)
static constexpr const IndexType kOutputSimdWidth = kSimdWidth / 4;
#endif
// Size of forward propagation buffer used in this layer // Size of forward propagation buffer used in this layer
static constexpr std::size_t kSelfBufferSize = static constexpr std::size_t kSelfBufferSize =
@@ -67,61 +62,11 @@ namespace Eval::NNUE::Layers {
// Read network parameters // Read network parameters
bool ReadParameters(std::istream& stream) { bool ReadParameters(std::istream& stream) {
if (!previous_layer_.ReadParameters(stream)) return false; if (!previous_layer_.ReadParameters(stream)) return false;
for (std::size_t i = 0; i < kOutputDimensions; ++i) stream.read(reinterpret_cast<char*>(biases_),
biases_[i] = read_little_endian<BiasType>(stream); kOutputDimensions * sizeof(BiasType));
for (std::size_t i = 0; i < kOutputDimensions * kPaddedInputDimensions; ++i) stream.read(reinterpret_cast<char*>(weights_),
#if !defined (USE_SSSE3) kOutputDimensions * kPaddedInputDimensions *
weights_[i] = read_little_endian<WeightType>(stream); sizeof(WeightType));
#else
weights_[
(i / 4) % (kPaddedInputDimensions / 4) * kOutputDimensions * 4 +
i / kPaddedInputDimensions * 4 +
i % 4
] = read_little_endian<WeightType>(stream);
// Determine if eights of weight and input products can be summed using 16bits
// without saturation. We assume worst case combinations of 0 and 127 for all inputs.
if (kOutputDimensions > 1 && !stream.fail())
{
canSaturate16.count = 0;
#if !defined(USE_VNNI)
for (IndexType i = 0; i < kPaddedInputDimensions; i += 16)
for (IndexType j = 0; j < kOutputDimensions; ++j)
for (int x = 0; x < 2; ++x)
{
WeightType* w = &weights_[i * kOutputDimensions + j * 4 + x * 2];
int sum[2] = {0, 0};
for (int k = 0; k < 8; ++k)
{
IndexType idx = k / 2 * kOutputDimensions * 4 + k % 2;
sum[w[idx] < 0] += w[idx];
}
for (int sign : {-1, 1})
while (sign * sum[sign == -1] > 258)
{
int maxK = 0, maxW = 0;
for (int k = 0; k < 8; ++k)
{
IndexType idx = k / 2 * kOutputDimensions * 4 + k % 2;
if (maxW < sign * w[idx])
maxK = k, maxW = sign * w[idx];
}
IndexType idx = maxK / 2 * kOutputDimensions * 4 + maxK % 2;
sum[sign == -1] -= w[idx];
canSaturate16.add(j, i + maxK / 2 * 4 + maxK % 2 + x * 2, w[idx]);
w[idx] = 0;
}
}
// Non functional optimization for faster more linear access
std::sort(canSaturate16.ids, canSaturate16.ids + canSaturate16.count,
[](const typename CanSaturate::Entry& e1, const typename CanSaturate::Entry& e2)
{ return e1.in == e2.in ? e1.out < e2.out : e1.in < e2.in; });
#endif
}
#endif
return !stream.fail(); return !stream.fail();
} }
@@ -130,281 +75,109 @@ namespace Eval::NNUE::Layers {
const TransformedFeatureType* transformed_features, char* buffer) const { const TransformedFeatureType* transformed_features, char* buffer) const {
const auto input = previous_layer_.Propagate( const auto input = previous_layer_.Propagate(
transformed_features, buffer + kSelfBufferSize); transformed_features, buffer + kSelfBufferSize);
#if defined (USE_AVX512)
[[maybe_unused]] const __m512i kOnes512 = _mm512_set1_epi16(1);
[[maybe_unused]] auto m512_hadd = [](__m512i sum, int bias) -> int {
return _mm512_reduce_add_epi32(sum) + bias;
};
[[maybe_unused]] auto m512_add_dpbusd_epi32 = [=](__m512i& acc, __m512i a, __m512i b) {
#if defined (USE_VNNI)
acc = _mm512_dpbusd_epi32(acc, a, b);
#else
__m512i product0 = _mm512_maddubs_epi16(a, b);
product0 = _mm512_madd_epi16(product0, kOnes512);
acc = _mm512_add_epi32(acc, product0);
#endif
};
[[maybe_unused]] auto m512_add_dpbusd_epi32x4 = [=](__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512i b1,
__m512i a2, __m512i b2, __m512i a3, __m512i b3) {
#if defined (USE_VNNI)
acc = _mm512_dpbusd_epi32(acc, a0, b0);
acc = _mm512_dpbusd_epi32(acc, a1, b1);
acc = _mm512_dpbusd_epi32(acc, a2, b2);
acc = _mm512_dpbusd_epi32(acc, a3, b3);
#else
__m512i product0 = _mm512_maddubs_epi16(a0, b0);
__m512i product1 = _mm512_maddubs_epi16(a1, b1);
__m512i product2 = _mm512_maddubs_epi16(a2, b2);
__m512i product3 = _mm512_maddubs_epi16(a3, b3);
product0 = _mm512_add_epi16(product0, product1);
product2 = _mm512_add_epi16(product2, product3);
product0 = _mm512_add_epi16(product0, product2);
product0 = _mm512_madd_epi16(product0, kOnes512);
acc = _mm512_add_epi32(acc, product0);
#endif
};
#endif
#if defined (USE_AVX2)
[[maybe_unused]] const __m256i kOnes256 = _mm256_set1_epi16(1);
[[maybe_unused]] auto m256_hadd = [](__m256i sum, int bias) -> int {
__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));
return _mm_cvtsi128_si32(sum128) + bias;
};
[[maybe_unused]] auto m256_add_dpbusd_epi32 = [=](__m256i& acc, __m256i a, __m256i b) {
#if defined (USE_VNNI)
acc = _mm256_dpbusd_epi32(acc, a, b);
#else
__m256i product0 = _mm256_maddubs_epi16(a, b);
product0 = _mm256_madd_epi16(product0, kOnes256);
acc = _mm256_add_epi32(acc, product0);
#endif
};
[[maybe_unused]] auto m256_add_dpbusd_epi32x4 = [=](__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256i b1,
__m256i a2, __m256i b2, __m256i a3, __m256i b3) {
#if defined (USE_VNNI)
acc = _mm256_dpbusd_epi32(acc, a0, b0);
acc = _mm256_dpbusd_epi32(acc, a1, b1);
acc = _mm256_dpbusd_epi32(acc, a2, b2);
acc = _mm256_dpbusd_epi32(acc, a3, b3);
#else
__m256i product0 = _mm256_maddubs_epi16(a0, b0);
__m256i product1 = _mm256_maddubs_epi16(a1, b1);
__m256i product2 = _mm256_maddubs_epi16(a2, b2);
__m256i product3 = _mm256_maddubs_epi16(a3, b3);
product0 = _mm256_add_epi16(product0, product1);
product2 = _mm256_add_epi16(product2, product3);
product0 = _mm256_add_epi16(product0, product2);
product0 = _mm256_madd_epi16(product0, kOnes256);
acc = _mm256_add_epi32(acc, product0);
#endif
};
#endif
#if defined (USE_SSSE3)
[[maybe_unused]] const __m128i kOnes128 = _mm_set1_epi16(1);
[[maybe_unused]] auto m128_hadd = [](__m128i sum, int bias) -> int {
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
return _mm_cvtsi128_si32(sum) + bias;
};
[[maybe_unused]] auto m128_add_dpbusd_epi32 = [=](__m128i& acc, __m128i a, __m128i b) {
__m128i product0 = _mm_maddubs_epi16(a, b);
product0 = _mm_madd_epi16(product0, kOnes128);
acc = _mm_add_epi32(acc, product0);
};
[[maybe_unused]] auto m128_add_dpbusd_epi32x4 = [=](__m128i& acc, __m128i a0, __m128i b0, __m128i a1, __m128i b1,
__m128i a2, __m128i b2, __m128i a3, __m128i b3) {
__m128i product0 = _mm_maddubs_epi16(a0, b0);
__m128i product1 = _mm_maddubs_epi16(a1, b1);
__m128i product2 = _mm_maddubs_epi16(a2, b2);
__m128i product3 = _mm_maddubs_epi16(a3, b3);
product0 = _mm_adds_epi16(product0, product1);
product2 = _mm_adds_epi16(product2, product3);
product0 = _mm_adds_epi16(product0, product2);
product0 = _mm_madd_epi16(product0, kOnes128);
acc = _mm_add_epi32(acc, product0);
};
#endif
#if defined (USE_AVX512)
using vec_t = __m512i;
#define vec_setzero _mm512_setzero_si512
#define vec_set_32 _mm512_set1_epi32
auto& vec_add_dpbusd_32 = m512_add_dpbusd_epi32;
auto& vec_add_dpbusd_32x4 = m512_add_dpbusd_epi32x4;
auto& vec_hadd = m512_hadd;
#elif defined (USE_AVX2)
using vec_t = __m256i;
#define vec_setzero _mm256_setzero_si256
#define vec_set_32 _mm256_set1_epi32
auto& vec_add_dpbusd_32 = m256_add_dpbusd_epi32;
auto& vec_add_dpbusd_32x4 = m256_add_dpbusd_epi32x4;
auto& vec_hadd = m256_hadd;
#elif defined (USE_SSSE3)
using vec_t = __m128i;
#define vec_setzero _mm_setzero_si128
#define vec_set_32 _mm_set1_epi32
auto& vec_add_dpbusd_32 = m128_add_dpbusd_epi32;
auto& vec_add_dpbusd_32x4 = m128_add_dpbusd_epi32x4;
auto& vec_hadd = m128_hadd;
#endif
#if defined (USE_SSSE3)
const auto output = reinterpret_cast<OutputType*>(buffer); const auto output = reinterpret_cast<OutputType*>(buffer);
const auto input_vector = reinterpret_cast<const vec_t*>(input);
static_assert(kOutputDimensions % kOutputSimdWidth == 0 || kOutputDimensions == 1); #if defined(USE_AVX512)
constexpr IndexType kNumChunks = kPaddedInputDimensions / (kSimdWidth * 2);
const __m512i kOnes = _mm512_set1_epi16(1);
const auto input_vector = reinterpret_cast<const __m512i*>(input);
// kOutputDimensions is either 1 or a multiple of kSimdWidth #elif defined(USE_AVX2)
// because then it is also an input dimension.
if constexpr (kOutputDimensions % kOutputSimdWidth == 0)
{
constexpr IndexType kNumChunks = kPaddedInputDimensions / 4;
const auto input32 = reinterpret_cast<const std::int32_t*>(input);
vec_t* outptr = reinterpret_cast<vec_t*>(output);
std::memcpy(output, biases_, kOutputDimensions * sizeof(OutputType));
for (int i = 0; i < (int)kNumChunks - 3; i += 4)
{
const vec_t in0 = vec_set_32(input32[i + 0]);
const vec_t in1 = vec_set_32(input32[i + 1]);
const vec_t in2 = vec_set_32(input32[i + 2]);
const vec_t in3 = vec_set_32(input32[i + 3]);
const auto col0 = reinterpret_cast<const vec_t*>(&weights_[(i + 0) * kOutputDimensions * 4]);
const auto col1 = reinterpret_cast<const vec_t*>(&weights_[(i + 1) * kOutputDimensions * 4]);
const auto col2 = reinterpret_cast<const vec_t*>(&weights_[(i + 2) * kOutputDimensions * 4]);
const auto col3 = reinterpret_cast<const vec_t*>(&weights_[(i + 3) * kOutputDimensions * 4]);
for (int j = 0; j * kOutputSimdWidth < kOutputDimensions; ++j)
vec_add_dpbusd_32x4(outptr[j], in0, col0[j], in1, col1[j], in2, col2[j], in3, col3[j]);
}
for (int i = 0; i < canSaturate16.count; ++i)
output[canSaturate16.ids[i].out] += input[canSaturate16.ids[i].in] * canSaturate16.ids[i].w;
}
else if constexpr (kOutputDimensions == 1)
{
#if defined (USE_AVX512)
if constexpr (kPaddedInputDimensions % (kSimdWidth * 2) != 0)
{
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
const auto input_vector256 = reinterpret_cast<const __m256i*>(input);
__m256i sum0 = _mm256_setzero_si256();
const auto row0 = reinterpret_cast<const __m256i*>(&weights_[0]);
for (int j = 0; j < (int)kNumChunks; ++j)
{
const __m256i in = input_vector256[j];
m256_add_dpbusd_epi32(sum0, in, row0[j]);
}
output[0] = m256_hadd(sum0, biases_[0]);
}
else
#endif
{
#if defined (USE_AVX512)
constexpr IndexType kNumChunks = kPaddedInputDimensions / (kSimdWidth * 2);
#else
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
#endif
vec_t sum0 = vec_setzero();
const auto row0 = reinterpret_cast<const vec_t*>(&weights_[0]);
for (int j = 0; j < (int)kNumChunks; ++j)
{
const vec_t in = input_vector[j];
vec_add_dpbusd_32(sum0, in, row0[j]);
}
output[0] = vec_hadd(sum0, biases_[0]);
}
}
#else
// Use old implementation for the other architectures.
auto output = reinterpret_cast<OutputType*>(buffer);
#if defined(USE_SSE2)
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
const __m128i kZeros = _mm_setzero_si128(); const __m256i kOnes = _mm256_set1_epi16(1);
const auto input_vector = reinterpret_cast<const __m256i*>(input);
#elif defined(USE_SSSE3)
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
const __m128i kOnes = _mm_set1_epi16(1);
const auto input_vector = reinterpret_cast<const __m128i*>(input); const auto input_vector = reinterpret_cast<const __m128i*>(input);
#elif defined(USE_MMX) #elif defined(USE_NEON)
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; constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
const auto input_vector = reinterpret_cast<const int8x8_t*>(input); const auto input_vector = reinterpret_cast<const int8x8_t*>(input);
#endif #endif
for (IndexType i = 0; i < kOutputDimensions; ++i) { for (IndexType i = 0; i < kOutputDimensions; ++i) {
const IndexType offset = i * kPaddedInputDimensions; const IndexType offset = i * kPaddedInputDimensions;
#if defined(USE_SSE2) #if defined(USE_AVX512)
__m128i sum_lo = _mm_cvtsi32_si128(biases_[i]); __m512i sum = _mm512_setzero_si512();
__m128i sum_hi = kZeros; const auto row = reinterpret_cast<const __m512i*>(&weights_[offset]);
for (IndexType j = 0; j < kNumChunks; ++j) {
#if defined(__MINGW32__) || defined(__MINGW64__)
__m512i product = _mm512_maddubs_epi16(_mm512_loadu_si512(&input_vector[j]), _mm512_load_si512(&row[j]));
#else
__m512i product = _mm512_maddubs_epi16(_mm512_load_si512(&input_vector[j]), _mm512_load_si512(&row[j]));
#endif
product = _mm512_madd_epi16(product, kOnes);
sum = _mm512_add_epi32(sum, product);
}
output[i] = _mm512_reduce_add_epi32(sum) + biases_[i];
// 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 iv_256 = reinterpret_cast<const __m256i*>(input);
const auto row_256 = reinterpret_cast<const __m256i*>(&weights_[offset]);
int j = kNumChunks * 2;
#if defined(__MINGW32__) || defined(__MINGW64__) // See HACK comment below in AVX2.
__m256i sum256 = _mm256_maddubs_epi16(_mm256_loadu_si256(&iv_256[j]), _mm256_load_si256(&row_256[j]));
#else
__m256i sum256 = _mm256_maddubs_epi16(_mm256_load_si256(&iv_256[j]), _mm256_load_si256(&row_256[j]));
#endif
sum256 = _mm256_madd_epi16(sum256, _mm256_set1_epi16(1));
sum256 = _mm256_hadd_epi32(sum256, sum256);
sum256 = _mm256_hadd_epi32(sum256, sum256);
const __m128i lo = _mm256_extracti128_si256(sum256, 0);
const __m128i hi = _mm256_extracti128_si256(sum256, 1);
output[i] += _mm_cvtsi128_si32(lo) + _mm_cvtsi128_si32(hi);
}
#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(
#if defined(__MINGW32__) || defined(__MINGW64__)
// HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary
// compiled with g++ in MSYS2 crashes here because the output memory is not aligned
// even though alignas is specified.
_mm256_loadu_si256
#else
_mm256_load_si256
#endif
(&input_vector[j]), _mm256_load_si256(&row[j]));
product = _mm256_madd_epi16(product, kOnes);
sum = _mm256_add_epi32(sum, product);
}
sum = _mm256_hadd_epi32(sum, sum);
sum = _mm256_hadd_epi32(sum, sum);
const __m128i lo = _mm256_extracti128_si256(sum, 0);
const __m128i hi = _mm256_extracti128_si256(sum, 1);
output[i] = _mm_cvtsi128_si32(lo) + _mm_cvtsi128_si32(hi) + biases_[i];
#elif defined(USE_SSSE3)
__m128i sum = _mm_cvtsi32_si128(biases_[i]);
const auto row = reinterpret_cast<const __m128i*>(&weights_[offset]); const auto row = reinterpret_cast<const __m128i*>(&weights_[offset]);
for (IndexType j = 0; j < kNumChunks; ++j) { for (IndexType j = 0; j < kNumChunks; ++j) {
__m128i row_j = _mm_load_si128(&row[j]); __m128i product = _mm_maddubs_epi16(
__m128i input_j = _mm_load_si128(&input_vector[j]); _mm_load_si128(&input_vector[j]), _mm_load_si128(&row[j]));
__m128i extended_row_lo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8); product = _mm_madd_epi16(product, kOnes);
__m128i extended_row_hi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8); sum = _mm_add_epi32(sum, product);
__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); sum = _mm_hadd_epi32(sum, sum);
__m128i sum_high_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2)); sum = _mm_hadd_epi32(sum, sum);
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); output[i] = _mm_cvtsi128_si32(sum);
#elif defined(USE_MMX) #elif defined(USE_NEON)
__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 extended_row_lo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8);
__m64 extended_row_hi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8);
__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]}; int32x4_t sum = {biases_[i]};
const auto row = reinterpret_cast<const int8x8_t*>(&weights_[offset]); const auto row = reinterpret_cast<const int8x8_t*>(&weights_[offset]);
for (IndexType j = 0; j < kNumChunks; ++j) { for (IndexType j = 0; j < kNumChunks; ++j) {
@@ -414,21 +187,15 @@ namespace Eval::NNUE::Layers {
} }
output[i] = sum[0] + sum[1] + sum[2] + sum[3]; output[i] = sum[0] + sum[1] + sum[2] + sum[3];
#else #else
OutputType sum = biases_[i]; OutputType sum = biases_[i];
for (IndexType j = 0; j < kInputDimensions; ++j) { for (IndexType j = 0; j < kInputDimensions; ++j) {
sum += weights_[offset + j] * input[j]; sum += weights_[offset + j] * input[j];
} }
output[i] = sum; output[i] = sum;
#endif #endif
} }
#if defined(USE_MMX)
_mm_empty();
#endif
#endif
return output; return output;
} }
@@ -439,24 +206,8 @@ namespace Eval::NNUE::Layers {
PreviousLayer previous_layer_; PreviousLayer previous_layer_;
alignas(kCacheLineSize) BiasType biases_[kOutputDimensions]; alignas(kCacheLineSize) BiasType biases_[kOutputDimensions];
alignas(kCacheLineSize) WeightType weights_[kOutputDimensions * kPaddedInputDimensions]; alignas(kCacheLineSize)
#if defined (USE_SSSE3) WeightType weights_[kOutputDimensions * kPaddedInputDimensions];
struct CanSaturate {
int count;
struct Entry {
uint16_t out;
uint16_t in;
int8_t w;
} ids[kPaddedInputDimensions * kOutputDimensions * 3 / 4];
void add(int i, int j, int8_t w) {
ids[count].out = i;
ids[count].in = j;
ids[count].w = w;
++count;
}
} canSaturate16;
#endif
}; };
} // namespace Eval::NNUE::Layers } // namespace Eval::NNUE::Layers
+45 -25
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -74,17 +74,55 @@ namespace Eval::NNUE::Layers {
const auto out = reinterpret_cast<__m256i*>(output); const auto out = reinterpret_cast<__m256i*>(output);
for (IndexType i = 0; i < kNumChunks; ++i) { for (IndexType i = 0; i < kNumChunks; ++i) {
const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32(
_mm256_load_si256(&in[i * 4 + 0]),
_mm256_load_si256(&in[i * 4 + 1])), kWeightScaleBits); #if defined(__MINGW32__) || defined(__MINGW64__)
// HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary
// compiled with g++ in MSYS2 crashes here because the output memory is not aligned
// even though alignas is specified.
_mm256_loadu_si256
#else
_mm256_load_si256
#endif
(&in[i * 4 + 0]),
#if defined(__MINGW32__) || defined(__MINGW64__)
_mm256_loadu_si256
#else
_mm256_load_si256
#endif
(&in[i * 4 + 1])), kWeightScaleBits);
const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32(
_mm256_load_si256(&in[i * 4 + 2]),
_mm256_load_si256(&in[i * 4 + 3])), kWeightScaleBits); #if defined(__MINGW32__) || defined(__MINGW64__)
_mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( _mm256_loadu_si256
#else
_mm256_load_si256
#endif
(&in[i * 4 + 2]),
#if defined(__MINGW32__) || defined(__MINGW64__)
_mm256_loadu_si256
#else
_mm256_load_si256
#endif
(&in[i * 4 + 3])), kWeightScaleBits);
#if defined(__MINGW32__) || defined(__MINGW64__)
_mm256_storeu_si256
#else
_mm256_store_si256
#endif
(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8(
_mm256_packs_epi16(words0, words1), kZero), kOffsets)); _mm256_packs_epi16(words0, words1), kZero), kOffsets));
} }
constexpr IndexType kStart = kNumChunks * kSimdWidth; constexpr IndexType kStart = kNumChunks * kSimdWidth;
#elif defined(USE_SSE2) #elif defined(USE_SSSE3)
constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth; constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth;
#ifdef USE_SSE41 #ifdef USE_SSE41
@@ -115,24 +153,6 @@ namespace Eval::NNUE::Layers {
} }
constexpr IndexType kStart = kNumChunks * kSimdWidth; 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) #elif defined(USE_NEON)
constexpr IndexType kNumChunks = kInputDimensions / (kSimdWidth / 2); constexpr IndexType kNumChunks = kInputDimensions / (kSimdWidth / 2);
const int8x8_t kZero = {0}; const int8x8_t kZero = {0};
+1 -1
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
+5 -6
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -25,14 +25,13 @@
namespace Eval::NNUE { namespace Eval::NNUE {
// The accumulator of a StateInfo without parent is set to the INIT state
enum AccumulatorState { EMPTY, COMPUTED, INIT };
// Class that holds the result of affine transformation of input features // Class that holds the result of affine transformation of input features
struct alignas(kCacheLineSize) Accumulator { struct alignas(32) Accumulator {
std::int16_t std::int16_t
accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions]; accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions];
AccumulatorState state[2]; Value score;
bool computed_accumulation;
bool computed_score;
}; };
} // namespace Eval::NNUE } // namespace Eval::NNUE
+1 -1
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
+2 -57
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -21,9 +21,6 @@
#ifndef NNUE_COMMON_H_INCLUDED #ifndef NNUE_COMMON_H_INCLUDED
#define NNUE_COMMON_H_INCLUDED #define NNUE_COMMON_H_INCLUDED
#include <cstring>
#include <iostream>
#if defined(USE_AVX2) #if defined(USE_AVX2)
#include <immintrin.h> #include <immintrin.h>
@@ -36,9 +33,6 @@
#elif defined(USE_SSE2) #elif defined(USE_SSE2)
#include <emmintrin.h> #include <emmintrin.h>
#elif defined(USE_MMX)
#include <mmintrin.h>
#elif defined(USE_NEON) #elif defined(USE_NEON)
#include <arm_neon.h> #include <arm_neon.h>
#endif #endif
@@ -62,43 +56,12 @@ namespace Eval::NNUE {
#elif defined(USE_SSE2) #elif defined(USE_SSE2)
constexpr std::size_t kSimdWidth = 16; constexpr std::size_t kSimdWidth = 16;
#elif defined(USE_MMX)
constexpr std::size_t kSimdWidth = 8;
#elif defined(USE_NEON) #elif defined(USE_NEON)
constexpr std::size_t kSimdWidth = 16; constexpr std::size_t kSimdWidth = 16;
#endif #endif
constexpr std::size_t kMaxSimdWidth = 32; constexpr std::size_t kMaxSimdWidth = 32;
// unique number for each piece type on each square
enum {
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
};
constexpr uint32_t kpp_board_index[COLOR_NB][PIECE_NB] = {
// convention: W - us, B - them
// viewed from other side, W and B are reversed
{ PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_KING, PS_NONE,
PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_KING, PS_NONE },
{ PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_KING, PS_NONE,
PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_KING, PS_NONE }
};
// Type of input feature after conversion // Type of input feature after conversion
using TransformedFeatureType = std::uint8_t; using TransformedFeatureType = std::uint8_t;
using IndexType = std::uint32_t; using IndexType = std::uint32_t;
@@ -106,25 +69,7 @@ namespace Eval::NNUE {
// Round n up to be a multiple of base // Round n up to be a multiple of base
template <typename IntType> template <typename IntType>
constexpr IntType CeilToMultiple(IntType n, IntType base) { constexpr IntType CeilToMultiple(IntType n, IntType base) {
return (n + base - 1) / base * 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 } // namespace Eval::NNUE
+211 -273
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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,56 +29,6 @@
namespace Eval::NNUE { namespace Eval::NNUE {
// If vector instructions are enabled, we update and refresh the
// accumulator tile by tile such that each tile fits in the CPU's
// vector registers.
#define VECTOR
#ifdef USE_AVX512
typedef __m512i vec_t;
#define vec_load(a) _mm512_load_si512(a)
#define vec_store(a,b) _mm512_store_si512(a,b)
#define vec_add_16(a,b) _mm512_add_epi16(a,b)
#define vec_sub_16(a,b) _mm512_sub_epi16(a,b)
static constexpr IndexType kNumRegs = 8; // only 8 are needed
#elif USE_AVX2
typedef __m256i vec_t;
#define vec_load(a) _mm256_load_si256(a)
#define vec_store(a,b) _mm256_store_si256(a,b)
#define vec_add_16(a,b) _mm256_add_epi16(a,b)
#define vec_sub_16(a,b) _mm256_sub_epi16(a,b)
static constexpr IndexType kNumRegs = 16;
#elif USE_SSE2
typedef __m128i vec_t;
#define vec_load(a) (*(a))
#define vec_store(a,b) *(a)=(b)
#define vec_add_16(a,b) _mm_add_epi16(a,b)
#define vec_sub_16(a,b) _mm_sub_epi16(a,b)
static constexpr IndexType kNumRegs = Is64Bit ? 16 : 8;
#elif USE_MMX
typedef __m64 vec_t;
#define vec_load(a) (*(a))
#define vec_store(a,b) *(a)=(b)
#define vec_add_16(a,b) _mm_add_pi16(a,b)
#define vec_sub_16(a,b) _mm_sub_pi16(a,b)
static constexpr IndexType kNumRegs = 8;
#elif USE_NEON
typedef int16x8_t vec_t;
#define vec_load(a) (*(a))
#define vec_store(a,b) *(a)=(b)
#define vec_add_16(a,b) vaddq_s16(a,b)
#define vec_sub_16(a,b) vsubq_s16(a,b)
static constexpr IndexType kNumRegs = 16;
#else
#undef VECTOR
#endif
// Input feature converter // Input feature converter
class FeatureTransformer { class FeatureTransformer {
@@ -86,11 +36,6 @@ namespace Eval::NNUE {
// Number of output dimensions for one side // Number of output dimensions for one side
static constexpr IndexType kHalfDimensions = kTransformedFeatureDimensions; static constexpr IndexType kHalfDimensions = kTransformedFeatureDimensions;
#ifdef VECTOR
static constexpr IndexType kTileHeight = kNumRegs * sizeof(vec_t) / 2;
static_assert(kHalfDimensions % kTileHeight == 0, "kTileHeight must divide kHalfDimensions");
#endif
public: public:
// Output type // Output type
using OutputType = TransformedFeatureType; using OutputType = TransformedFeatureType;
@@ -105,40 +50,45 @@ namespace Eval::NNUE {
// Hash value embedded in the evaluation file // Hash value embedded in the evaluation file
static constexpr std::uint32_t GetHashValue() { static constexpr std::uint32_t GetHashValue() {
return RawFeatures::kHashValue ^ kOutputDimensions; return RawFeatures::kHashValue ^ kOutputDimensions;
} }
// Read network parameters // Read network parameters
bool ReadParameters(std::istream& stream) { bool ReadParameters(std::istream& stream) {
stream.read(reinterpret_cast<char*>(biases_),
for (std::size_t i = 0; i < kHalfDimensions; ++i) kHalfDimensions * sizeof(BiasType));
biases_[i] = read_little_endian<BiasType>(stream); stream.read(reinterpret_cast<char*>(weights_),
for (std::size_t i = 0; i < kHalfDimensions * kInputDimensions; ++i) kHalfDimensions * kInputDimensions * sizeof(WeightType));
weights_[i] = read_little_endian<WeightType>(stream);
return !stream.fail(); 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 // Convert input features
void Transform(const Position& pos, OutputType* output) const { void Transform(const Position& pos, OutputType* output, bool refresh) const {
if (refresh || !UpdateAccumulatorIfPossible(pos)) {
UpdateAccumulator(pos, WHITE); RefreshAccumulator(pos);
UpdateAccumulator(pos, BLACK); }
const auto& accumulation = pos.state()->accumulator.accumulation; const auto& accumulation = pos.state()->accumulator.accumulation;
#if defined(USE_AVX512) #if defined(USE_AVX2)
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth * 2);
static_assert(kHalfDimensions % (kSimdWidth * 2) == 0);
const __m512i kControl = _mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7);
const __m512i kZero = _mm512_setzero_si512();
#elif defined(USE_AVX2)
constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth;
constexpr int kControl = 0b11011000; constexpr int kControl = 0b11011000;
const __m256i kZero = _mm256_setzero_si256(); const __m256i kZero = _mm256_setzero_si256();
#elif defined(USE_SSE2) #elif defined(USE_SSSE3)
constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth;
#ifdef USE_SSE41 #ifdef USE_SSE41
@@ -147,10 +97,6 @@ namespace Eval::NNUE {
const __m128i k0x80s = _mm_set1_epi8(-128); const __m128i k0x80s = _mm_set1_epi8(-128);
#endif #endif
#elif defined(USE_MMX)
constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth;
const __m64 k0x80s = _mm_set1_pi8(-128);
#elif defined(USE_NEON) #elif defined(USE_NEON)
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
const int8x8_t kZero = {0}; const int8x8_t kZero = {0};
@@ -160,29 +106,44 @@ namespace Eval::NNUE {
for (IndexType p = 0; p < 2; ++p) { for (IndexType p = 0; p < 2; ++p) {
const IndexType offset = kHalfDimensions * p; const IndexType offset = kHalfDimensions * p;
#if defined(USE_AVX512) #if defined(USE_AVX2)
auto out = reinterpret_cast<__m512i*>(&output[offset]);
for (IndexType j = 0; j < kNumChunks; ++j) {
__m512i sum0 = _mm512_load_si512(
&reinterpret_cast<const __m512i*>(accumulation[perspectives[p]][0])[j * 2 + 0]);
__m512i sum1 = _mm512_load_si512(
&reinterpret_cast<const __m512i*>(accumulation[perspectives[p]][0])[j * 2 + 1]);
_mm512_store_si512(&out[j], _mm512_permutexvar_epi64(kControl,
_mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), kZero)));
}
#elif defined(USE_AVX2)
auto out = reinterpret_cast<__m256i*>(&output[offset]); auto out = reinterpret_cast<__m256i*>(&output[offset]);
for (IndexType j = 0; j < kNumChunks; ++j) { for (IndexType j = 0; j < kNumChunks; ++j) {
__m256i sum0 = _mm256_load_si256( __m256i sum0 =
&reinterpret_cast<const __m256i*>(accumulation[perspectives[p]][0])[j * 2 + 0]);
__m256i sum1 = _mm256_load_si256( #if defined(__MINGW32__) || defined(__MINGW64__)
&reinterpret_cast<const __m256i*>(accumulation[perspectives[p]][0])[j * 2 + 1]); // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary
_mm256_store_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( // compiled with g++ in MSYS2 crashes here because the output memory is not aligned
// even though alignas is specified.
_mm256_loadu_si256
#else
_mm256_load_si256
#endif
(&reinterpret_cast<const __m256i*>(
accumulation[perspectives[p]][0])[j * 2 + 0]);
__m256i sum1 =
#if defined(__MINGW32__) || defined(__MINGW64__)
_mm256_loadu_si256
#else
_mm256_load_si256
#endif
(&reinterpret_cast<const __m256i*>(
accumulation[perspectives[p]][0])[j * 2 + 1]);
#if defined(__MINGW32__) || defined(__MINGW64__)
_mm256_storeu_si256
#else
_mm256_store_si256
#endif
(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8(
_mm256_packs_epi16(sum0, sum1), kZero), kControl)); _mm256_packs_epi16(sum0, sum1), kZero), kControl));
} }
#elif defined(USE_SSE2) #elif defined(USE_SSSE3)
auto out = reinterpret_cast<__m128i*>(&output[offset]); auto out = reinterpret_cast<__m128i*>(&output[offset]);
for (IndexType j = 0; j < kNumChunks; ++j) { for (IndexType j = 0; j < kNumChunks; ++j) {
__m128i sum0 = _mm_load_si128(&reinterpret_cast<const __m128i*>( __m128i sum0 = _mm_load_si128(&reinterpret_cast<const __m128i*>(
@@ -194,25 +155,14 @@ namespace Eval::NNUE {
_mm_store_si128(&out[j], _mm_store_si128(&out[j],
#ifdef USE_SSE41 #ifdef USE_SSE41
_mm_max_epi8(packedbytes, kZero) _mm_max_epi8(packedbytes, kZero)
#else #else
_mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)
#endif #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) #elif defined(USE_NEON)
const auto out = reinterpret_cast<int8x8_t*>(&output[offset]); const auto out = reinterpret_cast<int8x8_t*>(&output[offset]);
for (IndexType j = 0; j < kNumChunks; ++j) { for (IndexType j = 0; j < kNumChunks; ++j) {
@@ -230,178 +180,166 @@ namespace Eval::NNUE {
#endif #endif
} }
#if defined(USE_MMX)
_mm_empty();
#endif
} }
private: private:
void UpdateAccumulator(const Position& pos, const Color c) const { // Calculate cumulative value without using difference calculation
void RefreshAccumulator(const Position& pos) const {
#ifdef VECTOR auto& accumulator = pos.state()->accumulator;
// Gcc-10.2 unnecessarily spills AVX2 registers if this array IndexType i = 0;
// is defined in the VECTOR code below, once in each branch Features::IndexList active_indices[2];
vec_t acc[kNumRegs]; RawFeatures::AppendActiveIndices(pos, kRefreshTriggers[i],
#endif active_indices);
for (Color perspective : { WHITE, BLACK }) {
// Look for a usable accumulator of an earlier position. We keep track std::memcpy(accumulator.accumulation[perspective][i], biases_,
// of the estimated gain in terms of features to be added/subtracted. kHalfDimensions * sizeof(BiasType));
StateInfo *st = pos.state(), *next = nullptr; for (const auto index : active_indices[perspective]) {
int gain = pos.count<ALL_PIECES>() - 2;
while (st->accumulator.state[c] == EMPTY)
{
auto& dp = st->dirtyPiece;
// The first condition tests whether an incremental update is
// possible at all: if this side's king has moved, it is not possible.
static_assert(std::is_same_v<RawFeatures::SortedTriggerSet,
Features::CompileTimeList<Features::TriggerEvent, Features::TriggerEvent::kFriendKingMoved>>,
"Current code assumes that only kFriendlyKingMoved refresh trigger is being used.");
if ( dp.piece[0] == make_piece(c, KING)
|| (gain -= dp.dirty_num + 1) < 0)
break;
next = st;
st = st->previous;
}
if (st->accumulator.state[c] == COMPUTED)
{
if (next == nullptr)
return;
// Update incrementally in two steps. First, we update the "next"
// accumulator. Then, we update the current accumulator (pos.state()).
// Gather all features to be updated. This code assumes HalfKP features
// only and doesn't support refresh triggers.
static_assert(std::is_same_v<Features::FeatureSet<Features::HalfKP<Features::Side::kFriend>>,
RawFeatures>);
Features::IndexList removed[2], added[2];
Features::HalfKP<Features::Side::kFriend>::AppendChangedIndices(pos,
next->dirtyPiece, c, &removed[0], &added[0]);
for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous)
Features::HalfKP<Features::Side::kFriend>::AppendChangedIndices(pos,
st2->dirtyPiece, c, &removed[1], &added[1]);
// Mark the accumulators as computed.
next->accumulator.state[c] = COMPUTED;
pos.state()->accumulator.state[c] = COMPUTED;
// Now update the accumulators listed in info[], where the last element is a sentinel.
StateInfo *info[3] =
{ next, next == pos.state() ? nullptr : pos.state(), nullptr };
#ifdef VECTOR
for (IndexType j = 0; j < kHalfDimensions / kTileHeight; ++j)
{
// Load accumulator
auto accTile = reinterpret_cast<vec_t*>(
&st->accumulator.accumulation[c][0][j * kTileHeight]);
for (IndexType k = 0; k < kNumRegs; ++k)
acc[k] = vec_load(&accTile[k]);
for (IndexType i = 0; info[i]; ++i)
{
// Difference calculation for the deactivated features
for (const auto index : removed[i])
{
const IndexType offset = kHalfDimensions * index + j * kTileHeight;
auto column = reinterpret_cast<const vec_t*>(&weights_[offset]);
for (IndexType k = 0; k < kNumRegs; ++k)
acc[k] = vec_sub_16(acc[k], column[k]);
}
// Difference calculation for the activated features
for (const auto index : added[i])
{
const IndexType offset = kHalfDimensions * index + j * kTileHeight;
auto column = reinterpret_cast<const vec_t*>(&weights_[offset]);
for (IndexType k = 0; k < kNumRegs; ++k)
acc[k] = vec_add_16(acc[k], column[k]);
}
// Store accumulator
accTile = reinterpret_cast<vec_t*>(
&info[i]->accumulator.accumulation[c][0][j * kTileHeight]);
for (IndexType k = 0; k < kNumRegs; ++k)
vec_store(&accTile[k], acc[k]);
}
}
#else
for (IndexType i = 0; info[i]; ++i)
{
std::memcpy(info[i]->accumulator.accumulation[c][0],
st->accumulator.accumulation[c][0],
kHalfDimensions * sizeof(BiasType));
st = info[i];
// Difference calculation for the deactivated features
for (const auto index : removed[i])
{
const IndexType offset = kHalfDimensions * index;
for (IndexType j = 0; j < kHalfDimensions; ++j)
st->accumulator.accumulation[c][0][j] -= weights_[offset + j];
}
// Difference calculation for the activated features
for (const auto index : added[i])
{
const IndexType offset = kHalfDimensions * index;
for (IndexType j = 0; j < kHalfDimensions; ++j)
st->accumulator.accumulation[c][0][j] += weights_[offset + j];
}
}
#endif
}
else
{
// Refresh the accumulator
auto& accumulator = pos.state()->accumulator;
accumulator.state[c] = COMPUTED;
Features::IndexList active;
Features::HalfKP<Features::Side::kFriend>::AppendActiveIndices(pos, c, &active);
#ifdef VECTOR
for (IndexType j = 0; j < kHalfDimensions / kTileHeight; ++j)
{
auto biasesTile = reinterpret_cast<const vec_t*>(
&biases_[j * kTileHeight]);
for (IndexType k = 0; k < kNumRegs; ++k)
acc[k] = biasesTile[k];
for (const auto index : active)
{
const IndexType offset = kHalfDimensions * index + j * kTileHeight;
auto column = reinterpret_cast<const vec_t*>(&weights_[offset]);
for (unsigned k = 0; k < kNumRegs; ++k)
acc[k] = vec_add_16(acc[k], column[k]);
}
auto accTile = reinterpret_cast<vec_t*>(
&accumulator.accumulation[c][0][j * kTileHeight]);
for (unsigned k = 0; k < kNumRegs; k++)
vec_store(&accTile[k], acc[k]);
}
#else
std::memcpy(accumulator.accumulation[c][0], biases_,
kHalfDimensions * sizeof(BiasType));
for (const auto index : active)
{
const IndexType offset = kHalfDimensions * index; const IndexType offset = kHalfDimensions * index;
for (IndexType j = 0; j < kHalfDimensions; ++j) #if defined(USE_AVX2)
accumulator.accumulation[c][0][j] += weights_[offset + j]; 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) {
#if defined(__MINGW32__) || defined(__MINGW64__)
_mm256_storeu_si256(&accumulation[j], _mm256_add_epi16(_mm256_loadu_si256(&accumulation[j]), column[j]));
#else
accumulation[j] = _mm256_add_epi16(accumulation[j], column[j]);
#endif #endif
}
#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_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) accumulator.computed_accumulation = true;
_mm_empty(); 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_NEON)
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
auto accumulation = reinterpret_cast<int16x8_t*>(
&accumulator.accumulation[perspective][i][0]);
#endif #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_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_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
}
}
}
accumulator.computed_accumulation = true;
accumulator.computed_score = false;
} }
using BiasType = std::int16_t; using BiasType = std::int16_t;
+25 -44
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -30,30 +30,29 @@ namespace {
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// Pawn penalties // Pawn penalties
constexpr Score Backward = S( 9, 22); constexpr Score Backward = S( 9, 24);
constexpr Score Doubled = S(13, 51); constexpr Score Doubled = S(11, 56);
constexpr Score DoubledEarly = S(20, 7); constexpr Score Isolated = S( 5, 15);
constexpr Score Isolated = S( 3, 15); constexpr Score WeakLever = S( 0, 56);
constexpr Score WeakLever = S( 4, 58); constexpr Score WeakUnopposed = S(13, 27);
constexpr Score WeakUnopposed = S(13, 24);
// Bonus for blocked pawns at 5th or 6th rank // Bonus for blocked pawns at 5th or 6th rank
constexpr Score BlockedPawn[2] = { S(-17, -6), S(-9, 2) }; constexpr Score BlockedPawn[2] = { S(-11, -4), S(-3, 4) };
constexpr Score BlockedStorm[RANK_NB] = { constexpr Score BlockedStorm[RANK_NB] = {
S(0, 0), S(0, 0), S(75, 78), S(-8, 16), S(-6, 10), S(-6, 6), S(0, 2) S(0, 0), S(0, 0), S(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, 5, 7, 11, 23, 48, 87 }; constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 };
// Strength of pawn shelter for our king by [distance from edge][rank]. // Strength of pawn shelter for our king by [distance from edge][rank].
// RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king.
constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = { constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = {
{ V( -5), V( 82), V( 92), V( 54), V( 36), V( 22), V( 28) }, { V( -6), V( 81), V( 93), V( 58), V( 39), V( 18), V( 25) },
{ V(-44), V( 63), V( 33), V(-50), V(-30), V(-12), V( -62) }, { V(-43), V( 61), V( 35), V(-49), V(-29), V(-11), V( -63) },
{ V(-11), V( 77), V( 22), V( -6), V( 31), V( 8), V( -45) }, { V(-10), V( 75), V( 23), V( -2), V( 32), V( 3), V( -45) },
{ V(-39), V(-12), V(-29), V(-50), V(-43), V(-68), V(-164) } { V(-39), V(-13), V(-29), V(-52), V(-48), V(-67), V(-166) }
}; };
// Danger of enemy pawns moving toward our king by [distance from edge][rank]. // Danger of enemy pawns moving toward our king by [distance from edge][rank].
@@ -61,18 +60,12 @@ namespace {
// is behind our king. Note that UnblockedStorm[0][1-2] accommodate opponent pawn // is behind our king. Note that UnblockedStorm[0][1-2] accommodate opponent pawn
// on edge, likely blocked by our king. // on edge, likely blocked by our king.
constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = { constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = {
{ V( 87), V(-288), V(-168), V( 96), V( 47), V( 44), V( 46) }, { V( 85), V(-289), V(-166), V(97), V(50), V( 45), V( 50) },
{ V( 42), V( -25), V( 120), V( 45), V( 34), V( -9), V( 24) }, { V( 46), V( -25), V( 122), V(45), V(37), V(-10), V( 20) },
{ V( -8), V( 51), V( 167), V( 35), V( -4), V(-16), V(-12) }, { V( -6), V( 51), V( 168), V(34), V(-2), V(-22), V(-14) },
{ V(-17), V( -13), V( 100), V( 4), V( 9), V(-16), V(-31) } { V(-15), V( -11), V( 101), V( 4), V(11), V(-15), V(-29) }
}; };
// KingOnFile[semi-open Us][semi-open Them] contains bonuses/penalties
// for king when the king is on a semi-open or open file.
constexpr Score KingOnFile[2][2] = {{ S(-21,10), S(-7, 1) },
{ S( 0,-3), S( 9,-4) }};
#undef S #undef S
#undef V #undef V
@@ -87,14 +80,13 @@ namespace {
constexpr Color Them = ~Us; constexpr Color Them = ~Us;
constexpr Direction Up = pawn_push(Us); constexpr Direction Up = pawn_push(Us);
constexpr Direction Down = -Up;
Bitboard neighbours, stoppers, support, phalanx, opposed; Bitboard neighbours, stoppers, support, phalanx, opposed;
Bitboard lever, leverPush, blocked; Bitboard lever, leverPush, blocked;
Square s; Square s;
bool backward, passed, doubled; bool backward, passed, doubled;
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
Bitboard b = pos.pieces(Us, PAWN); const Square* pl = pos.squares<PAWN>(Us);
Bitboard ourPawns = pos.pieces( Us, PAWN); Bitboard ourPawns = pos.pieces( Us, PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN);
@@ -107,9 +99,8 @@ namespace {
e->blockedCount += popcount(shift<Up>(ourPawns) & (theirPawns | doubleAttackThem)); e->blockedCount += popcount(shift<Up>(ourPawns) & (theirPawns | doubleAttackThem));
// Loop through all pawns of the current color and score each pawn // Loop through all pawns of the current color and score each pawn
while (b) { while ((s = *pl++) != SQ_NONE)
s = pop_lsb(&b); {
assert(pos.piece_on(s) == make_piece(Us, PAWN)); assert(pos.piece_on(s) == make_piece(Us, PAWN));
Rank r = relative_rank(Us, s); Rank r = relative_rank(Us, s);
@@ -125,13 +116,6 @@ namespace {
phalanx = neighbours & rank_bb(s); phalanx = neighbours & rank_bb(s);
support = neighbours & rank_bb(s - Up); support = neighbours & rank_bb(s - Up);
if (doubled)
{
// Additional doubled penalty if none of their pawns is fixed
if (!(ourPawns & shift<Down>(theirPawns | pawn_attacks_bb<Them>(theirPawns))))
score -= DoubledEarly;
}
// A pawn is backward when it is behind all pawns of the same color on // A pawn is backward when it is behind all pawns of the same color on
// the adjacent files and cannot safely advance. // the adjacent files and cannot safely advance.
backward = !(neighbours & forward_ranks_bb(Them, s + Up)) backward = !(neighbours & forward_ranks_bb(Them, s + Up))
@@ -163,7 +147,7 @@ namespace {
if (support | phalanx) if (support | phalanx)
{ {
int v = Connected[r] * (2 + bool(phalanx) - bool(opposed)) int v = Connected[r] * (2 + bool(phalanx) - bool(opposed))
+ 22 * popcount(support); + 21 * popcount(support);
score += make_score(v, v * (r - 2) / 4); score += make_score(v, v * (r - 2) / 4);
} }
@@ -181,14 +165,14 @@ namespace {
else if (backward) else if (backward)
score -= Backward score -= Backward
+ WeakUnopposed * !opposed * bool(~(FileABB | FileHBB) & s); + WeakUnopposed * !opposed;
if (!support) if (!support)
score -= Doubled * doubled score -= Doubled * doubled
+ WeakLever * more_than_one(lever); + WeakLever * more_than_one(lever);
if (blocked && r >= RANK_5) if (blocked && r > RANK_4)
score += BlockedPawn[r - RANK_5]; score += BlockedPawn[r-4];
} }
return score; return score;
@@ -235,7 +219,7 @@ Score Entry::evaluate_shelter(const Position& pos, Square ksq) const {
Score bonus = make_score(5, 5); Score bonus = make_score(5, 5);
File center = std::clamp(file_of(ksq), FILE_B, FILE_G); File center = Utility::clamp(file_of(ksq), FILE_B, FILE_G);
for (File f = File(center - 1); f <= File(center + 1); ++f) for (File f = File(center - 1); f <= File(center + 1); ++f)
{ {
b = ourPawns & file_bb(f); b = ourPawns & file_bb(f);
@@ -253,9 +237,6 @@ Score Entry::evaluate_shelter(const Position& pos, Square ksq) const {
bonus -= make_score(UnblockedStorm[d][theirRank], 0); bonus -= make_score(UnblockedStorm[d][theirRank], 0);
} }
// King On File
bonus -= KingOnFile[pos.is_on_semiopen_file(Us, ksq)][pos.is_on_semiopen_file(Them, ksq)];
return bonus; return bonus;
} }
+1 -1
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
+148 -91
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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,8 +77,6 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) {
&& !pos.can_castle(ANY_CASTLING)) && !pos.can_castle(ANY_CASTLING))
{ {
StateInfo st; StateInfo st;
ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize);
Position p; Position p;
p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread()); p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread());
Tablebases::ProbeState s1, s2; Tablebases::ProbeState s1, s2;
@@ -197,8 +195,12 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
std::memset(this, 0, sizeof(Position)); std::memset(this, 0, sizeof(Position));
std::memset(si, 0, sizeof(StateInfo)); std::memset(si, 0, sizeof(StateInfo));
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
@@ -210,8 +212,21 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
else if (token == '/') else if (token == '/')
sq += 2 * SOUTH; sq += 2 * SOUTH;
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;
} }
} }
@@ -249,8 +264,6 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
set_castling_right(c, rsq); set_castling_right(c, rsq);
} }
set_state(st);
// 4. En passant square. // 4. En passant square.
// Ignore if square is invalid or not on side to move relative rank 6. // Ignore if square is invalid or not on side to move relative rank 6.
bool enpassant = false; bool enpassant = false;
@@ -264,24 +277,12 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
// a) side to move have a pawn threatening epSquare // a) side to move have a pawn threatening epSquare
// b) there is an enemy pawn in front of epSquare // b) there is an enemy pawn in front of epSquare
// c) there is no piece on epSquare or behind epSquare // c) there is no piece on epSquare or behind epSquare
// d) enemy pawn didn't block a check of its own color by moving forward
enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN) enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN)
&& (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))) && (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))
&& !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove)))) && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))));
&& ( file_of(square<KING>(sideToMove)) == file_of(st->epSquare)
|| !(blockers_for_king(sideToMove) & (st->epSquare + pawn_push(~sideToMove))));
} }
// It's necessary for st->previous to be intialized in this way because legality check relies on its existence if (!enpassant)
if (enpassant) {
st->previous = new StateInfo();
remove_piece(st->epSquare - pawn_push(sideToMove));
st->previous->checkersBB = attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove);
st->previous->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square<KING>(WHITE), st->previous->pinners[BLACK]);
st->previous->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square<KING>(BLACK), st->previous->pinners[WHITE]);
put_piece(make_piece(~sideToMove, PAWN), st->epSquare - pawn_push(sideToMove));
}
else
st->epSquare = SQ_NONE; st->epSquare = SQ_NONE;
// 5-6. Halfmove clock and fullmove number // 5-6. Halfmove clock and fullmove number
@@ -293,8 +294,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
chess960 = isChess960; chess960 = isChess960;
thisThread = th; thisThread = th;
st->accumulator.state[WHITE] = Eval::NNUE::INIT; set_state(st);
st->accumulator.state[BLACK] = Eval::NNUE::INIT;
assert(pos_is_ok()); assert(pos_is_ok());
@@ -515,11 +515,23 @@ bool Position::legal(Move m) const {
assert(color_of(moved_piece(m)) == us); assert(color_of(moved_piece(m)) == us);
assert(piece_on(square<KING>(us)) == make_piece(us, KING)); assert(piece_on(square<KING>(us)) == make_piece(us, KING));
// st->previous->blockersForKing consider capsq as empty. // En passant captures are a tricky special case. Because they are rather
// If pinned, it has to move along the king ray. // uncommon, we do it simply by testing whether the king is attacked after
if (type_of(m) == EN_PASSANT) // the move is made.
return !(st->previous->blockersForKing[sideToMove] & from) if (type_of(m) == ENPASSANT)
|| aligned(from, to, square<KING>(us)); {
Square ksq = square<KING>(us);
Square capsq = to - pawn_push(us);
Bitboard occupied = (pieces() ^ from ^ capsq) | to;
assert(to == ep_square());
assert(moved_piece(m) == make_piece(us, PAWN));
assert(piece_on(capsq) == make_piece(~us, PAWN));
assert(piece_on(to) == NO_PIECE);
return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK))
&& !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
}
// Castling moves generation does not check if the castling path is clear of // Castling moves generation does not check if the castling path is clear of
// enemy attacks, it is delayed at a later time: now! // enemy attacks, it is delayed at a later time: now!
@@ -534,9 +546,11 @@ bool Position::legal(Move m) const {
if (attackers_to(s) & pieces(~us)) if (attackers_to(s) & pieces(~us))
return false; return false;
// In case of Chess960, verify if the Rook blocks some checks // In case of Chess960, verify that when moving the castling rook we do
// not discover some hidden checker.
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
return !chess960 || !(blockers_for_king(us) & to_sq(m)); return !chess960
|| !(attacks_bb<ROOK>(to, pieces() ^ to_sq(m)) & pieces(~us, ROOK, QUEEN));
} }
// If the moving piece is a king, check whether the destination square is // If the moving piece is a king, check whether the destination square is
@@ -546,8 +560,8 @@ bool Position::legal(Move m) const {
// A non-king move is legal if and only if it is not pinned or it // A non-king move is legal if and only if it is not pinned or it
// is moving along the ray towards or away from the king. // is moving along the ray towards or away from the king.
return !(blockers_for_king(us) & from) return !(blockers_for_king(us) & from)
|| aligned(from, to, square<KING>(us)); || aligned(from, to, square<KING>(us));
} }
@@ -563,10 +577,8 @@ bool Position::pseudo_legal(const Move m) const {
Piece pc = moved_piece(m); Piece pc = moved_piece(m);
// Use a slower but simpler function for uncommon cases // Use a slower but simpler function for uncommon cases
// yet we skip the legality check of MoveList<LEGAL>().
if (type_of(m) != NORMAL) if (type_of(m) != NORMAL)
return checkers() ? MoveList< EVASIONS>(*this).contains(m) return MoveList<LEGAL>(*this).contains(m);
: MoveList<NON_EVASIONS>(*this).contains(m);
// Is not a promotion, so promotion piece must be empty // Is not a promotion, so promotion piece must be empty
if (promotion_type(m) - KNIGHT != NO_PIECE_TYPE) if (promotion_type(m) - KNIGHT != NO_PIECE_TYPE)
@@ -652,24 +664,31 @@ bool Position::gives_check(Move m) const {
case PROMOTION: case PROMOTION:
return attacks_bb(promotion_type(m), to, pieces() ^ from) & square<KING>(~sideToMove); return attacks_bb(promotion_type(m), to, pieces() ^ from) & square<KING>(~sideToMove);
// The double-pushed pawn blocked a check? En Passant will remove the blocker. // En passant capture with check? We have already handled the case
// The only discovery check that wasn't handle is through capsq and fromsq // of direct checks and ordinary discovered check, so the only case we
// So the King must be in the same rank as fromsq to consider this possibility. // need to handle is the unusual case of a discovered check through
// st->previous->blockersForKing consider capsq as empty. // the captured pawn.
case EN_PASSANT: case ENPASSANT:
return st->previous->checkersBB
|| ( rank_of(square<KING>(~sideToMove)) == rank_of(from)
&& st->previous->blockersForKing[~sideToMove] & from);
default: //CASTLING
{ {
// Castling is encoded as 'king captures the rook' Square capsq = make_square(file_of(to), rank_of(from));
Square ksq = square<KING>(~sideToMove); Bitboard b = (pieces() ^ from ^ capsq) | to;
Square rto = relative_square(sideToMove, to > from ? SQ_F1 : SQ_D1);
return (attacks_bb<ROOK>(rto) & ksq) return (attacks_bb< ROOK>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK))
&& (attacks_bb<ROOK>(rto, pieces() ^ from ^ to) & ksq); | (attacks_bb<BISHOP>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP));
} }
case CASTLING:
{
Square kfrom = from;
Square rfrom = to; // Castling is encoded as 'king captures the rook'
Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1);
Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1);
return (attacks_bb<ROOK>(rto) & square<KING>(~sideToMove))
&& (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
}
default:
assert(false);
return false;
} }
} }
@@ -700,8 +719,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
++st->pliesFromNull; ++st->pliesFromNull;
// Used by NNUE // Used by NNUE
st->accumulator.state[WHITE] = Eval::NNUE::EMPTY; st->accumulator.computed_accumulation = false;
st->accumulator.state[BLACK] = Eval::NNUE::EMPTY; st->accumulator.computed_score = false;
PieceId dp0 = PIECE_ID_NONE;
PieceId dp1 = PIECE_ID_NONE;
auto& dp = st->dirtyPiece; auto& dp = st->dirtyPiece;
dp.dirty_num = 1; dp.dirty_num = 1;
@@ -710,7 +731,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m); Square to = to_sq(m);
Piece pc = piece_on(from); Piece pc = piece_on(from);
Piece captured = type_of(m) == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to); Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to);
assert(color_of(pc) == us); assert(color_of(pc) == us);
assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us)); assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us));
@@ -736,7 +757,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// update non-pawn material. // update non-pawn material.
if (type_of(captured) == PAWN) if (type_of(captured) == PAWN)
{ {
if (type_of(m) == EN_PASSANT) if (type_of(m) == ENPASSANT)
{ {
capsq -= pawn_push(us); capsq -= pawn_push(us);
@@ -754,16 +775,18 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
if (Eval::useNNUE) if (Eval::useNNUE)
{ {
dp.dirty_num = 2; // 1 piece moved, 1 piece captured dp.dirty_num = 2; // 2 pieces moved
dp.piece[1] = captured; dp1 = piece_id_on(capsq);
dp.from[1] = capsq; dp.pieceId[1] = dp1;
dp.to[1] = SQ_NONE; 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);
if (type_of(m) == EN_PASSANT) if (type_of(m) == ENPASSANT)
board[capsq] = NO_PIECE; board[capsq] = NO_PIECE;
// Update material hash key and prefetch access to materialTable // Update material hash key and prefetch access to materialTable
@@ -798,9 +821,11 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
{ {
if (Eval::useNNUE) if (Eval::useNNUE)
{ {
dp.piece[0] = pc; dp0 = piece_id_on(from);
dp.from[0] = from; dp.pieceId[0] = dp0;
dp.to[0] = to; 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);
@@ -809,7 +834,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// 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)
{ {
// Set en passant square if the moved pawn can be captured // Set en-passant square if the moved pawn can be captured
if ( (int(to) ^ int(from)) == 16 if ( (int(to) ^ int(from)) == 16
&& (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN))) && (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN)))
{ {
@@ -829,12 +854,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
if (Eval::useNNUE) if (Eval::useNNUE)
{ {
// Promoting pawn to SQ_NONE, promoted piece from SQ_NONE dp0 = piece_id_on(to);
dp.to[0] = SQ_NONE; evalList.put_piece(dp0, to, promotion);
dp.piece[dp.dirty_num] = promotion; dp.new_piece[0] = evalList.piece_with_id(dp0);
dp.from[dp.dirty_num] = SQ_NONE;
dp.to[dp.dirty_num] = to;
dp.dirty_num++;
} }
// Update hash keys // Update hash keys
@@ -928,11 +950,17 @@ 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;
if (type_of(m) == EN_PASSANT) if (type_of(m) == ENPASSANT)
{ {
capsq -= pawn_push(us); capsq -= pawn_push(us);
@@ -944,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);
}
} }
} }
@@ -965,16 +1001,32 @@ 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 (Do && Eval::useNNUE) if (Eval::useNNUE)
{ {
PieceId dp0, dp1;
auto& dp = st->dirtyPiece; auto& dp = st->dirtyPiece;
dp.piece[0] = make_piece(us, KING); dp.dirty_num = 2; // 2 pieces moved
dp.from[0] = from;
dp.to[0] = to; if (Do)
dp.piece[1] = make_piece(us, ROOK); {
dp.from[1] = rfrom; dp0 = piece_id_on(from);
dp.to[1] = rto; dp1 = piece_id_on(rfrom);
dp.dirty_num = 2; 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
@@ -994,16 +1046,17 @@ void Position::do_null_move(StateInfo& newSt) {
assert(!checkers()); assert(!checkers());
assert(&newSt != st); assert(&newSt != st);
std::memcpy(&newSt, st, offsetof(StateInfo, accumulator)); 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;
st->dirtyPiece.dirty_num = 0;
st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator()
st->accumulator.state[WHITE] = Eval::NNUE::EMPTY;
st->accumulator.state[BLACK] = Eval::NNUE::EMPTY;
if (st->epSquare != SQ_NONE) if (st->epSquare != SQ_NONE)
{ {
st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
@@ -1011,7 +1064,7 @@ void Position::do_null_move(StateInfo& newSt) {
} }
st->key ^= Zobrist::side; st->key ^= Zobrist::side;
prefetch(TT.first_entry(key())); prefetch(TT.first_entry(st->key));
++st->rule50; ++st->rule50;
st->pliesFromNull = 0; st->pliesFromNull = 0;
@@ -1036,7 +1089,7 @@ void Position::undo_null_move() {
/// Position::key_after() computes the new hash key after the given move. Needed /// Position::key_after() computes the new hash key after the given move. Needed
/// for speculative prefetch. It doesn't recognize special moves like castling, /// for speculative prefetch. It doesn't recognize special moves like castling,
/// en passant and promotions. /// en-passant and promotions.
Key Position::key_after(Move m) const { Key Position::key_after(Move m) const {
@@ -1061,7 +1114,7 @@ bool Position::see_ge(Move m, Value threshold) const {
assert(is_ok(m)); assert(is_ok(m));
// Only deal with normal moves, assume others pass a simple SEE // Only deal with normal moves, assume others pass a simple see
if (type_of(m) != NORMAL) if (type_of(m) != NORMAL)
return VALUE_ZERO >= threshold; return VALUE_ZERO >= threshold;
@@ -1092,8 +1145,8 @@ bool Position::see_ge(Move m, Value threshold) const {
// Don't allow pinned pieces to attack (except the king) as long as // Don't allow pinned pieces to attack (except the king) as long as
// there are pinners on their original square. // there are pinners on their original square.
if (pinners(~stm) & occupied) if (st->pinners[~stm] & occupied)
stmAttackers &= ~blockers_for_king(stm); stmAttackers &= ~st->blockersForKing[stm];
if (!stmAttackers) if (!stmAttackers)
break; break;
@@ -1313,17 +1366,21 @@ bool Position::pos_is_ok() const {
assert(0 && "pos_is_ok: Bitboards"); assert(0 && "pos_is_ok: Bitboards");
StateInfo si = *st; StateInfo si = *st;
ASSERT_ALIGNED(&si, Eval::NNUE::kCacheLineSize);
set_state(&si); set_state(&si);
if (std::memcmp(&si, st, sizeof(StateInfo))) if (std::memcmp(&si, st, sizeof(StateInfo)))
assert(0 && "pos_is_ok: State"); assert(0 && "pos_is_ok: State");
for (Piece pc : Pieces) for (Piece pc : Pieces)
{
if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc))) if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))
|| pieceCount[pc] != std::count(board, board + SQUARE_NB, pc)) || pieceCount[pc] != std::count(board, board + SQUARE_NB, pc))
assert(0 && "pos_is_ok: Pieces"); assert(0 && "pos_is_ok: Pieces");
for (int i = 0; i < pieceCount[pc]; ++i)
if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i)
assert(0 && "pos_is_ok: Index");
}
for (Color c : { WHITE, BLACK }) for (Color c : { WHITE, BLACK })
for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE}) for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE})
{ {
+55 -16
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -26,7 +26,6 @@
#include "bitboard.h" #include "bitboard.h"
#include "evaluate.h" #include "evaluate.h"
#include "psqt.h"
#include "types.h" #include "types.h"
#include "nnue/nnue_accumulator.h" #include "nnue/nnue_accumulator.h"
@@ -100,6 +99,7 @@ public:
bool empty(Square s) const; bool empty(Square s) const;
template<PieceType Pt> int count(Color c) const; template<PieceType Pt> int count(Color c) const;
template<PieceType Pt> int count() const; template<PieceType Pt> int count() const;
template<PieceType Pt> const Square* squares(Color c) const;
template<PieceType Pt> Square square(Color c) const; template<PieceType Pt> Square square(Color c) const;
bool is_on_semiopen_file(Color c, Square s) const; bool is_on_semiopen_file(Color c, Square s) const;
@@ -113,8 +113,7 @@ public:
Bitboard checkers() const; Bitboard checkers() const;
Bitboard blockers_for_king(Color c) const; Bitboard blockers_for_king(Color c) const;
Bitboard check_squares(PieceType pt) const; Bitboard check_squares(PieceType pt) const;
Bitboard pinners(Color c) const; bool is_discovery_check_on_king(Color c, Move m) const;
bool is_discovered_check_on_king(Color c, Move m) const;
// Attacks to/from a given square // Attacks to/from a given square
Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s) const;
@@ -171,6 +170,7 @@ public:
// Used by NNUE // Used by NNUE
StateInfo* state() const; 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)
@@ -185,11 +185,16 @@ 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];
Bitboard byColorBB[COLOR_NB]; Bitboard byColorBB[COLOR_NB];
int pieceCount[PIECE_NB]; int pieceCount[PIECE_NB];
Square pieceList[PIECE_NB][16];
int index[SQUARE_NB];
int castlingRightsMask[SQUARE_NB]; int castlingRightsMask[SQUARE_NB];
Square castlingRookSquare[CASTLING_RIGHT_NB]; Square castlingRookSquare[CASTLING_RIGHT_NB];
Bitboard castlingPath[CASTLING_RIGHT_NB]; Bitboard castlingPath[CASTLING_RIGHT_NB];
@@ -199,8 +204,15 @@ 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 {
extern Score psq[PIECE_NB][SQUARE_NB];
}
extern std::ostream& operator<<(std::ostream& os, const Position& pos); extern std::ostream& operator<<(std::ostream& os, const Position& pos);
inline Color Position::side_to_move() const { inline Color Position::side_to_move() const {
@@ -248,9 +260,13 @@ template<PieceType Pt> inline int Position::count() const {
return count<Pt>(WHITE) + count<Pt>(BLACK); return count<Pt>(WHITE) + count<Pt>(BLACK);
} }
template<PieceType Pt> inline const Square* Position::squares(Color c) const {
return pieceList[make_piece(c, Pt)];
}
template<PieceType Pt> inline Square Position::square(Color c) const { template<PieceType Pt> inline Square Position::square(Color c) const {
assert(count<Pt>(c) == 1); assert(pieceCount[make_piece(c, Pt)] == 1);
return lsb(pieces(c, Pt)); return squares<Pt>(c)[0];
} }
inline Square Position::ep_square() const { inline Square Position::ep_square() const {
@@ -293,15 +309,11 @@ inline Bitboard Position::blockers_for_king(Color c) const {
return st->blockersForKing[c]; return st->blockersForKing[c];
} }
inline Bitboard Position::pinners(Color c) const {
return st->pinners[c];
}
inline Bitboard Position::check_squares(PieceType pt) const { inline Bitboard Position::check_squares(PieceType pt) const {
return st->checkSquares[pt]; return st->checkSquares[pt];
} }
inline bool Position::is_discovered_check_on_king(Color c, Move m) const { inline bool Position::is_discovery_check_on_king(Color c, Move m) const {
return st->blockersForKing[c] & from_sq(m); return st->blockersForKing[c] & from_sq(m);
} }
@@ -319,8 +331,7 @@ inline int Position::pawns_on_same_color_squares(Color c, Square s) const {
} }
inline Key Position::key() const { inline Key Position::key() const {
return st->rule50 < 14 ? st->key return st->key;
: st->key ^ make_key((st->rule50 - 14) / 8);
} }
inline Key Position::pawn_key() const { inline Key Position::pawn_key() const {
@@ -369,7 +380,7 @@ inline bool Position::capture_or_promotion(Move m) const {
inline bool Position::capture(Move m) const { inline bool Position::capture(Move m) const {
assert(is_ok(m)); assert(is_ok(m));
// Castling is encoded as "king captures rook" // Castling is encoded as "king captures rook"
return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == EN_PASSANT; return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT;
} }
inline Piece Position::captured_piece() const { inline Piece Position::captured_piece() const {
@@ -385,25 +396,35 @@ inline void Position::put_piece(Piece pc, Square s) {
board[s] = pc; board[s] = pc;
byTypeBB[ALL_PIECES] |= byTypeBB[type_of(pc)] |= s; byTypeBB[ALL_PIECES] |= byTypeBB[type_of(pc)] |= s;
byColorBB[color_of(pc)] |= s; byColorBB[color_of(pc)] |= s;
pieceCount[pc]++; index[s] = pieceCount[pc]++;
pieceList[pc][index[s]] = s;
pieceCount[make_piece(color_of(pc), ALL_PIECES)]++; pieceCount[make_piece(color_of(pc), ALL_PIECES)]++;
psq += PSQT::psq[pc][s]; psq += PSQT::psq[pc][s];
} }
inline void Position::remove_piece(Square s) { inline void Position::remove_piece(Square s) {
// WARNING: This is not a reversible operation. If we remove a piece in
// do_move() and then replace it in undo_move() we will put it at the end of
// the list and not in its original place, it means index[] and pieceList[]
// are not invariant to a do_move() + undo_move() sequence.
Piece pc = board[s]; Piece pc = board[s];
byTypeBB[ALL_PIECES] ^= s; byTypeBB[ALL_PIECES] ^= s;
byTypeBB[type_of(pc)] ^= s; byTypeBB[type_of(pc)] ^= s;
byColorBB[color_of(pc)] ^= s; byColorBB[color_of(pc)] ^= s;
/* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */ /* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */
pieceCount[pc]--; Square lastSquare = pieceList[pc][--pieceCount[pc]];
index[lastSquare] = index[s];
pieceList[pc][index[lastSquare]] = lastSquare;
pieceList[pc][pieceCount[pc]] = SQ_NONE;
pieceCount[make_piece(color_of(pc), ALL_PIECES)]--; pieceCount[make_piece(color_of(pc), ALL_PIECES)]--;
psq -= PSQT::psq[pc][s]; psq -= PSQT::psq[pc][s];
} }
inline void Position::move_piece(Square from, Square to) { inline void Position::move_piece(Square from, Square to) {
// index[from] is not updated and becomes stale. This works as long as index[]
// is accessed just by known occupied squares.
Piece pc = board[from]; Piece pc = board[from];
Bitboard fromTo = from | to; Bitboard fromTo = from | to;
byTypeBB[ALL_PIECES] ^= fromTo; byTypeBB[ALL_PIECES] ^= fromTo;
@@ -411,6 +432,8 @@ inline void Position::move_piece(Square from, Square to) {
byColorBB[color_of(pc)] ^= fromTo; byColorBB[color_of(pc)] ^= fromTo;
board[from] = NO_PIECE; board[from] = NO_PIECE;
board[to] = pc; board[to] = pc;
index[to] = index[from];
pieceList[pc][index[to]] = to;
psq += PSQT::psq[pc][to] - PSQT::psq[pc][from]; psq += PSQT::psq[pc][to] - PSQT::psq[pc][from];
} }
@@ -423,4 +446,20 @@ inline StateInfo* Position::state() const {
return st; 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
+35 -41
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -16,22 +16,19 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "psqt.h"
#include <algorithm> #include <algorithm>
#include "bitboard.h"
#include "types.h" #include "types.h"
#include "bitboard.h"
namespace PSQT {
namespace #define S(mg, eg) make_score(mg, eg)
{
auto constexpr S = make_score; // Bonus[PieceType][Square / 2] contains Piece-Square scores. For each piece
// type on a given square a (middlegame, endgame) score pair is assigned. Table
// 'Bonus' contains Piece-Square parameters. // is defined for files A..D and white side: it is symmetric for black side and
// Scores are explicit for files A to D, implicitly mirrored for E to H. // second half of the files.
constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
{ }, { },
{ }, { },
@@ -46,14 +43,14 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
{ S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) } { S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) }
}, },
{ // Bishop { // Bishop
{ S(-37,-40), S(-4 ,-21), S( -6,-26), S(-16, -8) }, { S(-53,-57), S( -5,-30), S( -8,-37), S(-23,-12) },
{ S(-11,-26), S( 6, -9), S( 13,-12), S( 3, 1) }, { S(-15,-37), S( 8,-13), S( 19,-17), S( 4, 1) },
{ S(-5 ,-11), S( 15, -1), S( -4, -1), S( 12, 7) }, { S( -7,-16), S( 21, -1), S( -5, -2), S( 17, 10) },
{ S(-4 ,-14), S( 8, -4), S( 18, 0), S( 27, 12) }, { S( -5,-20), S( 11, -6), S( 25, 0), S( 39, 17) },
{ S(-8 ,-12), S( 20, -1), S( 15,-10), S( 22, 11) }, { S(-12,-17), S( 29, -1), S( 22,-14), S( 31, 15) },
{ S(-11,-21), S( 4, 4), S( 1, 3), S( 8, 4) }, { S(-16,-30), S( 6, 6), S( 1, 4), S( 11, 6) },
{ S(-12,-22), S(-10,-14), S( 4, -1), S( 0, 1) }, { S(-17,-31), S(-14,-20), S( 5, -1), S( 0, 1) },
{ S(-34,-32), S( 1,-29), S(-10,-26), S(-16,-17) } { S(-48,-46), S( 1,-42), S(-14,-37), S(-23,-24) }
}, },
{ // Rook { // Rook
{ S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) }, { S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) },
@@ -67,13 +64,13 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
}, },
{ // Queen { // Queen
{ S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) }, { S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) },
{ S(-3,-54), S( 5,-31), S( 8,-22), S(12, -4) }, { S(-3,-55), S( 5,-31), S( 8,-22), S(12, -4) },
{ S(-3,-39), S( 6,-18), S(13, -9), S( 7, 3) }, { S(-3,-39), S( 6,-18), S(13, -9), S( 7, 3) },
{ S( 4,-23), S( 5, -3), S( 9, 13), S( 8, 24) }, { S( 4,-23), S( 5, -3), S( 9, 13), S( 8, 24) },
{ S( 0,-29), S(14, -6), S(12, 9), S( 5, 21) }, { S( 0,-29), S(14, -6), S(12, 9), S( 5, 21) },
{ S(-4,-38), S(10,-18), S( 6,-11), S( 8, 1) }, { S(-4,-38), S(10,-18), S( 6,-12), S( 8, 1) },
{ S(-5,-50), S( 6,-27), S(10,-24), S( 8, -8) }, { S(-5,-50), S( 6,-27), S(10,-24), S( 8, -8) },
{ S(-2,-74), S(-2,-52), S( 1,-43), S(-2,-34) } { S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) }
}, },
{ // King { // King
{ S(271, 1), S(327, 45), S(271, 85), S(198, 76) }, { S(271, 1), S(327, 45), S(271, 85), S(198, 76) },
@@ -90,22 +87,19 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
constexpr Score PBonus[RANK_NB][FILE_NB] = constexpr Score PBonus[RANK_NB][FILE_NB] =
{ // Pawn (asymmetric distribution) { // Pawn (asymmetric distribution)
{ }, { },
{ S( 2, -8), S( 4, -6), S( 11, 9), S( 18, 5), S( 16, 16), S( 21, 6), S( 9, -6), S( -3,-18) }, { 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, -9), S(-15, -7), S( 11,-10), S( 15, 5), S( 31, 2), S( 23, 3), S( 6, -8), S(-20, -5) }, { 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( -3, 7), S(-20, 1), S( 8, -8), S( 19, -2), S( 39,-14), S( 17,-13), S( 2,-11), S( -5, -6) }, { 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( 11, 12), S( -4, 6), S(-11, 2), S( 2, -6), S( 11, -5), S( 0, -4), S(-12, 14), S( 5, 9) }, { 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( 3, 27), S(-11, 18), S( -6, 19), S( 22, 29), S( -8, 30), S( -5, 9), S(-14, 8), S(-11, 14) }, { 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, -1), S( 6,-14), S( -2, 13), S(-11, 22), S( 4, 24), S(-14, 17), S( 10, 7), S( -9, 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) }
}; };
} // namespace #undef S
namespace PSQT
{
Score psq[PIECE_NB][SQUARE_NB]; Score psq[PIECE_NB][SQUARE_NB];
// PSQT::init() initializes piece-square tables: the white halves of the tables are // PSQT::init() initializes piece-square tables: the white halves of the tables are
// copied from Bonus[] and PBonus[], adding the piece value, then the black halves of // copied from Bonus[] and PBonus[], adding the piece value, then the black halves of
// the tables are initialized by flipping and changing the sign of the white scores. // the tables are initialized by flipping and changing the sign of the white scores.
@@ -113,15 +107,15 @@ void init() {
for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING}) for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING})
{ {
Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
for (Square s = SQ_A1; s <= SQ_H8; ++s) for (Square s = SQ_A1; s <= SQ_H8; ++s)
{ {
File f = File(edge_distance(file_of(s))); File f = File(edge_distance(file_of(s)));
psq[ pc][s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)] psq[ pc][s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)]
: Bonus[pc][rank_of(s)][f]); : Bonus[pc][rank_of(s)][f]);
psq[~pc][flip_rank(s)] = -psq[pc][s]; psq[~pc][flip_rank(s)] = -psq[pc][s];
} }
} }
} }
-38
View File
@@ -1,38 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2021 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/>.
*/
#ifndef PSQT_H_INCLUDED
#define PSQT_H_INCLUDED
#include "types.h"
namespace PSQT
{
extern Score psq[PIECE_NB][SQUARE_NB];
// Fill psqt array from a set of internally linked parameters
extern void init();
} // namespace PSQT
#endif // PSQT_H_INCLUDED
+204 -258
View File
File diff suppressed because it is too large Load Diff
+2 -3
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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,8 +48,6 @@ struct Stack {
int statScore; int statScore;
int moveCount; int moveCount;
bool inCheck; bool inCheck;
bool ttPv;
bool ttHit;
}; };
@@ -71,6 +69,7 @@ struct RootMove {
Value previousScore = -VALUE_INFINITE; Value previousScore = -VALUE_INFINITE;
int selDepth = 0; int selDepth = 0;
int tbRank = 0; int tbRank = 0;
int bestMoveCount = 0;
Value tbScore; Value tbScore;
std::vector<Move> pv; std::vector<Move> pv;
}; };
+6 -8
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -223,9 +223,7 @@ public:
*mapping = statbuf.st_size; *mapping = statbuf.st_size;
*baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); *baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
#if defined(MADV_RANDOM)
madvise(*baseAddress, statbuf.st_size, MADV_RANDOM); madvise(*baseAddress, statbuf.st_size, MADV_RANDOM);
#endif
::close(fd); ::close(fd);
if (*baseAddress == MAP_FAILED) if (*baseAddress == MAP_FAILED)
@@ -760,7 +758,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
if (entry->hasPawns) { if (entry->hasPawns) {
idx = LeadPawnIdx[leadPawnsCnt][squares[0]]; idx = LeadPawnIdx[leadPawnsCnt][squares[0]];
std::stable_sort(squares + 1, squares + leadPawnsCnt, pawns_comp); std::sort(squares + 1, squares + leadPawnsCnt, pawns_comp);
for (int i = 1; i < leadPawnsCnt; ++i) for (int i = 1; i < leadPawnsCnt; ++i)
idx += Binomial[i][MapPawns[squares[i]]]; idx += Binomial[i][MapPawns[squares[i]]];
@@ -861,7 +859,7 @@ encode_remaining:
while (d->groupLen[++next]) while (d->groupLen[++next])
{ {
std::stable_sort(groupSq, groupSq + d->groupLen[next]); std::sort(groupSq, groupSq + d->groupLen[next]);
uint64_t n = 0; uint64_t n = 0;
// Map down a square if "comes later" than a square in the previous // Map down a square if "comes later" than a square in the previous
@@ -1000,7 +998,7 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) {
// so that d->lowestSym[i] >= d->lowestSym[i+1] (when read as LittleEndian). // so that d->lowestSym[i] >= d->lowestSym[i+1] (when read as LittleEndian).
// Starting from this we compute a base64[] table indexed by symbol length // Starting from this we compute a base64[] table indexed by symbol length
// and containing 64 bit values so that d->base64[i] >= d->base64[i+1]. // and containing 64 bit values so that d->base64[i] >= d->base64[i+1].
// See https://en.wikipedia.org/wiki/Huffman_coding // See http://www.eecs.harvard.edu/~michaelm/E210/huffman.pdf
for (int i = d->base64.size() - 2; i >= 0; --i) { for (int i = d->base64.size() - 2; i >= 0; --i) {
d->base64[i] = (d->base64[i + 1] + number<Sym, LittleEndian>(&d->lowestSym[i]) d->base64[i] = (d->base64[i + 1] + number<Sym, LittleEndian>(&d->lowestSym[i])
- number<Sym, LittleEndian>(&d->lowestSym[i + 1])) / 2; - number<Sym, LittleEndian>(&d->lowestSym[i + 1])) / 2;
@@ -1141,7 +1139,7 @@ void* mapped(TBTable<Type>& e, const Position& pos) {
if (e.ready.load(std::memory_order_acquire)) if (e.ready.load(std::memory_order_acquire))
return e.baseAddress; // Could be nullptr if file does not exist return e.baseAddress; // Could be nullptr if file does not exist
std::scoped_lock<std::mutex> lk(mutex); std::unique_lock<std::mutex> lk(mutex);
if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock
return e.baseAddress; return e.baseAddress;
@@ -1440,7 +1438,7 @@ WDLScore Tablebases::probe_wdl(Position& pos, ProbeState* result) {
// If n = 100 immediately after a capture or pawn move, then the position // If n = 100 immediately after a capture or pawn move, then the position
// is also certainly a win, and during the whole phase until the next // is also certainly a win, and during the whole phase until the next
// capture or pawn move, the inequality to be preserved is // capture or pawn move, the inequality to be preserved is
// dtz + 50-move-counter <= 100. // dtz + 50-movecounter <= 100.
// //
// In short, if a move is available resulting in dtz + 50-move-counter <= 99, // In short, if a move is available resulting in dtz + 50-move-counter <= 99,
// then do not accept moves leading to dtz + 50-move-counter == 100. // then do not accept moves leading to dtz + 50-move-counter == 100.
+1 -1
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
+30 -16
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -51,6 +51,17 @@ Thread::~Thread() {
} }
/// Thread::bestMoveCount(Move move) return best move counter for the given root move
int Thread::best_move_count(Move move) const {
auto rm = std::find(rootMoves.begin() + pvIdx,
rootMoves.begin() + pvLast, move);
return rm != rootMoves.begin() + pvLast ? rm->bestMoveCount : 0;
}
/// Thread::clear() reset histories, usually before a new game /// Thread::clear() reset histories, usually before a new game
void Thread::clear() { void Thread::clear() {
@@ -193,18 +204,21 @@ 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 they are set from // be deduced from a fen string, so set() clears them and to not lose the info
// setupStates->back() later. The rootState is per thread, earlier states are shared // we need to backup and later restore setupStates->back(). Note that setupStates
// since they are read-only. // is shared by threads but is accessed in read-only mode.
StateInfo tmp = setupStates->back();
for (Thread* th : *this) for (Thread* th : *this)
{ {
th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0; th->nodes = th->tbHits = 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(), &th->rootState, th); th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
th->rootState = setupStates->back();
} }
setupStates->back() = tmp;
main()->start_searching(); main()->start_searching();
} }
@@ -224,16 +238,16 @@ Thread* ThreadPool::get_best_thread() const {
votes[th->rootMoves[0].pv[0]] += votes[th->rootMoves[0].pv[0]] +=
(th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth);
if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY)
{ {
// Make sure we pick the shortest mate / TB conversion or stave off mate the longest // Make sure we pick the shortest mate / TB conversion or stave off mate the longest
if (th->rootMoves[0].score > bestThread->rootMoves[0].score) if (th->rootMoves[0].score > bestThread->rootMoves[0].score)
bestThread = th; bestThread = th;
} }
else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY
|| ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY
&& votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]])) && votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]))
bestThread = th; bestThread = th;
} }
return bestThread; return bestThread;
+2 -3
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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,6 +54,7 @@ public:
void idle_loop(); void idle_loop();
void start_searching(); void start_searching();
void wait_for_search_finished(); void wait_for_search_finished();
int best_move_count(Move move) const;
Pawns::Table pawnsTable; Pawns::Table pawnsTable;
Material::Table materialTable; Material::Table materialTable;
@@ -64,7 +65,6 @@ public:
std::atomic<uint64_t> nodes, tbHits, bestMoveChanges; std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
Position rootPos; Position rootPos;
StateInfo rootState;
Search::RootMoves rootMoves; Search::RootMoves rootMoves;
Depth rootDepth, completedDepth; Depth rootDepth, completedDepth;
CounterMoveHistory counterMoves; CounterMoveHistory counterMoves;
@@ -73,7 +73,6 @@ public:
CapturePieceToHistory captureHistory; CapturePieceToHistory captureHistory;
ContinuationHistory continuationHistory[2][2]; ContinuationHistory continuationHistory[2][2];
Score contempt; Score contempt;
int failedHighCnt;
}; };
+2 -2
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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,7 +27,7 @@
/// The implementation calls pthread_create() with the stack size parameter /// The implementation calls pthread_create() with the stack size parameter
/// equal to the linux 8MB default, on platforms that support it. /// equal to the linux 8MB default, on platforms that support it.
#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(USE_PTHREADS) #if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__)
#include <pthread.h> #include <pthread.h>
+10 -10
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -38,9 +38,9 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
TimePoint slowMover = TimePoint(Options["Slow Mover"]); TimePoint slowMover = TimePoint(Options["Slow Mover"]);
TimePoint npmsec = TimePoint(Options["nodestime"]); TimePoint npmsec = TimePoint(Options["nodestime"]);
// optScale is a percentage of available time to use for the current move. // opt_scale is a percentage of available time to use for the current move.
// maxScale is a multiplier applied to optimumTime. // max_scale is a multiplier applied to optimumTime.
double optScale, maxScale; double opt_scale, max_scale;
// If we have to play in 'nodes as time' mode, then convert from time // If we have to play in 'nodes as time' mode, then convert from time
// to nodes, and use resulting values in time management formulas. // to nodes, and use resulting values in time management formulas.
@@ -75,22 +75,22 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
// game time for the current move, so also cap to 20% of available game time. // game time for the current move, so also cap to 20% of available game time.
if (limits.movestogo == 0) if (limits.movestogo == 0)
{ {
optScale = std::min(0.0084 + std::pow(ply + 3.0, 0.5) * 0.0042, opt_scale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0,
0.2 * limits.time[us] / double(timeLeft)); 0.2 * limits.time[us] / double(timeLeft));
maxScale = std::min(7.0, 4.0 + ply / 12.0); max_scale = std::min(7.0, 4.0 + ply / 12.0);
} }
// x moves in y seconds (+ z increment) // x moves in y seconds (+ z increment)
else else
{ {
optScale = std::min((0.8 + ply / 128.0) / mtg, opt_scale = std::min((0.8 + ply / 128.0) / mtg,
0.8 * limits.time[us] / double(timeLeft)); 0.8 * limits.time[us] / double(timeLeft));
maxScale = std::min(6.3, 1.5 + 0.11 * mtg); max_scale = std::min(6.3, 1.5 + 0.11 * mtg);
} }
// Never use more than 80% of the available time for this move // Never use more than 80% of the available time for this move
optimumTime = TimePoint(optScale * timeLeft); optimumTime = TimePoint(opt_scale * timeLeft);
maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime)); maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, max_scale * optimumTime));
if (Options["Ponder"]) if (Options["Ponder"])
optimumTime += optimumTime / 4; optimumTime += optimumTime / 4;
+1 -1
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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 -23
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -37,19 +37,18 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev)
if (m || (uint16_t)k != key16) if (m || (uint16_t)k != key16)
move16 = (uint16_t)m; move16 = (uint16_t)m;
// Overwrite less valuable entries (cheapest checks first) // Overwrite less valuable entries
if (b == BOUND_EXACT if ((uint16_t)k != key16
|| (uint16_t)k != key16 || d - DEPTH_OFFSET > depth8 - 4
|| d - DEPTH_OFFSET > depth8 - 4) || b == BOUND_EXACT)
{ {
assert(d > DEPTH_OFFSET); assert(d >= DEPTH_OFFSET);
assert(d < 256 + DEPTH_OFFSET);
key16 = (uint16_t)k; key16 = (uint16_t)k;
depth8 = (uint8_t)(d - DEPTH_OFFSET);
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
value16 = (int16_t)v; value16 = (int16_t)v;
eval16 = (int16_t)ev; eval16 = (int16_t)ev;
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
depth8 = (uint8_t)(d - DEPTH_OFFSET);
} }
} }
@@ -62,12 +61,11 @@ void TranspositionTable::resize(size_t mbSize) {
Threads.main()->wait_for_search_finished(); Threads.main()->wait_for_search_finished();
aligned_large_pages_free(table); aligned_ttmem_free(mem);
clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster);
table = static_cast<Cluster*>(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), mem));
table = static_cast<Cluster*>(aligned_large_pages_alloc(clusterCount * sizeof(Cluster))); if (!mem)
if (!table)
{ {
std::cerr << "Failed to allocate " << mbSize std::cerr << "Failed to allocate " << mbSize
<< "MB for transposition table." << std::endl; << "MB for transposition table." << std::endl;
@@ -121,23 +119,22 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
const uint16_t key16 = (uint16_t)key; // Use the low 16 bits as key inside the cluster const uint16_t key16 = (uint16_t)key; // Use the low 16 bits as key inside the cluster
for (int i = 0; i < ClusterSize; ++i) for (int i = 0; i < ClusterSize; ++i)
if (tte[i].key16 == key16 || !tte[i].depth8) if (!tte[i].key16 || tte[i].key16 == key16)
{ {
tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & (GENERATION_DELTA - 1))); // Refresh tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & 0x7)); // Refresh
return found = (bool)tte[i].depth8, &tte[i]; return found = (bool)tte[i].key16, &tte[i];
} }
// Find an entry to be replaced according to the replacement strategy // Find an entry to be replaced according to the replacement strategy
TTEntry* replace = tte; TTEntry* replace = tte;
for (int i = 1; i < ClusterSize; ++i) for (int i = 1; i < ClusterSize; ++i)
// Due to our packed storage format for generation and its cyclic // Due to our packed storage format for generation and its cyclic
// nature we add GENERATION_CYCLE (256 is the modulus, plus what // nature we add 263 (256 is the modulus plus 7 to keep the unrelated
// is needed to keep the unrelated lowest n bits from affecting // lowest three bits from affecting the result) to calculate the entry
// the result) to calculate the entry age correctly even after // age correctly even after generation8 overflows into the next cycle.
// generation8 overflows into the next cycle. if ( replace->depth8 - ((263 + generation8 - replace->genBound8) & 0xF8)
if ( replace->depth8 - ((GENERATION_CYCLE + generation8 - replace->genBound8) & GENERATION_MASK) > tte[i].depth8 - ((263 + generation8 - tte[i].genBound8) & 0xF8))
> tte[i].depth8 - ((GENERATION_CYCLE + generation8 - tte[i].genBound8) & GENERATION_MASK))
replace = &tte[i]; replace = &tte[i];
return found = false, replace; return found = false, replace;
@@ -152,7 +149,7 @@ int TranspositionTable::hashfull() const {
int cnt = 0; int cnt = 0;
for (int i = 0; i < 1000; ++i) for (int i = 0; i < 1000; ++i)
for (int j = 0; j < ClusterSize; ++j) for (int j = 0; j < ClusterSize; ++j)
cnt += table[i].entry[j].depth8 && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8; cnt += (table[i].entry[j].genBound8 & 0xF8) == generation8;
return cnt / ClusterSize; return cnt / ClusterSize;
} }
+10 -15
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -25,13 +25,13 @@
/// TTEntry struct is the 10 bytes transposition table entry, defined as below: /// TTEntry struct is the 10 bytes transposition table entry, defined as below:
/// ///
/// key 16 bit /// key 16 bit
/// depth 8 bit
/// generation 5 bit
/// pv node 1 bit
/// bound type 2 bit
/// move 16 bit /// move 16 bit
/// value 16 bit /// value 16 bit
/// eval value 16 bit /// eval value 16 bit
/// generation 5 bit
/// pv node 1 bit
/// bound type 2 bit
/// depth 8 bit
struct TTEntry { struct TTEntry {
@@ -47,11 +47,11 @@ private:
friend class TranspositionTable; friend class TranspositionTable;
uint16_t key16; uint16_t key16;
uint8_t depth8;
uint8_t genBound8;
uint16_t move16; uint16_t move16;
int16_t value16; int16_t value16;
int16_t eval16; int16_t eval16;
uint8_t genBound8;
uint8_t depth8;
}; };
@@ -72,15 +72,9 @@ class TranspositionTable {
static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size"); static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size");
// Constants used to refresh the hash table periodically
static constexpr unsigned GENERATION_BITS = 3; // nb of bits reserved for other things
static constexpr int GENERATION_DELTA = (1 << GENERATION_BITS); // increment for generation field
static constexpr int GENERATION_CYCLE = 255 + (1 << GENERATION_BITS); // cycle length
static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF; // mask to pull out generation number
public: public:
~TranspositionTable() { aligned_large_pages_free(table); } ~TranspositionTable() { aligned_ttmem_free(mem); }
void new_search() { generation8 += GENERATION_DELTA; } // Lower bits are used for other things void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound
TTEntry* probe(const Key key, bool& found) const; TTEntry* probe(const Key key, bool& found) const;
int hashfull() const; int hashfull() const;
void resize(size_t mbSize); void resize(size_t mbSize);
@@ -95,6 +89,7 @@ private:
size_t clusterCount; size_t clusterCount;
Cluster* table; Cluster* table;
void* mem;
uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 uint8_t generation8; // Size must be not bigger than TTEntry::genBound8
}; };
+1 -1
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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 -4
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -130,9 +130,9 @@ class Tune {
SetRange range; SetRange range;
}; };
// Our facility to fill the container, each Entry corresponds to a parameter // Our facilty to fill the container, each Entry corresponds to a parameter to tune.
// to tune. We use variadic templates to deal with an unspecified number of // We use variadic templates to deal with an unspecified number of entries, each one
// entries, each one of a possible different type. // of a possible different type.
static std::string next(std::string& names, bool pop = true); static std::string next(std::string& names, bool pop = true);
int add(const SetRange&, std::string&&) { return 0; } int add(const SetRange&, std::string&&) { return 0; }
+115 -22
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -57,12 +57,6 @@
/// _WIN32 Building on Windows (any) /// _WIN32 Building on Windows (any)
/// _WIN64 Building on Windows 64 bit /// _WIN64 Building on Windows 64 bit
#if defined(__GNUC__ ) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <= 2)) && defined(_WIN32) && !defined(__clang__)
#define ALIGNAS_ON_STACK_VARIABLES_BROKEN
#endif
#define ASSERT_ALIGNED(ptr, alignment) assert(reinterpret_cast<uintptr_t>(ptr) % alignment == 0)
#if defined(_WIN64) && defined(_MSC_VER) // No Makefile used #if defined(_WIN64) && defined(_MSC_VER) // No Makefile used
# include <intrin.h> // Microsoft header for _BitScanForward64() # include <intrin.h> // Microsoft header for _BitScanForward64()
# define IS_64BIT # define IS_64BIT
@@ -113,7 +107,7 @@ constexpr int MAX_PLY = 246;
/// bit 6-11: origin square (from 0 to 63) /// bit 6-11: origin square (from 0 to 63)
/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2) /// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
/// bit 14-15: special move flag: promotion (1), en passant (2), castling (3) /// bit 14-15: special move flag: promotion (1), en passant (2), castling (3)
/// NOTE: en passant bit is set only when a pawn can be captured /// NOTE: EN-PASSANT bit is set only when a pawn can be captured
/// ///
/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in /// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in
/// any normal move destination square is always different from origin square /// any normal move destination square is always different from origin square
@@ -127,7 +121,7 @@ enum Move : int {
enum MoveType { enum MoveType {
NORMAL, NORMAL,
PROMOTION = 1 << 14, PROMOTION = 1 << 14,
EN_PASSANT = 2 << 14, ENPASSANT = 2 << 14,
CASTLING = 3 << 14 CASTLING = 3 << 14
}; };
@@ -184,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 = 126, PawnValueEg = 208, PawnValueMg = 124, PawnValueEg = 206,
KnightValueMg = 781, KnightValueEg = 854, KnightValueMg = 781, KnightValueEg = 854,
BishopValueMg = 825, BishopValueEg = 915, BishopValueMg = 825, BishopValueEg = 915,
RookValueMg = 1276, RookValueEg = 1380, RookValueMg = 1276, RookValueEg = 1380,
@@ -202,11 +196,27 @@ enum PieceType {
enum Piece { enum Piece {
NO_PIECE, NO_PIECE,
W_PAWN = PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN = PAWN + 8, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING, B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
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 },
@@ -222,8 +232,7 @@ enum : int {
DEPTH_QS_RECAPTURES = -5, DEPTH_QS_RECAPTURES = -5,
DEPTH_NONE = -6, DEPTH_NONE = -6,
DEPTH_OFFSET = DEPTH_NONE
DEPTH_OFFSET = -7 // value used only for TT entry occupancy check
}; };
enum Square : int { enum Square : int {
@@ -261,20 +270,93 @@ 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
}; };
// Keep track of what a move changes on the board (used by NNUE) // 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 { struct DirtyPiece {
// Number of changed pieces // Number of changed pieces
int dirty_num; int dirty_num;
// Max 3 pieces can change in one move. A promotion with capture moves // The ids of changed pieces, max. 2 pieces can change in one move
// both the pawn and the captured piece to SQ_NONE and the piece promoted PieceId pieceId[2];
// to from SQ_NONE to the capture square.
Piece piece[3];
// From and to squares, which may be SQ_NONE // What changed from the piece with that piece number
Square from[3]; ExtPieceSquare old_piece[2];
Square to[3]; 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).
@@ -324,6 +406,8 @@ ENABLE_FULL_OPERATORS_ON(Value)
ENABLE_FULL_OPERATORS_ON(Direction) ENABLE_FULL_OPERATORS_ON(Direction)
ENABLE_INCR_OPERATORS_ON(Piece) 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)
@@ -412,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;
} }
@@ -448,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;
} }
+4 -4
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -85,7 +85,7 @@ namespace {
Position p; Position p;
p.set(pos.fen(), Options["UCI_Chess960"], &states->back(), Threads.main()); p.set(pos.fen(), Options["UCI_Chess960"], &states->back(), Threads.main());
Eval::NNUE::verify(); Eval::verify_NNUE();
sync_cout << "\n" << Eval::trace(p) << sync_endl; sync_cout << "\n" << Eval::trace(p) << sync_endl;
} }
@@ -170,7 +170,7 @@ namespace {
if (token == "go" || token == "eval") if (token == "go" || token == "eval")
{ {
cerr << "\nPosition: " << cnt++ << '/' << num << " (" << pos.fen() << ")" << endl; cerr << "\nPosition: " << cnt++ << '/' << num << endl;
if (token == "go") if (token == "go")
{ {
go(pos, is, states); go(pos, is, states);
@@ -211,7 +211,7 @@ namespace {
double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3];
// Transform eval to centipawns with limited range // Transform eval to centipawns with limited range
double x = std::clamp(double(100 * v) / PawnValueEg, -1000.0, 1000.0); double x = Utility::clamp(double(100 * v) / PawnValueEg, -1000.0, 1000.0);
// Return win rate in per mille (rounded to nearest) // Return win rate in per mille (rounded to nearest)
return int(0.5 + 1000 / (1 + std::exp((a - x) / b))); return int(0.5 + 1000 / (1 + std::exp((a - x) / b)));
+1 -1
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
+5 -6
View File
@@ -1,6 +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-2021 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
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
@@ -21,7 +21,6 @@
#include <ostream> #include <ostream>
#include <sstream> #include <sstream>
#include "evaluate.h"
#include "misc.h" #include "misc.h"
#include "search.h" #include "search.h"
#include "thread.h" #include "thread.h"
@@ -41,8 +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::NNUE::init(); } void on_use_NNUE(const Option& ) { Eval::init_NNUE(); }
void on_eval_file(const Option& ) { Eval::NNUE::init(); } 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 {
@@ -79,8 +78,8 @@ void init(OptionsMap& o) {
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(true, on_use_NNUE); o["Use NNUE"] << Option(false, on_use_NNUE);
o["EvalFile"] << Option(EvalFileDefaultName, on_eval_file); o["EvalFile"] << Option("nn-97f742aaefcd.nnue", on_eval_file);
} }
+5 -5
View File
@@ -20,7 +20,7 @@ case $1 in
--valgrind-thread) --valgrind-thread)
echo "valgrind-thread testing started" echo "valgrind-thread testing started"
prefix='' prefix=''
exeprefix='valgrind --fair-sched=try --error-exitcode=42' exeprefix='valgrind --error-exitcode=42'
postfix='1>/dev/null' postfix='1>/dev/null'
threads="2" threads="2"
;; ;;
@@ -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 8 default depth" "bench 128 $threads 10 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 240 set timeout 10
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 20\n" send "go depth 30\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 8 default depth\n" send "bench 128 1 10 default depth\n"
send "quit\n" send "quit\n"
expect eof expect eof