Merge branch 'master' into clusterMergeMaster12

fixes minor merge conflicts

looks good:

Score of cluster vs master: 15 - 1 - 134  [0.547] 150
Elo difference: 32.5 +/- 17.5, LOS: 100.0 %, DrawRatio: 89.3 %

for 4 threads against 4x4 threads at 10+0.1s
This commit is contained in:
Joost VandeVondele
2021-03-16 19:38:05 +01:00
63 changed files with 1794 additions and 1290 deletions
+12
View File
@@ -0,0 +1,12 @@
# Files from build
**/*.o
**/*.s
src/.depend
# Built binary
src/stockfish*
src/-lstdc++.res
# Neural network for the NNUE evaluation
**/*.nnue
+10 -2
View File
@@ -19,10 +19,12 @@ 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)
Andy Duplain Andy Duplain
Antoine Champion (antoinechampion)
Aram Tumanian (atumanian) Aram Tumanian (atumanian)
Arjun Temurnikar Arjun Temurnikar
Auguste Pop Auguste Pop
@@ -32,6 +34,7 @@ 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
@@ -43,6 +46,8 @@ Daniel Dugovic (ddugovic)
Dariusz Orzechowski (dorzechowski) Dariusz Orzechowski (dorzechowski)
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
@@ -63,7 +68,6 @@ Gary Heckman (gheckman)
George Sobala (gsobala) George Sobala (gsobala)
gguliash gguliash
Gian-Carlo Pascutto (gcp) Gian-Carlo Pascutto (gcp)
Deshawn Mohan-Smith (GoldenRare)
Gontran Lemaire (gonlem) Gontran Lemaire (gonlem)
Goodkov Vasiliy Aleksandrovich (goodkov) Goodkov Vasiliy Aleksandrovich (goodkov)
Gregor Cramer Gregor Cramer
@@ -85,7 +89,7 @@ Jekaa
Jerry Donald Watson (jerrydonaldwatson) Jerry Donald Watson (jerrydonaldwatson)
jjoshua2 jjoshua2
Jonathan Calovski (Mysseno) Jonathan Calovski (Mysseno)
Jonathan Dumale (SFisGOD) Jonathan Buladas Dumale (SFisGOD)
Joost VandeVondele (vondele) Joost VandeVondele (vondele)
Jörg Oster (joergoster) Jörg Oster (joergoster)
Joseph Ellis (jhellis3) Joseph Ellis (jhellis3)
@@ -97,6 +101,7 @@ 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)
@@ -109,8 +114,10 @@ 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)
@@ -161,6 +168,7 @@ Sergio Vieri (sergiovieri)
sf-x sf-x
Shane Booth (shane31) Shane Booth (shane31)
Shawn Varghese (xXH4CKST3RXx) Shawn Varghese (xXH4CKST3RXx)
Siad Daboul (Topologist)
Stefan Geschwentner (locutus2) Stefan Geschwentner (locutus2)
Stefano Cardanobile (Stefano80) Stefano Cardanobile (Stefano80)
Steinar Gunderson (sesse) Steinar Gunderson (sesse)
+54 -50
View File
@@ -12,7 +12,7 @@ about how to use Stockfish with it.
The Stockfish engine features two evaluation functions for chess, the classical The Stockfish engine features two evaluation functions for chess, the classical
evaluation based on handcrafted terms, and the NNUE evaluation based on efficiently evaluation based on handcrafted terms, and the NNUE evaluation based on efficiently
updateable neural networks. The classical evaluation runs efficiently on almost all updatable neural networks. The classical evaluation runs efficiently on almost all
CPU architectures, while the NNUE evaluation benefits from the vector CPU architectures, while the NNUE evaluation benefits from the vector
intrinsics available on most CPUs (sse2, avx2, neon, or similar). intrinsics available on most CPUs (sse2, avx2, neon, or similar).
@@ -24,6 +24,8 @@ This distribution of Stockfish consists of the following files:
* Readme.md, the file you are currently reading. * Readme.md, the file you are currently reading.
* 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.
@@ -31,17 +33,6 @@ This distribution of Stockfish consists of the following files:
* a file with the .nnue extension, storing the neural network for the NNUE * a file with the .nnue extension, storing the neural network for the NNUE
evaluation. Binary distributions will have this file embedded. evaluation. Binary distributions will have this file embedded.
Note: to use the NNUE evaluation, the additional data file with neural network parameters
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.
## UCI options ## UCI options
Currently, Stockfish has the following UCI options: Currently, Stockfish has the following UCI options:
@@ -53,6 +44,9 @@ 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.
@@ -108,8 +102,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 agressively if you experience too much slowdown to a higher value to probe less aggressively if you experience too much slowdown
(in terms of nps) due to TB probing. (in terms of nps) due to tablebase 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
@@ -139,42 +133,50 @@ 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 and NNUE evaluation ## A note on classical evaluation versus 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 evalutions of millions of positions at moderate search depth. on the evaluations of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward. The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move. of the neural network need to be updated after a typical chess move.
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional [The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
tools to train and develop the NNUE networks. tools to train and develop the NNUE networks. On CPUs supporting modern vector instructions
(avx2 and similar), the NNUE evaluation results in much stronger playing strength, even
if the nodes per second computed by the engine is somewhat lower (roughly 80% of nps
is typical).
On CPUs supporting modern vector instructions (avx2 and similar), the NNUE evaluation Notes:
results in stronger playing strength, even if the nodes per second computed by the engine
is somewhat lower (roughly 60% of nps is typical).
Note that the NNUE evaluation depends on the Stockfish binary and the network parameter 1) the NNUE evaluation depends on the Stockfish binary and the network parameter
file (see EvalFile). Not every parameter file is compatible with a given Stockfish binary. file (see the EvalFile UCI option). Not every parameter file is compatible with a given
The default value of the EvalFile UCI option is the name of a network that is guaranteed Stockfish binary, but the default value of the EvalFile UCI option is the name of a network
to be compatible with that binary. that is guaranteed to be compatible with that binary.
## What to expect from Syzygybases? 2) to use the NNUE evaluation, the additional data file with neural network parameters
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
that it has found a winning line into a tablebase position. 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
@@ -182,14 +184,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 Syzygybases use a variation of the DTM metric (distance-to-mate), while the Syzygy tablebases 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 Syzygybases are counter). This special metric is one of the reasons that the Syzygy tablebases 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.
@@ -227,8 +229,8 @@ to ```rank 0``` only (e.g. ```srun --input=0 --output=0```).
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 nps, but on large hash sizes. Typical increases are 5..10% in terms of nodes per
speed increases up to 30% have been measured. The support is second, but 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.
@@ -236,17 +238,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. Logout/login may be needed on how to enable this privilege, then run [RAMMap](https://docs.microsoft.com/en-us/sysinternals/downloads/rammap)
afterwards. Due to memory fragmentation, it may not always be to double-check that large pages are used. We suggest that you reboot
possible to allocate large pages even when enabled. A reboot your computer after you have enabled large pages, because long Windows
might alleviate this problem. To determine whether large pages sessions suffer from memory fragmentation, which may prevent Stockfish
are in use, see the engine log. from getting large pages: a fresh session is better in this regard.
## Compiling Stockfish yourself from the sources ## Compiling Stockfish yourself from the sources
@@ -261,11 +263,11 @@ targets with corresponding descriptions.
``` ```
cd src cd src
make help make help
make build ARCH=x86-64-modern
make net make net
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.
@@ -301,8 +303,9 @@ 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 in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking) Discussions about Stockfish take place these days mainly in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking)
group and engine testing is done on [Fishtest](https://tests.stockfishchess.org/tests). group and on the [Stockfish Discord channel](https://discord.gg/nv8gDtt).
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.
@@ -310,16 +313,17 @@ 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 that you are free to do almost exactly (GPL v3). Essentially, this means 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 web site, selling friends, making it available for download from your website, 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. If you make any changes to the to where the source code can be found, to generate the exact binary
source code, these changes must also be made available under the GPL. you are distributing. If you make any changes to the source code,
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*.
+187 -171
View File
@@ -1,173 +1,189 @@
Contributors with >10,000 CPU hours as of Sept 2, 2020 Contributors to Fishtest with >10,000 CPU hours, as of Feb 15, 2021.
Thank you! Thank you!
Username CPU Hours Games played Username CPU Hours Games played
-------------------------------------------------- ----------------------------------------------------
noobpwnftw 19352969 1231459677 noobpwnftw 23930906 1560559941
mlang 957168 61657446 dew 1169948 70333008
dew 949885 56893432 mlang 957168 61657446
mibere 703817 46865007 mibere 703840 46867607
crunchy 427035 27344275 tvijlbrief 517888 33379462
cw 416006 27521077 JojoM 515404 30334272
JojoM 415904 24479564 cw 443276 29385549
fastgm 404873 23953472 crunchy 427035 27344275
CSU_Dynasty 335774 22850550 grandphish2 425794 26347253
tvijlbrief 335199 21871270 fastgm 414133 24519696
Fisherman 325053 21786603 gvreuls 377843 24708884
gvreuls 311480 20751516 CSU_Dynasty 338718 23030006
ctoks 275877 18710423 Fisherman 326795 21820747
velislav 241267 15596372 TueRens 313730 19490246
glinscott 217799 13780820 ctoks 298442 20052551
nordlandia 211692 13484886 velislav 270519 17355456
bcross 206213 14934233 bcross 241064 17196165
bking_US 198894 11876016 glinscott 217799 13780820
leszek 189170 11446821 nordlandia 211692 13484886
mgrabiak 183896 11778092 bking_US 198894 11876016
drabel 181408 12489478 drabel 191096 13129722
TueRens 181349 12192000 leszek 189170 11446821
Thanar 179852 12365359 mgrabiak 187153 12013300
vdv 175171 9881246 robal 181389 11539242
robal 166948 10702862 Thanar 179852 12365359
spams 157128 10319326 vdv 175274 9889046
marrco 149947 9376421 spams 157128 10319326
sqrt2 147963 9724586 marrco 150292 9401741
vdbergh 137041 8926915 sqrt2 147963 9724586
CoffeeOne 136294 5004100 CoffeeOne 137086 5022516
malala 136182 8002293 vdbergh 137041 8926915
mhoram 128934 8177193 malala 136182 8002293
davar 122092 7960001 mhoram 132780 8398229
dsmith 122059 7570238 xoto 124729 8652088
xoto 119696 8222144 davar 122092 7960001
grandphish2 116481 7582197 dsmith 122059 7570238
Data 113305 8220352 Data 113305 8220352
BrunoBanani 112960 7436849 BrunoBanani 112960 7436849
ElbertoOne 99028 7023771 pemo 109598 5036441
MaZePallas 98571 6362619 Dantist 106768 6431396
brabos 92118 6186135 MaZePallas 102741 6630419
psk 89957 5984901 ElbertoOne 99028 7023771
sunu 88463 6007033 brabos 92118 6186135
sterni1971 86948 5613788 linrock 90903 6708639
Vizvezdenec 83752 5343724 psk 89957 5984901
BRAVONE 81239 5054681 sunu 88614 6020673
nssy 76497 5259388 sterni1971 86948 5613788
teddybaer 75125 5407666 Vizvezdenec 83761 5344740
Pking_cda 73776 5293873 BRAVONE 81239 5054681
jromang 70695 4940891 nssy 76497 5259388
solarlight 70517 5028306 cuistot 76366 4370584
dv8silencer 70287 3883992 racerschmacer 75753 5442626
Bobo1239 68515 4652287 teddybaer 75125 5407666
racerschmacer 67468 4935996 Pking_cda 73776 5293873
manap 66273 4121774 0x3C33 73133 4670293
tinker 63458 4213726 jromang 72117 5054915
linrock 59082 4516053 solarlight 70517 5028306
robnjr 57262 4053117 dv8silencer 70287 3883992
Freja 56938 3733019 Bobo1239 68515 4652287
ttruscott 56005 3679485 manap 66273 4121774
renouve 53811 3501516 tinker 64321 4268390
cuistot 52532 3014920 robnjr 57262 4053117
finfish 51360 3370515 Freja 56938 3733019
eva42 51272 3599691 ttruscott 56010 3680085
rkl 50759 3840947 rkl 54986 4150767
rap 49985 3219146 renouve 53811 3501516
pb00067 49727 3298270 finfish 51360 3370515
ronaldjerum 47654 3240695 eva42 51272 3599691
bigpen0r 47278 3291647 rap 49985 3219146
biffhero 46564 3111352 pb00067 49727 3298270
VoyagerOne 45386 3445881 amicic 49691 3042481
speedycpu 43842 3003273 ronaldjerum 47654 3240695
jbwiebe 43305 2805433 bigpen0r 47278 3291647
Antihistamine 41788 2761312 biffhero 46564 3111352
mhunt 41735 2691355 VoyagerOne 45476 3452465
eastorwest 40387 2812173 eastorwest 45033 3071805
homyur 39893 2850481 speedycpu 43842 3003273
gri 39871 2515779 jbwiebe 43305 2805433
oryx 38228 2941656 Antihistamine 41788 2761312
0x3C33 37773 2529097 mhunt 41735 2691355
SC 37290 2731014 homyur 39893 2850481
csnodgrass 36207 2688994 gri 39871 2515779
jmdana 36108 2205261 oryx 38282 2944400
strelock 34716 2074055 Spprtr 38157 2470529
Garf 33800 2747562 SC 37290 2731014
EthanOConnor 33370 2090311 csnodgrass 36207 2688994
slakovv 32915 2021889 jmdana 36157 2210661
Spprtr 32591 2139601 strelock 34716 2074055
Prcuvu 30377 2170122 Garf 33800 2747562
anst 30301 2190091 skiminki 33515 2055584
jkiiski 30136 1904470 EthanOConnor 33370 2090311
hyperbolic.tom 29840 2017394 slakovv 32915 2021889
Pyafue 29650 1902349 yurikvelo 32600 2255966
OuaisBla 27629 1578000 Prcuvu 30377 2170122
chriswk 26902 1868317 manapbk 30326 1770143
achambord 26582 1767323 anst 30301 2190091
Patrick_G 26276 1801617 jkiiski 30136 1904470
yorkman 26193 1992080 hyperbolic.tom 29840 2017394
SFTUser 25182 1675689 Pyafue 29650 1902349
nabildanial 24942 1519409 qurashee 27758 1509620
Sharaf_DG 24765 1786697 OuaisBla 27636 1578800
ncfish1 24411 1520927 chriswk 26902 1868317
agg177 23890 1395014 achambord 26582 1767323
JanErik 23408 1703875 Fifis 26376 1776853
Isidor 23388 1680691 Patrick_G 26276 1801617
Norabor 22976 1587862 yorkman 26193 1992080
cisco2015 22880 1759669 SFTUser 25182 1675689
Zirie 22542 1472937 nabildanial 24942 1519409
team-oh 22272 1636708 Sharaf_DG 24765 1786697
MazeOfGalious 21978 1629593 ncfish1 24411 1520927
sg4032 21945 1643065 agg177 23890 1395014
ianh2105 21725 1632562 JanErik 23408 1703875
xor12 21628 1680365 Isidor 23388 1680691
dex 21612 1467203 Norabor 23164 1591830
nesoneg 21494 1463031 cisco2015 22895 1762069
horst.prack 20878 1465656 Zirie 22542 1472937
0xB00B1ES 20590 1208666 team-oh 22272 1636708
j3corre 20405 941444 MazeOfGalious 21978 1629593
Adrian.Schmidt123 20316 1281436 sg4032 21945 1643065
wei 19973 1745989 ianh2105 21725 1632562
rstoesser 19569 1293588 xor12 21628 1680365
eudhan 19274 1283717 dex 21612 1467203
Ente 19070 1373058 nesoneg 21494 1463031
jundery 18445 1115855 jjoshua2 20997 1422689
iisiraider 18247 1101015 horst.prack 20878 1465656
ville 17883 1384026 0xB00B1ES 20590 1208666
chris 17698 1487385 sphinx 20515 1352368
purplefishies 17595 1092533 j3corre 20405 941444
DragonLord 17014 1162790 Adrian.Schmidt123 20316 1281436
dju 16515 929427 Ente 20017 1432602
IgorLeMasson 16064 1147232 wei 19973 1745989
ako027ako 15671 1173203 rstoesser 19569 1293588
Nikolay.IT 15154 1068349 eudhan 19274 1283717
Andrew Grant 15114 895539 jundery 18445 1115855
yurikvelo 15027 1165616 iisiraider 18247 1101015
OssumOpossum 14857 1007129 ville 17883 1384026
enedene 14476 905279 chris 17698 1487385
bpfliegel 14298 884523 purplefishies 17595 1092533
jpulman 13982 870599 DMBK 17357 1279152
joster 13794 950160 DragonLord 17014 1162790
Nesa92 13786 1114691 dju 16515 929427
Dark_wizzie 13422 1007152 IgorLeMasson 16064 1147232
Hjax 13350 900887 ako027ako 15671 1173203
Fifis 13313 965473 Nikolay.IT 15154 1068349
mabichito 12903 749391 Andrew Grant 15114 895539
thijsk 12886 722107 OssumOpossum 14857 1007129
crocogoat 12876 1048802 enedene 14476 905279
AdrianSA 12860 804972 bpfliegel 14298 884523
Flopzee 12698 894821 jpulman 13982 870599
fatmurphy 12547 853210 joster 13794 950160
SapphireBrand 12416 969604 Nesa92 13786 1114691
modolief 12386 896470 crocogoat 13753 1114622
scuzzi 12362 833465 Hjax 13535 915487
pgontarz 12151 848794 Dark_wizzie 13422 1007152
stocky 11954 699440 mpx86 12941 693640
mschmidt 11941 803401 mabichito 12903 749391
infinity 11470 727027 thijsk 12886 722107
torbjo 11387 728873 AdrianSA 12860 804972
Thomas A. Anderson 11372 732094 Flopzee 12698 894821
snicolet 11106 869170 fatmurphy 12547 853210
amicic 10779 733593 scuzzi 12511 845761
rpngn 10712 688203 Karby 12429 735880
d64 10680 771144 SapphireBrand 12416 969604
basepi 10637 744851 modolief 12386 896470
jjoshua2 10559 670905 pgontarz 12151 848794
dzjp 10343 732529 stocky 11954 699440
ols 10259 570669 mschmidt 11941 803401
lbraesch 10252 647825 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
+11 -7
View File
@@ -1,7 +1,5 @@
# Stockfish, a UCI chess playing engine derived from Glaurung 2.1 # Stockfish, a UCI chess playing engine derived from Glaurung 2.1
# Copyright (C) 2004-2008 Tord Romstad (Glaurung author) # Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
# Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
# Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
# #
# Stockfish is free software: you can redistribute it and/or modify # Stockfish is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@@ -368,9 +366,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)
@@ -481,6 +481,10 @@ 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
@@ -590,7 +594,7 @@ endif
ifeq ($(optimize),yes) ifeq ($(optimize),yes)
ifeq ($(debug), no) ifeq ($(debug), no)
ifeq ($(comp),clang) ifeq ($(comp),clang)
CXXFLAGS += -flto=thin CXXFLAGS += -flto
ifneq ($(findstring MINGW,$(KERNEL)),) ifneq ($(findstring MINGW,$(KERNEL)),)
CXXFLAGS += -fuse-ld=lld CXXFLAGS += -fuse-ld=lld
else ifneq ($(findstring MSYS,$(KERNEL)),) else ifneq ($(findstring MSYS,$(KERNEL)),)
@@ -610,7 +614,7 @@ ifeq ($(debug), no)
LDFLAGS += -save-temps LDFLAGS += -save-temps
endif endif
else else
CXXFLAGS += -flto=thin CXXFLAGS += -flto
LDFLAGS += $(CXXFLAGS) LDFLAGS += $(CXXFLAGS)
endif endif
@@ -720,7 +724,7 @@ endif
config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \ config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \
clang-profile-use clang-profile-make clang-profile-use clang-profile-make
build: config-sanity net build: net config-sanity
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) all $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all
profile-build: net config-sanity objclean profileclean profile-build: net config-sanity objclean profileclean
@@ -793,7 +797,7 @@ default:
all: $(EXE) .depend all: $(EXE) .depend
config-sanity: config-sanity: net
@echo "" @echo ""
@echo "Config:" @echo "Config:"
@echo "debug: '$(debug)'" @echo "debug: '$(debug)'"
+5 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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
@@ -92,6 +92,8 @@ const vector<string> Defaults = {
} // namespace } // namespace
namespace Stockfish {
/// 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
@@ -168,3 +170,5 @@ vector<string> setup_bench(const Position& current, istream& is) {
return list; return list;
} }
} // namespace Stockfish
+5 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,6 +23,8 @@
#include "bitboard.h" #include "bitboard.h"
#include "types.h" #include "types.h"
namespace Stockfish {
namespace { namespace {
// There are 24 possible pawn squares: files A to D and ranks from 2 to 7. // There are 24 possible pawn squares: files A to D and ranks from 2 to 7.
@@ -66,7 +68,6 @@ namespace {
} // namespace } // namespace
bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) { bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) {
assert(file_of(wpsq) <= FILE_D); assert(file_of(wpsq) <= FILE_D);
@@ -96,7 +97,6 @@ void Bitbases::init() {
KPKBitbase.set(idx); KPKBitbase.set(idx);
} }
namespace { namespace {
KPKPosition::KPKPosition(unsigned idx) { KPKPosition::KPKPosition(unsigned idx) {
@@ -168,3 +168,5 @@ namespace {
} }
} // namespace } // namespace
} // namespace Stockfish
+6 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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
@@ -22,6 +22,8 @@
#include "bitboard.h" #include "bitboard.h"
#include "misc.h" #include "misc.h"
namespace Stockfish {
uint8_t PopCnt16[1 << 16]; uint8_t PopCnt16[1 << 16];
uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
@@ -42,7 +44,6 @@ namespace {
} }
/// safe_destination() returns the bitboard of target square for the given step /// 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. /// from the given square. If the step is off the board, returns empty bitboard.
@@ -55,7 +56,7 @@ inline Bitboard safe_destination(Square s, int step) {
/// Bitboards::pretty() returns an ASCII representation of a bitboard suitable /// Bitboards::pretty() returns an ASCII representation of a bitboard suitable
/// to be printed to standard output. Useful for debugging. /// to be printed to standard output. Useful for debugging.
const std::string Bitboards::pretty(Bitboard b) { std::string Bitboards::pretty(Bitboard b) {
std::string s = "+---+---+---+---+---+---+---+---+\n"; std::string s = "+---+---+---+---+---+---+---+---+\n";
@@ -111,7 +112,6 @@ void Bitboards::init() {
} }
} }
namespace { namespace {
Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) { Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
@@ -211,3 +211,5 @@ namespace {
} }
} }
} }
} // namespace Stockfish
+8 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,19 +23,21 @@
#include "types.h" #include "types.h"
namespace Stockfish {
namespace Bitbases { namespace Bitbases {
void init(); void init();
bool probe(Square wksq, Square wpsq, Square bksq, Color us); bool probe(Square wksq, Square wpsq, Square bksq, Color us);
} } // namespace Stockfish::Bitbases
namespace Bitboards { namespace Bitboards {
void init(); void init();
const std::string pretty(Bitboard b); std::string pretty(Bitboard b);
} } // namespace Stockfish::Bitboards
constexpr Bitboard AllSquares = ~Bitboard(0); constexpr Bitboard AllSquares = ~Bitboard(0);
constexpr Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL; constexpr Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
@@ -430,4 +432,6 @@ inline Square frontmost_sq(Color c, Bitboard b) {
return c == WHITE ? msb(b) : lsb(b); return c == WHITE ? msb(b) : lsb(b);
} }
} // namespace Stockfish
#endif // #ifndef BITBOARD_H_INCLUDED #endif // #ifndef BITBOARD_H_INCLUDED
+4
View File
@@ -33,6 +33,7 @@
#include "tt.h" #include "tt.h"
#include "timeman.h" #include "timeman.h"
namespace Stockfish {
namespace Cluster { namespace Cluster {
// Total number of ranks and rank within the communicator // Total number of ranks and rank within the communicator
@@ -450,6 +451,7 @@ uint64_t TT_saves() {
} }
}
} }
#else #else
@@ -457,6 +459,7 @@ uint64_t TT_saves() {
#include "cluster.h" #include "cluster.h"
#include "thread.h" #include "thread.h"
namespace Stockfish {
namespace Cluster { namespace Cluster {
uint64_t nodes_searched() { uint64_t nodes_searched() {
@@ -474,6 +477,7 @@ uint64_t TT_saves() {
return Threads.TT_saves(); return Threads.TT_saves();
} }
}
} }
#endif // USE_MPI #endif // USE_MPI
+2
View File
@@ -26,6 +26,7 @@
#include "tt.h" #include "tt.h"
namespace Stockfish {
class Thread; class Thread;
/// The Cluster namespace contains functionality required to run on distributed /// The Cluster namespace contains functionality required to run on distributed
@@ -119,6 +120,7 @@ inline void signals_sync() { }
#endif /* USE_MPI */ #endif /* USE_MPI */
}
} }
#endif // #ifndef CLUSTER_H_INCLUDED #endif // #ifndef CLUSTER_H_INCLUDED
+9 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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
@@ -22,6 +22,8 @@
#include "endgame.h" #include "endgame.h"
#include "movegen.h" #include "movegen.h"
namespace Stockfish {
namespace { namespace {
// Used to drive the king towards the edge of the board // Used to drive the king towards the edge of the board
@@ -553,8 +555,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 = pos.squares<PAWN>(strongSide)[0]; Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN));
Square strongPawn2 = pos.squares<PAWN>(strongSide)[1]; Square strongPawn2 = msb(pos.pieces(strongSide, PAWN));
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 +640,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 = pos.squares<PAWN>(strongSide)[0]; Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN));
Square strongPawn2 = pos.squares<PAWN>(strongSide)[1]; Square strongPawn2 = msb(pos.pieces(strongSide, PAWN));
Square blockSq1, blockSq2; Square blockSq1, blockSq2;
if (relative_rank(strongSide, strongPawn1) > relative_rank(strongSide, strongPawn2)) if (relative_rank(strongSide, strongPawn1) > relative_rank(strongSide, strongPawn2))
@@ -741,3 +743,5 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
// it's probably at least a draw even with the pawn. // it's probably at least a draw even with the pawn.
return Bitbases::probe(strongKing, strongPawn, weakKing, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; return Bitbases::probe(strongKing, strongPawn, weakKing, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
} }
} // namespace Stockfish
+4 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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
@@ -28,6 +28,7 @@
#include "position.h" #include "position.h"
#include "types.h" #include "types.h"
namespace Stockfish {
/// EndgameCode lists all supported endgame functions by corresponding codes /// EndgameCode lists all supported endgame functions by corresponding codes
@@ -120,4 +121,6 @@ namespace Endgames {
} }
} }
} // namespace Stockfish
#endif // #ifndef ENDGAME_H_INCLUDED #endif // #ifndef ENDGAME_H_INCLUDED
+151 -98
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,12 +38,13 @@
#include "incbin/incbin.h" #include "incbin/incbin.h"
// Macro to embed the default NNUE file data in the engine binary (using incbin.h, by Dale Weiler). // 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 // This macro invocation will declare the following three variables
// const unsigned char gEmbeddedNNUEData[]; // a pointer to the embedded data // const unsigned char gEmbeddedNNUEData[]; // a pointer to the embedded data
// const unsigned char *const gEmbeddedNNUEEnd; // a marker to the end // const unsigned char *const gEmbeddedNNUEEnd; // a marker to the end
// const unsigned int gEmbeddedNNUESize; // the size of the embedded file // const unsigned int gEmbeddedNNUESize; // the size of the embedded file
// Note that this does not work in Microsof Visual Studio. // Note that this does not work in Microsoft Visual Studio.
#if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF) #if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF)
INCBIN(EmbeddedNNUE, EvalFileDefaultName); INCBIN(EmbeddedNNUE, EvalFileDefaultName);
#else #else
@@ -54,22 +55,24 @@
using namespace std; using namespace std;
using namespace Eval::NNUE; using namespace Stockfish::Eval::NNUE;
namespace Stockfish {
namespace Eval { namespace Eval {
bool useNNUE; bool useNNUE;
string eval_file_loaded = "None"; string eval_file_loaded = "None";
/// init_NNUE() tries to load a nnue network at startup time, or when the engine /// NNUE::init() tries to load a NNUE network at startup time, or when the engine
/// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.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. /// The name of the NNUE network is always retrieved from the EvalFile option.
/// We search the given network in three locations: internally (the default /// We search the given network in three locations: internally (the default
/// network may be embedded in the binary), in the active working directory and /// 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 /// 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. /// variable to have the engine search in a special directory in their distro.
void init_NNUE() { void NNUE::init() {
useNNUE = Options["Use NNUE"]; useNNUE = Options["Use NNUE"];
if (!useNNUE) if (!useNNUE)
@@ -112,8 +115,8 @@ namespace Eval {
} }
} }
/// verify_NNUE() verifies that the last net used was loaded successfully /// NNUE::verify() verifies that the last net used was loaded successfully
void verify_NNUE() { void NNUE::verify() {
string eval_file = string(Options["EvalFile"]); string eval_file = string(Options["EvalFile"]);
@@ -191,11 +194,11 @@ using namespace Trace;
namespace { namespace {
// Threshold for lazy and space evaluation // Threshold for lazy and space evaluation
constexpr Value LazyThreshold1 = Value(1400); constexpr Value LazyThreshold1 = Value(1565);
constexpr Value LazyThreshold2 = Value(1300); constexpr Value LazyThreshold2 = Value(1102);
constexpr Value SpaceThreshold = Value(12222); constexpr Value SpaceThreshold = Value(11551);
constexpr Value NNUEThreshold1 = Value(550); constexpr Value NNUEThreshold1 = Value(682);
constexpr Value NNUEThreshold2 = Value(150); 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 };
@@ -203,7 +206,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] = {
{}, {}, {792, 1283}, {645, 967}, {1084, 1897}, {772, 1119} {}, {}, {803, 1292}, {639, 974}, {1087, 1878}, {759, 1132}
}; };
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
@@ -211,19 +214,25 @@ 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,-81), S(-53,-56), S(-12,-31), S( -4,-16), S( 3, 5), S( 13, 11), // Knight { S(-62,-79), S(-53,-57), S(-12,-31), S( -3,-17), S( 3, 7), S( 12, 13), // Knight
S( 22, 17), S( 28, 20), S( 33, 25) }, S( 21, 16), S( 28, 21), S( 37, 26) },
{ S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishop { S(-47,-59), S(-20,-25), S( 14, -8), S( 29, 12), S( 39, 21), S( 53, 40), // Bishop
S( 55, 54), S( 63, 57), S( 63, 65), S( 68, 73), S( 81, 78), S( 81, 86), S( 53, 56), S( 60, 58), S( 62, 65), S( 69, 72), S( 78, 78), S( 83, 87),
S( 91, 88), S( 98, 97) }, S( 91, 88), S( 96, 98) },
{ S(-60,-78), S(-20,-17), S( 2, 23), S( 3, 39), S( 3, 70), S( 11, 99), // Rook { S(-60,-82), S(-24,-15), S( 0, 17) ,S( 3, 43), S( 4, 72), S( 14,100), // Rook
S( 22,103), S( 31,121), S( 40,134), S( 40,139), S( 41,158), S( 48,164), S( 20,102), S( 30,122), S( 41,133), S(41 ,139), S( 41,153), S( 45,160),
S( 57,168), S( 57,169), S( 62,172) }, S( 57,165), S( 58,170), S( 67,175) },
{ S(-30,-48), S(-12,-30), S( -8, -7), S( -9, 19), S( 20, 40), S( 23, 55), // Queen { S(-29,-49), S(-16,-29), S( -8, -8), S( -8, 17), S( 18, 39), S( 25, 54), // Queen
S( 23, 59), S( 35, 75), S( 38, 78), S( 53, 96), S( 64, 96), S( 65,100), S( 23, 59), S( 37, 73), S( 41, 76), S( 54, 95), S( 65, 95) ,S( 68,101),
S( 65,121), S( 66,127), S( 67,131), S( 67,133), S( 72,136), S( 72,141), S( 69,124), S( 70,128), S( 70,132), S( 70,133) ,S( 71,136), S( 72,140),
S( 77,147), S( 79,150), S( 93,151), S(108,168), S(108,168), S(108,171), S( 74,147), S( 76,149), S( 90,153), S(104,169), S(105,171), S(106,171),
S(110,182), S(114,182), S(114,192), S(116,219) } S(112,178), S(114,185), S(114,187), S(119,221) }
};
// 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
@@ -231,16 +240,15 @@ 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(56, 34), S(31, 23) }; constexpr Score Outpost[] = { S(57, 38), S(31, 24) };
// 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(9, 28), S(15, 31), S(17, 39), S(64, 70), S(171, 177), S(277, 260) S(0, 0), S(7, 27), S(16, 32), S(17, 40), S(64, 71), S(170, 174), S(278, 262)
}; };
// RookOnFile[semiopen/open] contains bonuses for each rook when there is constexpr Score RookOnClosedFile = S(10, 5);
// no (friendly) pawn on the rook file. constexpr Score RookOnOpenFile[] = { S(19, 6), S(47, 26) };
constexpr Score RookOnFile[] = { S(19, 7), S(48, 27) };
// 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
@@ -254,9 +262,8 @@ namespace {
}; };
// Assorted bonuses and penalties // Assorted bonuses and penalties
constexpr Score BadOutpost = S( -7, 36); constexpr Score UncontestedOutpost = S( 1, 10);
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);
@@ -269,7 +276,6 @@ namespace {
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);
@@ -388,15 +394,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);
const Square* pl = pos.squares<Pt>(Us); Bitboard b1 = pos.pieces(Us, Pt);
Bitboard b, bb; Bitboard b, bb;
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
attackedBy[Us][Pt] = 0; attackedBy[Us][Pt] = 0;
for (Square s = *pl; s != SQ_NONE; s = *++pl) while (b1) {
{ 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,13 +429,12 @@ 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
// Reduced bonus for knights (BadOutpost) if few relevant targets // Bonus for knights (UncontestedOutpost) if few relevant targets
bb = OutpostRanks & (attackedBy[Us][PAWN] | shift<Down>(pos.pieces(PAWN))) bb = OutpostRanks & (attackedBy[Us][PAWN] | shift<Down>(pos.pieces(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);
@@ -438,7 +443,7 @@ namespace {
&& 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 += BadOutpost; score += UncontestedOutpost * popcount(pos.pieces(PAWN) & (s & QueenSide ? QueenSide : KingSide));
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))
@@ -451,14 +456,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 (Pt == BISHOP) if constexpr (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 * pos.pawns_on_same_color_squares(Us, s) score -= BishopPawns[edge_distance(file_of(s))] * 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
@@ -483,26 +488,34 @@ namespace {
} }
} }
if (Pt == ROOK) if constexpr (Pt == ROOK)
{ {
// Bonus for rook on the same file as a queen // Bonuses for rook on a (semi-)open or closed file
if (file_bb(s) & pos.pieces(QUEEN))
score += RookOnQueenFile;
// Bonus for rook on an open or semi-open file
if (pos.is_on_semiopen_file(Us, s)) if (pos.is_on_semiopen_file(Us, s))
score += RookOnFile[pos.is_on_semiopen_file(Them, s)];
// Penalty when trapped by the king, even more if the king cannot castle
else if (mob <= 3)
{ {
File kf = file_of(pos.square<KING>(Us)); score += RookOnOpenFile[pos.is_on_semiopen_file(Them, s)];
if ((kf < FILE_E) == (file_of(s) < kf)) }
score -= TrappedRook * (1 + !pos.castling_rights(Us)); 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
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 (Pt == QUEEN) if constexpr (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;
@@ -510,7 +523,7 @@ namespace {
score -= WeakQueen; score -= WeakQueen;
} }
} }
if (T) if constexpr (T)
Trace::add(Pt, Us, score); Trace::add(Pt, Us, score);
return score; return score;
@@ -586,18 +599,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] kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] // (~10 Elo)
+ 185 * popcount(kingRing[Us] & weak) + 183 * popcount(kingRing[Us] & weak) // (~15 Elo)
+ 148 * popcount(unsafeChecks) + 148 * popcount(unsafeChecks) // (~4 Elo)
+ 98 * popcount(pos.blockers_for_king(Us)) + 98 * popcount(pos.blockers_for_king(Us)) // (~2 Elo)
+ 69 * kingAttacksCount[Them] + 69 * kingAttacksCount[Them] // (~0.5 Elo)
+ 3 * kingFlankAttack * kingFlankAttack / 8 + 3 * kingFlankAttack * kingFlankAttack / 8 // (~0.5 Elo)
+ mg_value(mobility[Them] - mobility[Us]) + mg_value(mobility[Them] - mobility[Us]) // (~0.5 Elo)
- 873 * !pos.count<QUEEN>(Them) - 873 * !pos.count<QUEEN>(Them) // (~24 Elo)
- 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) // (~5 Elo)
- 6 * mg_value(score) / 8 - 6 * mg_value(score) / 8 // (~8 Elo)
- 4 * kingFlankDefense - 4 * kingFlankDefense // (~5 Elo)
+ 37; + 37; // (~0.5 Elo)
// 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)
@@ -610,7 +623,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 (T) if constexpr (T)
Trace::add(KING, Us, score); Trace::add(KING, Us, score);
return score; return score;
@@ -711,7 +724,7 @@ namespace {
score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance); score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance);
} }
if (T) if constexpr (T)
Trace::add(THREAT, Us, score); Trace::add(THREAT, Us, score);
return score; return score;
@@ -781,14 +794,16 @@ 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]; unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
// If there are no enemy attacks on passed pawn span, assign a big bonus. // If there are no enemy pieces or 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 ? 35 : int k = !unsafeSquares ? 36 :
!(unsafeSquares & squaresToQueen) ? 20 : !(unsafeSquares & ~attackedBy[Us][PAWN]) ? 30 :
!(unsafeSquares & blockSq) ? 9 : !(unsafeSquares & squaresToQueen) ? 17 :
!(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
@@ -802,7 +817,7 @@ namespace {
score += bonus - PassedFile * edge_distance(file_of(s)); score += bonus - PassedFile * edge_distance(file_of(s));
} }
if (T) if constexpr (T)
Trace::add(PASSED, Us, score); Trace::add(PASSED, Us, score);
return score; return score;
@@ -837,11 +852,13 @@ 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 (T) if constexpr (T)
Trace::add(SPACE, Us, score); Trace::add(SPACE, Us, score);
return score; return score;
@@ -856,7 +873,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))
- distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK)); + int(rank_of(pos.square<KING>(WHITE)) - rank_of(pos.square<KING>(BLACK)));
bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide) bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide)
&& (pos.pieces(PAWN) & KingSide); && (pos.pieces(PAWN) & KingSide);
@@ -898,23 +915,37 @@ 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)); sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide)) - 4 * !pawnsOnBothFlanks;
// 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
@@ -922,7 +953,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 (T) if constexpr (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));
@@ -994,7 +1025,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 (T) if constexpr (T)
{ {
Trace::add(MATERIAL, pos.psq_score()); Trace::add(MATERIAL, pos.psq_score());
Trace::add(IMBALANCE, me->imbalance()); Trace::add(IMBALANCE, me->imbalance());
@@ -1019,20 +1050,40 @@ make_v:
Value Eval::evaluate(const Position& pos) { Value Eval::evaluate(const Position& pos) {
// Use classical eval if there is a large imbalance Value v;
// If there is a moderate imbalance, use classical eval with probability (1/8),
// as derived from the node counter.
bool useClassical = abs(eg_value(pos.psq_score())) * 16 > NNUEThreshold1 * (16 + pos.rule50_count());
bool classical = !Eval::useNNUE
|| useClassical
|| (abs(eg_value(pos.psq_score())) > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB));
Value v = classical ? Evaluation<NO_TRACE>(pos).value()
: NNUE::evaluate(pos) * 5 / 4 + Tempo;
if ( useClassical if (!Eval::useNNUE)
&& Eval::useNNUE v = Evaluation<NO_TRACE>(pos).value();
&& abs(v) * 16 < NNUEThreshold2 * (16 + pos.rule50_count())) else
v = NNUE::evaluate(pos) * 5 / 4 + Tempo; {
// 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 // Damp down the evaluation linearly when shuffling
v = v * (100 - pos.rule50_count()) / 100; v = v * (100 - pos.rule50_count()) / 100;
@@ -1101,3 +1152,5 @@ std::string Eval::trace(const Position& pos) {
return ss.str(); return ss.str();
} }
} // namespace Stockfish
+9 -7
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,6 +23,8 @@
#include "types.h" #include "types.h"
namespace Stockfish {
class Position; class Position;
namespace Eval { namespace Eval {
@@ -32,23 +34,23 @@ namespace Eval {
extern bool useNNUE; extern bool useNNUE;
extern std::string eval_file_loaded; extern std::string eval_file_loaded;
void init_NNUE();
void verify_NNUE();
// The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue
// for the build process (profile-build and fishtest) to work. Do not change the // for the build process (profile-build and fishtest) to work. Do not change the
// name of the macro, as it is used in the Makefile. // name of the macro, as it is used in the Makefile.
#define EvalFileDefaultName "nn-03744f8d56d8.nnue" #define EvalFileDefaultName "nn-62ef826d1a6d.nnue"
namespace NNUE { namespace NNUE {
Value evaluate(const Position& pos); Value evaluate(const Position& pos);
Value compute_eval(const Position& pos); bool load_eval(std::string name, std::istream& stream);
void update_eval(const Position& pos); void init();
bool load_eval(std::string streamName, std::istream& stream); void verify();
} // namespace NNUE } // namespace NNUE
} // namespace Eval } // namespace Eval
} // namespace Stockfish
#endif // #ifndef EVALUATE_H_INCLUDED #endif // #ifndef EVALUATE_H_INCLUDED
+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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,15 +21,14 @@
#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 { using namespace Stockfish;
void init();
}
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
@@ -47,7 +46,7 @@ int main(int argc, char* argv[]) {
Endgames::init(); Endgames::init();
Threads.set(size_t(Options["Threads"])); Threads.set(size_t(Options["Threads"]));
Search::clear(); // After threads are up Search::clear(); // After threads are up
Eval::init_NNUE(); Eval::NNUE::init();
UCI::loop(argc, argv); UCI::loop(argc, argv);
+31 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,32 +24,39 @@
using namespace std; using namespace std;
namespace Stockfish {
namespace { namespace {
#define S(mg, eg) make_score(mg, eg)
// Polynomial material imbalance parameters // Polynomial material imbalance parameters
constexpr int QuadraticOurs[][PIECE_TYPE_NB] = { // One Score parameter for each pair (our piece, another of our pieces)
// OUR PIECES constexpr Score QuadraticOurs[][PIECE_TYPE_NB] = {
// pair pawn knight bishop rook queen // OUR PIECE 2
{1438 }, // Bishop pair // bishop pair pawn knight bishop rook queen
{ 40, 38 }, // Pawn {S(1419, 1455) }, // Bishop pair
{ 32, 255, -62 }, // Knight OUR PIECES {S( 101, 28), S( 37, 39) }, // Pawn
{ 0, 104, 4, 0 }, // Bishop {S( 57, 64), S(249, 187), S(-49, -62) }, // Knight OUR PIECE 1
{ -26, -2, 47, 105, -208 }, // Rook {S( 0, 0), S(118, 137), S( 10, 27), S( 0, 0) }, // Bishop
{-189, 24, 117, 133, -134, -6 } // Queen {S( -63, -68), S( -5, 3), S(100, 81), S(132, 118), S(-246, -244) }, // Rook
{S(-210, -211), S( 37, 14), S(147, 141), S(161, 105), S(-158, -174), S(-9,-31) } // Queen
}; };
constexpr int QuadraticTheirs[][PIECE_TYPE_NB] = { // One Score parameter for each pair (our piece, their piece)
// THEIR PIECES constexpr Score QuadraticTheirs[][PIECE_TYPE_NB] = {
// pair pawn knight bishop rook queen // THEIR PIECE
{ }, // Bishop pair // bishop pair pawn knight bishop rook queen
{ 36, }, // Pawn { }, // Bishop pair
{ 9, 63, }, // Knight OUR PIECES {S( 33, 30) }, // Pawn
{ 59, 65, 42, }, // Bishop {S( 46, 18), S(106, 84) }, // Knight OUR PIECE
{ 46, 39, 24, -24, }, // Rook {S( 75, 35), S( 59, 44), S( 60, 15) }, // Bishop
{ 97, 100, -42, 137, 268, } // Queen {S( 26, 35), S( 6, 22), S( 38, 39), S(-12, -2) }, // Rook
{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) };
@@ -82,11 +89,11 @@ namespace {
/// piece type for both colors. /// piece type for both colors.
template<Color Us> template<Color Us>
int imbalance(const int pieceCount[][PIECE_TYPE_NB]) { Score imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
constexpr Color Them = ~Us; constexpr Color Them = ~Us;
int bonus = 0; Score bonus = SCORE_ZERO;
// 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)
@@ -213,8 +220,10 @@ 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->value = int16_t((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16); e->score = (imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16;
return e; return e;
} }
} // namespace Material } // namespace Material
} // namespace Stockfish
+7 -7
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,7 @@
#include "position.h" #include "position.h"
#include "types.h" #include "types.h"
namespace Material { namespace Stockfish::Material {
/// Material::Entry contains various information about a material configuration. /// Material::Entry contains various information about a material configuration.
/// It contains a material imbalance evaluation, a function pointer to a special /// It contains a material imbalance evaluation, a function pointer to a special
@@ -37,8 +37,8 @@ namespace Material {
struct Entry { struct Entry {
Score imbalance() const { return make_score(value, value); } Score imbalance() const { return score; }
Phase game_phase() const { return gamePhase; } Phase game_phase() const { return (Phase)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,15 +57,15 @@ 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)
int16_t value; Score score;
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;
Entry* probe(const Position& pos); Entry* probe(const Position& pos);
} // namespace Material } // namespace Stockfish::Material
#endif // #ifndef MATERIAL_H_INCLUDED #endif // #ifndef MATERIAL_H_INCLUDED
+38 -51
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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
@@ -61,6 +61,8 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
using namespace std; using namespace std;
namespace Stockfish {
namespace { 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
@@ -138,7 +140,7 @@ public:
/// the program was compiled) or "Stockfish <Version>", depending on whether /// the program was compiled) or "Stockfish <Version>", depending on whether
/// Version is empty. /// Version is empty.
const string engine_info(bool to_uci) { string engine_info(bool to_uci) {
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
string month, day, year; string month, day, year;
@@ -161,7 +163,7 @@ const string engine_info(bool to_uci) {
/// compiler_info() returns a string trying to describe the compiler we use /// compiler_info() returns a string trying to describe the compiler we use
const std::string compiler_info() { std::string compiler_info() {
#define stringify2(x) #x #define stringify2(x) #x
#define stringify(x) stringify2(x) #define stringify(x) stringify2(x)
@@ -357,27 +359,11 @@ void std_aligned_free(void* ptr) {
#endif #endif
} }
/// aligned_ttmem_alloc() will return suitably aligned memory, if possible using large pages. /// aligned_large_pages_alloc() will return suitably aligned memory, if possible using 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(__linux__) && !defined(__ANDROID__) #if defined(_WIN32)
#if defined(_WIN64)
void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { static void* aligned_large_pages_alloc_win(size_t allocSize) {
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;
#if defined(MADV_HUGEPAGE)
madvise(mem, allocSize, MADV_HUGEPAGE);
#endif
return mem;
}
#elif defined(_WIN64)
static void* aligned_ttmem_alloc_large_pages(size_t allocSize) {
HANDLE hProcessToken { }; HANDLE hProcessToken { };
LUID luid { }; LUID luid { };
@@ -421,51 +407,51 @@ static void* aligned_ttmem_alloc_large_pages(size_t allocSize) {
return mem; return mem;
} }
#endif
void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { void* aligned_large_pages_alloc(size_t allocSize) {
static bool firstCall = true;
#if defined(_WIN64)
// Try to allocate large pages // Try to allocate large pages
mem = aligned_ttmem_alloc_large_pages(allocSize); void* mem = aligned_large_pages_alloc_win(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)
mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
#else
void* mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
#endif
return mem; return mem;
} }
#else #else
void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { void* aligned_large_pages_alloc(size_t allocSize) {
constexpr size_t alignment = 64; // assumed cache line size #if defined(__linux__)
size_t size = allocSize + alignment - 1; // allocate some extra space constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page size
mem = malloc(size); #else
void* ret = reinterpret_cast<void*>((uintptr_t(mem) + alignment - 1) & ~uintptr_t(alignment - 1)); constexpr size_t alignment = 4096; // assumed small page size
return ret; #endif
// 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_ttmem_free() will free the previously allocated ttmem /// aligned_large_pages_free() will free the previously allocated ttmem
#if defined(_WIN64) #if defined(_WIN32)
void aligned_ttmem_free(void* mem) { void aligned_large_pages_free(void* mem) {
if (mem && !VirtualFree(mem, 0, MEM_RELEASE)) if (mem && !VirtualFree(mem, 0, MEM_RELEASE))
{ {
@@ -478,8 +464,8 @@ void aligned_ttmem_free(void* mem) {
#else #else
void aligned_ttmem_free(void *mem) { void aligned_large_pages_free(void *mem) {
free(mem); std_aligned_free(mem);
} }
#endif #endif
@@ -604,11 +590,10 @@ namespace CommandLine {
string argv0; // path+name of the executable binary, as given by argv[0] string argv0; // path+name of the executable binary, as given by argv[0]
string binaryDirectory; // path of the executable directory string binaryDirectory; // path of the executable directory
string workingDirectory; // path of the working directory string workingDirectory; // path of the working directory
string pathSeparator; // Separator for our current OS
void init(int argc, char* argv[]) { void init(int argc, char* argv[]) {
(void)argc; (void)argc;
string separator; string pathSeparator;
// extract the path+name of the executable binary // extract the path+name of the executable binary
argv0 = argv[0]; argv0 = argv[0];
@@ -648,3 +633,5 @@ void init(int argc, char* argv[]) {
} // namespace CommandLine } // namespace CommandLine
} // namespace Stockfish
+21 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,17 +24,20 @@
#include <ostream> #include <ostream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <cstdint>
#include "types.h" #include "types.h"
const std::string engine_info(bool to_uci = false); namespace Stockfish {
const std::string compiler_info();
std::string engine_info(bool to_uci = false);
std::string compiler_info();
void prefetch(void* addr); void prefetch(void* addr);
void start_logger(const std::string& fname); void start_logger(const std::string& fname);
void* std_aligned_alloc(size_t alignment, size_t size); void* std_aligned_alloc(size_t alignment, size_t size);
void std_aligned_free(void* ptr); void std_aligned_free(void* ptr);
void* aligned_ttmem_alloc(size_t size, void*& mem); void* aligned_large_pages_alloc(size_t size); // memory aligned by page size, min alignment: 4096 bytes
void aligned_ttmem_free(void* mem); // nop if mem == nullptr void aligned_large_pages_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);
@@ -63,6 +66,17 @@ 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
// `sizeof(T) * N + alignment` bytes, where `N` is the
// number of elements in the array.
template <uintptr_t Alignment, typename T>
T* align_ptr_up(T* ptr)
{
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
/// This class is based on original code written and dedicated /// This class is based on original code written and dedicated
@@ -131,4 +145,6 @@ namespace CommandLine {
extern std::string workingDirectory; // path of the working directory extern std::string workingDirectory; // path of the working directory
} }
} // namespace Stockfish
#endif // #ifndef MISC_H_INCLUDED #endif // #ifndef MISC_H_INCLUDED
+38 -40
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,6 +21,8 @@
#include "movegen.h" #include "movegen.h"
#include "position.h" #include "position.h"
namespace Stockfish {
namespace { namespace {
template<GenType Type, Direction D> template<GenType Type, Direction D>
@@ -85,7 +87,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 discovery check // don't generate captures. Note that a possible discovered 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 +136,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,10 +158,8 @@ 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 can be an evasion only if the checking piece // An en passant capture cannot resolve a discovered check.
// is the double pushed pawn and so is in the target. Otherwise this if (Type == EVASIONS && (target & (pos.ep_square() + Up)))
// 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());
@@ -167,7 +167,7 @@ namespace {
assert(b1); assert(b1);
while (b1) while (b1)
*moveList++ = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square()); *moveList++ = make<EN_PASSANT>(pop_lsb(&b1), pos.ep_square());
} }
} }
@@ -175,29 +175,24 @@ namespace {
} }
template<Color Us, PieceType Pt, bool Checks> template<PieceType Pt, bool Checks>
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) { ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard piecesToMove, 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()");
const Square* pl = pos.squares<Pt>(Us); Bitboard bb = piecesToMove & pos.pieces(Pt);
for (Square from = *pl; from != SQ_NONE; from = *++pl) if (!bb)
{ return moveList;
if (Checks)
{
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
&& !(attacks_bb<Pt>(from) & target & pos.check_squares(Pt)))
continue;
if (pos.blockers_for_king(~Us) & from) [[maybe_unused]] const Bitboard checkSquares = pos.check_squares(Pt);
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)
if (Checks) b &= checkSquares;
b &= pos.check_squares(Pt);
while (b) while (b)
*moveList++ = make_move(from, pop_lsb(&b)); *moveList++ = make_move(from, pop_lsb(&b));
@@ -209,8 +204,14 @@ 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
Bitboard target; static_assert(Type != LEGAL, "Unsupported type in generate_all()");
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)
{ {
@@ -222,23 +223,18 @@ namespace {
target = ~pos.pieces(); target = ~pos.pieces();
break; break;
case EVASIONS: case EVASIONS:
{ target = between_bb(pos.square<KING>(Us), lsb(pos.checkers())) | pos.checkers();
Square checksq = lsb(pos.checkers());
target = between_bb(pos.square<KING>(Us), checksq) | checksq;
break; break;
}
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<Us, KNIGHT, Checks>(pos, moveList, target); moveList = generate_moves<KNIGHT, Checks>(pos, moveList, piecesToMove, target);
moveList = generate_moves<Us, BISHOP, Checks>(pos, moveList, target); moveList = generate_moves<BISHOP, Checks>(pos, moveList, piecesToMove, target);
moveList = generate_moves<Us, ROOK, Checks>(pos, moveList, target); moveList = generate_moves< ROOK, Checks>(pos, moveList, piecesToMove, target);
moveList = generate_moves<Us, QUEEN, Checks>(pos, moveList, target); moveList = generate_moves< QUEEN, Checks>(pos, moveList, piecesToMove, target);
if (Type != QUIET_CHECKS && Type != EVASIONS) if (Type != QUIET_CHECKS && Type != EVASIONS)
{ {
@@ -260,7 +256,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.
@@ -283,8 +279,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. /// generate<QUIET_CHECKS> generates all pseudo-legal non-captures giving check,
/// Returns a pointer to the end of the move list. /// except castling. 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) {
@@ -357,7 +353,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 || from_sq(*cur) == ksq || type_of(*cur) == ENPASSANT) if ( ((pinned && pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT)
&& !pos.legal(*cur)) && !pos.legal(*cur))
*cur = (--moveList)->move; *cur = (--moveList)->move;
else else
@@ -365,3 +361,5 @@ ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
return moveList; return moveList;
} }
} // namespace Stockfish
+5 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,6 +23,8 @@
#include "types.h" #include "types.h"
namespace Stockfish {
class Position; class Position;
enum GenType { enum GenType {
@@ -70,4 +72,6 @@ private:
ExtMove moveList[MAX_MOVES], *last; ExtMove moveList[MAX_MOVES], *last;
}; };
} // namespace Stockfish
#endif // #ifndef MOVEGEN_H_INCLUDED #endif // #ifndef MOVEGEN_H_INCLUDED
+15 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,6 +20,8 @@
#include "movepick.h" #include "movepick.h"
namespace Stockfish {
namespace { namespace {
enum Stages { enum Stages {
@@ -73,8 +75,9 @@ 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 && (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) !( ttm
&& pos.pseudo_legal(ttm)); && (pos.checkers() || depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare)
&& pos.pseudo_legal(ttm));
} }
/// MovePicker constructor for ProbCut: we generate captures with SEE greater /// MovePicker constructor for ProbCut: we generate captures with SEE greater
@@ -98,15 +101,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 (Type == CAPTURES) if constexpr (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 (Type == QUIETS) else if constexpr (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)]
+ 2 * (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
+ 2 * (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]
+ (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)]
+ (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);
@@ -116,8 +119,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)]
+ (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
- (1 << 28); - (1 << 28);
} }
} }
@@ -141,7 +144,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) {
@@ -262,3 +265,5 @@ top:
assert(false); assert(false);
return MOVE_NONE; // Silence warning return MOVE_NONE; // Silence warning
} }
} // namespace Stockfish
+11 -7
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,6 +27,8 @@
#include "position.h" #include "position.h"
#include "types.h" #include "types.h"
namespace Stockfish {
/// StatsEntry stores the stat table value. It is usually a number but could /// StatsEntry stores the stat table value. It is usually a number but could
/// be a move or even a nested history. We use a class instead of naked value /// be a move or even a nested history. We use a class instead of naked value
/// to directly call history update operator<<() on the entry so to use stats /// to directly call history update operator<<() on the entry so to use stats
@@ -84,7 +86,7 @@ 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, 10692, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory; typedef Stats<int16_t, 13365, 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 moves which are/were in the PV (ttPv). It is cleared with each new /// and quiet moves which are/were in the PV (ttPv). It is cleared with each new
@@ -108,12 +110,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 alpha /// when MOVE_NONE is returned. In order to improve the efficiency of the
/// beta algorithm, MovePicker attempts to return the moves which are most likely /// alpha-beta algorithm, MovePicker attempts to return the moves which are most
/// to get a cut-off first. /// likely to get a cut-off first.
class MovePicker { class MovePicker {
enum PickType { Next, Best }; enum PickType { Next, Best };
@@ -156,4 +158,6 @@ private:
ExtMove moves[MAX_MOVES]; ExtMove moves[MAX_MOVES];
}; };
} // namespace Stockfish
#endif // #ifndef MOVEPICK_H_INCLUDED #endif // #ifndef MOVEPICK_H_INCLUDED
+3 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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
@@ -28,7 +28,7 @@
#include "../layers/affine_transform.h" #include "../layers/affine_transform.h"
#include "../layers/clipped_relu.h" #include "../layers/clipped_relu.h"
namespace Eval::NNUE { namespace Stockfish::Eval::NNUE {
// Input features used in evaluation function // Input features used in evaluation function
using RawFeatures = Features::FeatureSet< using RawFeatures = Features::FeatureSet<
@@ -49,6 +49,6 @@ using OutputLayer = AffineTransform<HiddenLayer2, 1>;
using Network = Layers::OutputLayer; using Network = Layers::OutputLayer;
} // namespace Eval::NNUE } // namespace Stockfish::Eval::NNUE
#endif // #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED #endif // #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED
+40 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,34 +25,14 @@
#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"
namespace Eval::NNUE { namespace Stockfish::Eval::NNUE {
uint32_t kpp_board_index[PIECE_NB][COLOR_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 }
};
// Input feature converter // Input feature converter
AlignedPtr<FeatureTransformer> feature_transformer; LargePagePtr<FeatureTransformer> feature_transformer;
// Evaluation function // Evaluation function
AlignedPtr<Network> network; AlignedPtr<Network> network;
@@ -70,14 +50,22 @@ 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, const AlignedPtr<T>& pointer) { bool ReadParameters(std::istream& stream, T& reference) {
std::uint32_t header; std::uint32_t header;
header = read_little_endian<std::uint32_t>(stream); header = read_little_endian<std::uint32_t>(stream);
if (!stream || header != T::GetHashValue()) return false; if (!stream || header != T::GetHashValue()) return false;
return pointer->ReadParameters(stream); return reference.ReadParameters(stream);
} }
} // namespace Detail } // namespace Detail
@@ -110,29 +98,47 @@ 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();
} }
// Evaluation function. Perform differential calculation. // Evaluation function. Perform differential calculation.
Value evaluate(const Position& pos) { Value evaluate(const Position& pos) {
alignas(kCacheLineSize) TransformedFeatureType // We manually align the arrays on the stack because with gcc < 9.3
transformed_features[FeatureTransformer::kBufferSize]; // 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); feature_transformer->Transform(pos, transformed_features);
alignas(kCacheLineSize) char buffer[Network::kBufferSize];
const auto output = network->Propagate(transformed_features, buffer); const auto output = network->Propagate(transformed_features, buffer);
return static_cast<Value>(output[0] / FV_SCALE); return static_cast<Value>(output[0] / FV_SCALE);
} }
// Load eval, from a file stream or a memory stream // Load eval, from a file stream or a memory stream
bool load_eval(std::string streamName, std::istream& stream) { bool load_eval(std::string name, std::istream& stream) {
Initialize(); Initialize();
fileName = streamName; fileName = name;
return ReadParameters(stream); return ReadParameters(stream);
} }
} // namespace Eval::NNUE } // namespace Stockfish::Eval::NNUE
+14 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,7 +25,7 @@
#include <memory> #include <memory>
namespace Eval::NNUE { namespace Stockfish::Eval::NNUE {
// Hash value of evaluation function structure // Hash value of evaluation function structure
constexpr std::uint32_t kHashValue = constexpr std::uint32_t kHashValue =
@@ -40,9 +40,20 @@ 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>>;
} // namespace Eval::NNUE template <typename T>
using LargePagePtr = std::unique_ptr<T, LargePageDeleter<T>>;
} // namespace Stockfish::Eval::NNUE
#endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED #endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED
+3 -68
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,7 @@
#include "features_common.h" #include "features_common.h"
#include <array> #include <array>
namespace Eval::NNUE::Features { namespace Stockfish::Eval::NNUE::Features {
// Class template that represents a list of values // Class template that represents a list of values
template <typename T, T... Values> template <typename T, T... Values>
@@ -43,47 +43,6 @@ 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.piece[0] == make_piece(perspective, KING);
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
@@ -103,32 +62,8 @@ 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 Stockfish::Eval::NNUE::Features
#endif // #ifndef NNUE_FEATURE_SET_H_INCLUDED #endif // #ifndef NNUE_FEATURE_SET_H_INCLUDED
+3 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,7 @@
#include "../../evaluate.h" #include "../../evaluate.h"
#include "../nnue_common.h" #include "../nnue_common.h"
namespace Eval::NNUE::Features { namespace Stockfish::Eval::NNUE::Features {
class IndexList; class IndexList;
@@ -40,6 +40,6 @@ namespace Eval::NNUE::Features {
kFriend // side to move kFriend // side to move
}; };
} // namespace Eval::NNUE::Features } // namespace Stockfish::Eval::NNUE::Features
#endif // #ifndef NNUE_FEATURES_COMMON_H_INCLUDED #endif // #ifndef NNUE_FEATURES_COMMON_H_INCLUDED
+10 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,19 +21,16 @@
#include "half_kp.h" #include "half_kp.h"
#include "index_list.h" #include "index_list.h"
namespace Eval::NNUE::Features { namespace Stockfish::Eval::NNUE::Features {
// Orient a square according to perspective (rotates by 180 for black) // Orient a square according to perspective (rotates by 180 for black)
inline Square orient(Color perspective, Square s) { inline Square orient(Color perspective, Square s) {
return Square(int(s) ^ (bool(perspective) * 63)); return Square(int(s) ^ (bool(perspective) * 63));
} }
// Find the index of the feature quantity from the king position and PieceSquare // Index of a feature for a given king position and another piece on some square
template <Side AssociatedKing> inline IndexType make_index(Color perspective, Square s, Piece pc, Square ksq) {
inline IndexType HalfKP<AssociatedKing>::MakeIndex( return IndexType(orient(perspective, s) + kpp_board_index[perspective][pc] + PS_END * ksq);
Color perspective, Square s, Piece pc, Square ksq) {
return IndexType(orient(perspective, s) + kpp_board_index[pc][perspective] + PS_END * ksq);
} }
// Get a list of indices for active features // Get a list of indices for active features
@@ -45,28 +42,27 @@ namespace Eval::NNUE::Features {
Bitboard bb = pos.pieces() & ~pos.pieces(KING); Bitboard bb = pos.pieces() & ~pos.pieces(KING);
while (bb) { while (bb) {
Square s = pop_lsb(&bb); Square s = pop_lsb(&bb);
active->push_back(MakeIndex(perspective, s, pos.piece_on(s), ksq)); active->push_back(make_index(perspective, s, pos.piece_on(s), ksq));
} }
} }
// 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, Color perspective, const Position& pos, const DirtyPiece& dp, Color perspective,
IndexList* removed, IndexList* added) { IndexList* removed, IndexList* added) {
Square ksq = orient(perspective, pos.square<KING>(perspective)); Square ksq = orient(perspective, pos.square<KING>(perspective));
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]; Piece pc = dp.piece[i];
if (type_of(pc) == KING) continue; if (type_of(pc) == KING) continue;
if (dp.from[i] != SQ_NONE) if (dp.from[i] != SQ_NONE)
removed->push_back(MakeIndex(perspective, dp.from[i], pc, ksq)); removed->push_back(make_index(perspective, dp.from[i], pc, ksq));
if (dp.to[i] != SQ_NONE) if (dp.to[i] != SQ_NONE)
added->push_back(MakeIndex(perspective, dp.to[i], pc, ksq)); added->push_back(make_index(perspective, dp.to[i], pc, ksq));
} }
} }
template class HalfKP<Side::kFriend>; template class HalfKP<Side::kFriend>;
} // namespace Eval::NNUE::Features } // namespace Stockfish::Eval::NNUE::Features
+4 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,7 @@
#include "../../evaluate.h" #include "../../evaluate.h"
#include "features_common.h" #include "features_common.h"
namespace Eval::NNUE::Features { namespace Stockfish::Eval::NNUE::Features {
// Feature HalfKP: Combination of the position of own king // Feature HalfKP: Combination of the position of own king
// and the position of pieces other than kings // and the position of pieces other than kings
@@ -50,14 +50,10 @@ 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, Color perspective, static void AppendChangedIndices(const Position& pos, const DirtyPiece& dp, Color perspective,
IndexList* removed, IndexList* added); IndexList* removed, IndexList* added);
private:
// Index of a feature for a given king position and another piece on some square
static IndexType MakeIndex(Color perspective, Square s, Piece pc, Square sq_k);
}; };
} // namespace Eval::NNUE::Features } // namespace Stockfish::Eval::NNUE::Features
#endif // #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED #endif // #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED
+3 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,7 @@
#include "../../position.h" #include "../../position.h"
#include "../nnue_architecture.h" #include "../nnue_architecture.h"
namespace Eval::NNUE::Features { namespace Stockfish::Eval::NNUE::Features {
// Class template used for feature index list // Class template used for feature index list
template <typename T, std::size_t MaxSize> template <typename T, std::size_t MaxSize>
@@ -59,6 +59,6 @@ namespace Eval::NNUE::Features {
: public ValueList<IndexType, RawFeatures::kMaxActiveDimensions> { : public ValueList<IndexType, RawFeatures::kMaxActiveDimensions> {
}; };
} // namespace Eval::NNUE::Features } // namespace Stockfish::Eval::NNUE::Features
#endif // NNUE_FEATURES_INDEX_LIST_H_INCLUDED #endif // NNUE_FEATURES_INDEX_LIST_H_INCLUDED
+305 -107
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,7 @@
#include <iostream> #include <iostream>
#include "../nnue_common.h" #include "../nnue_common.h"
namespace Eval::NNUE::Layers { namespace Stockfish::Eval::NNUE::Layers {
// Affine transformation layer // Affine transformation layer
template <typename PreviousLayer, IndexType OutputDimensions> template <typename PreviousLayer, IndexType OutputDimensions>
@@ -41,6 +41,11 @@ 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 =
@@ -65,7 +70,58 @@ namespace Eval::NNUE::Layers {
for (std::size_t i = 0; i < kOutputDimensions; ++i) for (std::size_t i = 0; i < kOutputDimensions; ++i)
biases_[i] = read_little_endian<BiasType>(stream); biases_[i] = read_little_endian<BiasType>(stream);
for (std::size_t i = 0; i < kOutputDimensions * kPaddedInputDimensions; ++i) for (std::size_t i = 0; i < kOutputDimensions * kPaddedInputDimensions; ++i)
#if !defined (USE_SSSE3)
weights_[i] = read_little_endian<WeightType>(stream); weights_[i] = read_little_endian<WeightType>(stream);
#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();
} }
@@ -74,122 +130,246 @@ 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);
#if defined(USE_AVX512) static_assert(kOutputDimensions % kOutputSimdWidth == 0 || kOutputDimensions == 1);
constexpr IndexType kNumChunks = kPaddedInputDimensions / (kSimdWidth * 2);
const auto input_vector = reinterpret_cast<const __m512i*>(input);
#if !defined(USE_VNNI)
const __m512i kOnes = _mm512_set1_epi16(1);
#endif
#elif defined(USE_AVX2) // kOutputDimensions is either 1 or a multiple of kSimdWidth
// 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 auto input_vector = reinterpret_cast<const __m256i*>(input);
#if !defined(USE_VNNI)
const __m256i kOnes = _mm256_set1_epi16(1);
#endif
#elif defined(USE_SSE2)
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
#ifndef USE_SSSE3
const __m128i kZeros = _mm_setzero_si128(); const __m128i kZeros = _mm_setzero_si128();
#else
const __m128i kOnes = _mm_set1_epi16(1);
#endif
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_MMX)
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
const __m64 kZeros = _mm_setzero_si64(); const __m64 kZeros = _mm_setzero_si64();
const auto input_vector = reinterpret_cast<const __m64*>(input); const auto input_vector = reinterpret_cast<const __m64*>(input);
#elif defined(USE_NEON) #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_AVX512) #if defined(USE_SSE2)
__m512i sum = _mm512_setzero_si512();
const auto row = reinterpret_cast<const __m512i*>(&weights_[offset]);
for (IndexType j = 0; j < kNumChunks; ++j) {
#if defined(USE_VNNI)
sum = _mm512_dpbusd_epi32(sum, _mm512_loadA_si512(&input_vector[j]), _mm512_load_si512(&row[j]));
#else
__m512i product = _mm512_maddubs_epi16(_mm512_loadA_si512(&input_vector[j]), _mm512_load_si512(&row[j]));
product = _mm512_madd_epi16(product, kOnes);
sum = _mm512_add_epi32(sum, product);
#endif
}
// Note: Changing kMaxSimdWidth from 32 to 64 breaks loading existing networks.
// As a result kPaddedInputDimensions may not be an even multiple of 64(512bit)
// and we have to do one more 256bit chunk.
if (kPaddedInputDimensions != kNumChunks * kSimdWidth * 2)
{
const auto iv256 = reinterpret_cast<const __m256i*>(&input_vector[kNumChunks]);
const auto row256 = reinterpret_cast<const __m256i*>(&row[kNumChunks]);
#if defined(USE_VNNI)
__m256i product256 = _mm256_dpbusd_epi32(
_mm512_castsi512_si256(sum), _mm256_loadA_si256(&iv256[0]), _mm256_load_si256(&row256[0]));
sum = _mm512_inserti32x8(sum, product256, 0);
#else
__m256i product256 = _mm256_maddubs_epi16(_mm256_loadA_si256(&iv256[0]), _mm256_load_si256(&row256[0]));
sum = _mm512_add_epi32(sum, _mm512_cvtepi16_epi32(product256));
#endif
}
output[i] = _mm512_reduce_add_epi32(sum) + biases_[i];
#elif defined(USE_AVX2)
__m256i sum = _mm256_setzero_si256();
const auto row = reinterpret_cast<const __m256i*>(&weights_[offset]);
for (IndexType j = 0; j < kNumChunks; ++j) {
#if defined(USE_VNNI)
sum = _mm256_dpbusd_epi32(sum, _mm256_loadA_si256(&input_vector[j]), _mm256_load_si256(&row[j]));
#else
__m256i product = _mm256_maddubs_epi16(_mm256_loadA_si256(&input_vector[j]), _mm256_load_si256(&row[j]));
product = _mm256_madd_epi16(product, kOnes);
sum = _mm256_add_epi32(sum, product);
#endif
}
__m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1));
sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC));
sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB));
output[i] = _mm_cvtsi128_si32(sum128) + biases_[i];
#elif defined(USE_SSSE3)
__m128i sum = _mm_setzero_si128();
const auto row = reinterpret_cast<const __m128i*>(&weights_[offset]);
for (int j = 0; j < (int)kNumChunks - 1; j += 2) {
__m128i product0 = _mm_maddubs_epi16(_mm_load_si128(&input_vector[j]), _mm_load_si128(&row[j]));
product0 = _mm_madd_epi16(product0, kOnes);
sum = _mm_add_epi32(sum, product0);
__m128i product1 = _mm_maddubs_epi16(_mm_load_si128(&input_vector[j+1]), _mm_load_si128(&row[j+1]));
product1 = _mm_madd_epi16(product1, kOnes);
sum = _mm_add_epi32(sum, product1);
}
if (kNumChunks & 0x1) {
__m128i product = _mm_maddubs_epi16(_mm_load_si128(&input_vector[kNumChunks-1]), _mm_load_si128(&row[kNumChunks-1]));
product = _mm_madd_epi16(product, kOnes);
sum = _mm_add_epi32(sum, product);
}
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB
output[i] = _mm_cvtsi128_si32(sum) + biases_[i];
#elif defined(USE_SSE2)
__m128i sum_lo = _mm_cvtsi32_si128(biases_[i]); __m128i sum_lo = _mm_cvtsi32_si128(biases_[i]);
__m128i sum_hi = kZeros; __m128i sum_hi = kZeros;
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 row_j = _mm_load_si128(&row[j]);
__m128i input_j = _mm_load_si128(&input_vector[j]); __m128i input_j = _mm_load_si128(&input_vector[j]);
__m128i row_signs = _mm_cmpgt_epi8(kZeros, row_j); __m128i extended_row_lo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8);
__m128i extended_row_lo = _mm_unpacklo_epi8(row_j, row_signs); __m128i extended_row_hi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8);
__m128i extended_row_hi = _mm_unpackhi_epi8(row_j, row_signs);
__m128i extended_input_lo = _mm_unpacklo_epi8(input_j, kZeros); __m128i extended_input_lo = _mm_unpacklo_epi8(input_j, kZeros);
__m128i extended_input_hi = _mm_unpackhi_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_lo = _mm_madd_epi16(extended_row_lo, extended_input_lo);
@@ -204,16 +384,15 @@ namespace Eval::NNUE::Layers {
sum = _mm_add_epi32(sum, sum_second_32); 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_MMX)
__m64 sum_lo = _mm_cvtsi32_si64(biases_[i]); __m64 sum_lo = _mm_cvtsi32_si64(biases_[i]);
__m64 sum_hi = kZeros; __m64 sum_hi = kZeros;
const auto row = reinterpret_cast<const __m64*>(&weights_[offset]); const auto row = reinterpret_cast<const __m64*>(&weights_[offset]);
for (IndexType j = 0; j < kNumChunks; ++j) { for (IndexType j = 0; j < kNumChunks; ++j) {
__m64 row_j = row[j]; __m64 row_j = row[j];
__m64 input_j = input_vector[j]; __m64 input_j = input_vector[j];
__m64 row_signs = _mm_cmpgt_pi8(kZeros, row_j); __m64 extended_row_lo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8);
__m64 extended_row_lo = _mm_unpacklo_pi8(row_j, row_signs); __m64 extended_row_hi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8);
__m64 extended_row_hi = _mm_unpackhi_pi8(row_j, row_signs);
__m64 extended_input_lo = _mm_unpacklo_pi8(input_j, kZeros); __m64 extended_input_lo = _mm_unpacklo_pi8(input_j, kZeros);
__m64 extended_input_hi = _mm_unpackhi_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_lo = _mm_madd_pi16(extended_row_lo, extended_input_lo);
@@ -225,7 +404,7 @@ namespace Eval::NNUE::Layers {
sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum)); sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum));
output[i] = _mm_cvtsi64_si32(sum); output[i] = _mm_cvtsi64_si32(sum);
#elif defined(USE_NEON) #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) {
@@ -235,18 +414,21 @@ 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) #if defined(USE_MMX)
_mm_empty(); _mm_empty();
#endif #endif
#endif
return output; return output;
} }
@@ -257,10 +439,26 @@ namespace Eval::NNUE::Layers {
PreviousLayer previous_layer_; PreviousLayer previous_layer_;
alignas(kCacheLineSize) BiasType biases_[kOutputDimensions]; alignas(kCacheLineSize) BiasType biases_[kOutputDimensions];
alignas(kCacheLineSize) alignas(kCacheLineSize) WeightType weights_[kOutputDimensions * kPaddedInputDimensions];
WeightType weights_[kOutputDimensions * kPaddedInputDimensions]; #if defined (USE_SSSE3)
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 Stockfish::Eval::NNUE::Layers
#endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED #endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED
+8 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,7 +23,7 @@
#include "../nnue_common.h" #include "../nnue_common.h"
namespace Eval::NNUE::Layers { namespace Stockfish::Eval::NNUE::Layers {
// Clipped ReLU // Clipped ReLU
template <typename PreviousLayer> template <typename PreviousLayer>
@@ -74,12 +74,12 @@ 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_loadA_si256(&in[i * 4 + 0]), _mm256_load_si256(&in[i * 4 + 0]),
_mm256_loadA_si256(&in[i * 4 + 1])), kWeightScaleBits); _mm256_load_si256(&in[i * 4 + 1])), kWeightScaleBits);
const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32(
_mm256_loadA_si256(&in[i * 4 + 2]), _mm256_load_si256(&in[i * 4 + 2]),
_mm256_loadA_si256(&in[i * 4 + 3])), kWeightScaleBits); _mm256_load_si256(&in[i * 4 + 3])), kWeightScaleBits);
_mm256_storeA_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( _mm256_store_si256(&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;
@@ -161,6 +161,6 @@ namespace Eval::NNUE::Layers {
PreviousLayer previous_layer_; PreviousLayer previous_layer_;
}; };
} // namespace Eval::NNUE::Layers } // namespace Stockfish::Eval::NNUE::Layers
#endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED #endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED
+3 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,7 +23,7 @@
#include "../nnue_common.h" #include "../nnue_common.h"
namespace Eval::NNUE::Layers { namespace Stockfish::Eval::NNUE::Layers {
// Input layer // Input layer
template <IndexType OutputDimensions, IndexType Offset = 0> template <IndexType OutputDimensions, IndexType Offset = 0>
@@ -63,6 +63,6 @@ class InputSlice {
private: private:
}; };
} // namespace Layers } // namespace Stockfish::Eval::NNUE::Layers
#endif // #ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED #endif // #ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED
+7 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,15 +23,18 @@
#include "nnue_architecture.h" #include "nnue_architecture.h"
namespace Eval::NNUE { namespace Stockfish::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(kCacheLineSize) Accumulator {
std::int16_t std::int16_t
accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions]; accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions];
bool computed_accumulation; AccumulatorState state[2];
}; };
} // namespace Eval::NNUE } // namespace Stockfish::Eval::NNUE
#endif // NNUE_ACCUMULATOR_H_INCLUDED #endif // NNUE_ACCUMULATOR_H_INCLUDED
+3 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,7 @@
// Defines the network structure // Defines the network structure
#include "architectures/halfkp_256x2-32-32.h" #include "architectures/halfkp_256x2-32-32.h"
namespace Eval::NNUE { namespace Stockfish::Eval::NNUE {
static_assert(kTransformedFeatureDimensions % kMaxSimdWidth == 0, ""); static_assert(kTransformedFeatureDimensions % kMaxSimdWidth == 0, "");
static_assert(Network::kOutputDimensions == 1, ""); static_assert(Network::kOutputDimensions == 1, "");
@@ -33,6 +33,6 @@ namespace Eval::NNUE {
// Trigger for full calculation instead of difference calculation // Trigger for full calculation instead of difference calculation
constexpr auto kRefreshTriggers = RawFeatures::kRefreshTriggers; constexpr auto kRefreshTriggers = RawFeatures::kRefreshTriggers;
} // namespace Eval::NNUE } // namespace Stockfish::Eval::NNUE
#endif // #ifndef NNUE_ARCHITECTURE_H_INCLUDED #endif // #ifndef NNUE_ARCHITECTURE_H_INCLUDED
+11 -27
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,30 +43,7 @@
#include <arm_neon.h> #include <arm_neon.h>
#endif #endif
// HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Otherwise a binary namespace Stockfish::Eval::NNUE {
// compiled with older g++ crashes because the output memory is not aligned
// even though alignas is specified.
#if defined(USE_AVX2)
#if defined(__GNUC__ ) && (__GNUC__ < 9) && defined(_WIN32) && !defined(__clang__)
#define _mm256_loadA_si256 _mm256_loadu_si256
#define _mm256_storeA_si256 _mm256_storeu_si256
#else
#define _mm256_loadA_si256 _mm256_load_si256
#define _mm256_storeA_si256 _mm256_store_si256
#endif
#endif
#if defined(USE_AVX512)
#if defined(__GNUC__ ) && (__GNUC__ < 9) && defined(_WIN32) && !defined(__clang__)
#define _mm512_loadA_si512 _mm512_loadu_si512
#define _mm512_storeA_si512 _mm512_storeu_si512
#else
#define _mm512_loadA_si512 _mm512_load_si512
#define _mm512_storeA_si512 _mm512_store_si512
#endif
#endif
namespace Eval::NNUE {
// Version of the evaluation file // Version of the evaluation file
constexpr std::uint32_t kVersion = 0x7AF32F16u; constexpr std::uint32_t kVersion = 0x7AF32F16u;
@@ -113,7 +90,14 @@ namespace Eval::NNUE {
PS_END2 = 12 * SQUARE_NB + 1 PS_END2 = 12 * SQUARE_NB + 1
}; };
extern uint32_t kpp_board_index[PIECE_NB][COLOR_NB]; 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;
@@ -143,6 +127,6 @@ namespace Eval::NNUE {
return result; return result;
} }
} // namespace Eval::NNUE } // namespace Stockfish::Eval::NNUE
#endif // #ifndef NNUE_COMMON_H_INCLUDED #endif // #ifndef NNUE_COMMON_H_INCLUDED
+175 -145
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,25 +27,25 @@
#include <cstring> // std::memset() #include <cstring> // std::memset()
namespace Eval::NNUE { namespace Stockfish::Eval::NNUE {
// If vector instructions are enabled, we update and refresh the // If vector instructions are enabled, we update and refresh the
// accumulator tile by tile such that each tile fits in the CPU's // accumulator tile by tile such that each tile fits in the CPU's
// vector registers. // vector registers.
#define TILING #define VECTOR
#ifdef USE_AVX512 #ifdef USE_AVX512
typedef __m512i vec_t; typedef __m512i vec_t;
#define vec_load(a) _mm512_loadA_si512(a) #define vec_load(a) _mm512_load_si512(a)
#define vec_store(a,b) _mm512_storeA_si512(a,b) #define vec_store(a,b) _mm512_store_si512(a,b)
#define vec_add_16(a,b) _mm512_add_epi16(a,b) #define vec_add_16(a,b) _mm512_add_epi16(a,b)
#define vec_sub_16(a,b) _mm512_sub_epi16(a,b) #define vec_sub_16(a,b) _mm512_sub_epi16(a,b)
static constexpr IndexType kNumRegs = 8; // only 8 are needed static constexpr IndexType kNumRegs = 8; // only 8 are needed
#elif USE_AVX2 #elif USE_AVX2
typedef __m256i vec_t; typedef __m256i vec_t;
#define vec_load(a) _mm256_loadA_si256(a) #define vec_load(a) _mm256_load_si256(a)
#define vec_store(a,b) _mm256_storeA_si256(a,b) #define vec_store(a,b) _mm256_store_si256(a,b)
#define vec_add_16(a,b) _mm256_add_epi16(a,b) #define vec_add_16(a,b) _mm256_add_epi16(a,b)
#define vec_sub_16(a,b) _mm256_sub_epi16(a,b) #define vec_sub_16(a,b) _mm256_sub_epi16(a,b)
static constexpr IndexType kNumRegs = 16; static constexpr IndexType kNumRegs = 16;
@@ -75,7 +75,7 @@ namespace Eval::NNUE {
static constexpr IndexType kNumRegs = 16; static constexpr IndexType kNumRegs = 16;
#else #else
#undef TILING #undef VECTOR
#endif #endif
@@ -86,7 +86,7 @@ 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 TILING #ifdef VECTOR
static constexpr IndexType kTileHeight = kNumRegs * sizeof(vec_t) / 2; static constexpr IndexType kTileHeight = kNumRegs * sizeof(vec_t) / 2;
static_assert(kHalfDimensions % kTileHeight == 0, "kTileHeight must divide kHalfDimensions"); static_assert(kHalfDimensions % kTileHeight == 0, "kTileHeight must divide kHalfDimensions");
#endif #endif
@@ -119,31 +119,21 @@ namespace Eval::NNUE {
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) const {
if (!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_AVX2) #if defined(USE_AVX512)
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();
@@ -170,14 +160,25 @@ 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_AVX2) #if defined(USE_AVX512)
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_loadA_si256( __m256i sum0 = _mm256_load_si256(
&reinterpret_cast<const __m256i*>(accumulation[perspectives[p]][0])[j * 2 + 0]); &reinterpret_cast<const __m256i*>(accumulation[perspectives[p]][0])[j * 2 + 0]);
__m256i sum1 = _mm256_loadA_si256( __m256i sum1 = _mm256_load_si256(
&reinterpret_cast<const __m256i*>(accumulation[perspectives[p]][0])[j * 2 + 1]); &reinterpret_cast<const __m256i*>(accumulation[perspectives[p]][0])[j * 2 + 1]);
_mm256_storeA_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( _mm256_store_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8(
_mm256_packs_epi16(sum0, sum1), kZero), kControl)); _mm256_packs_epi16(sum0, sum1), kZero), kControl));
} }
@@ -193,9 +194,9 @@ 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
); );
@@ -235,27 +236,142 @@ namespace Eval::NNUE {
} }
private: private:
// Calculate cumulative value without using difference calculation void UpdateAccumulator(const Position& pos, const Color c) const {
void RefreshAccumulator(const Position& pos) const {
auto& accumulator = pos.state()->accumulator; #ifdef VECTOR
IndexType i = 0; // Gcc-10.2 unnecessarily spills AVX2 registers if this array
Features::IndexList active_indices[2]; // is defined in the VECTOR code below, once in each branch
RawFeatures::AppendActiveIndices(pos, kRefreshTriggers[i], vec_t acc[kNumRegs];
active_indices); #endif
for (Color perspective : { WHITE, BLACK }) {
#ifdef TILING // Look for a usable accumulator of an earlier position. We keep track
for (unsigned j = 0; j < kHalfDimensions / kTileHeight; ++j) { // of the estimated gain in terms of features to be added/subtracted.
StateInfo *st = pos.state(), *next = nullptr;
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*>( auto biasesTile = reinterpret_cast<const vec_t*>(
&biases_[j * kTileHeight]); &biases_[j * kTileHeight]);
auto accTile = reinterpret_cast<vec_t*>( for (IndexType k = 0; k < kNumRegs; ++k)
&accumulator.accumulation[perspective][i][j * kTileHeight]);
vec_t acc[kNumRegs];
for (unsigned k = 0; k < kNumRegs; ++k)
acc[k] = biasesTile[k]; acc[k] = biasesTile[k];
for (const auto index : active_indices[perspective]) { for (const auto index : active)
{
const IndexType offset = kHalfDimensions * index + j * kTileHeight; const IndexType offset = kHalfDimensions * index + j * kTileHeight;
auto column = reinterpret_cast<const vec_t*>(&weights_[offset]); auto column = reinterpret_cast<const vec_t*>(&weights_[offset]);
@@ -263,18 +379,22 @@ namespace Eval::NNUE {
acc[k] = vec_add_16(acc[k], column[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++) for (unsigned k = 0; k < kNumRegs; k++)
vec_store(&accTile[k], acc[k]); vec_store(&accTile[k], acc[k]);
} }
#else #else
std::memcpy(accumulator.accumulation[perspective][i], biases_, std::memcpy(accumulator.accumulation[c][0], biases_,
kHalfDimensions * sizeof(BiasType)); kHalfDimensions * sizeof(BiasType));
for (const auto index : active_indices[perspective]) { for (const auto index : active)
{
const IndexType offset = kHalfDimensions * index; const IndexType offset = kHalfDimensions * index;
for (IndexType j = 0; j < kHalfDimensions; ++j) for (IndexType j = 0; j < kHalfDimensions; ++j)
accumulator.accumulation[perspective][i][j] += weights_[offset + j]; accumulator.accumulation[c][0][j] += weights_[offset + j];
} }
#endif #endif
} }
@@ -282,96 +402,6 @@ namespace Eval::NNUE {
#if defined(USE_MMX) #if defined(USE_MMX)
_mm_empty(); _mm_empty();
#endif #endif
accumulator.computed_accumulation = true;
}
// 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);
#ifdef TILING
for (IndexType j = 0; j < kHalfDimensions / kTileHeight; ++j) {
for (Color perspective : { WHITE, BLACK }) {
auto accTile = reinterpret_cast<vec_t*>(
&accumulator.accumulation[perspective][i][j * kTileHeight]);
vec_t acc[kNumRegs];
if (reset[perspective]) {
auto biasesTile = reinterpret_cast<const vec_t*>(
&biases_[j * kTileHeight]);
for (unsigned k = 0; k < kNumRegs; ++k)
acc[k] = biasesTile[k];
} else {
auto prevAccTile = reinterpret_cast<const vec_t*>(
&prev_accumulator.accumulation[perspective][i][j * kTileHeight]);
for (IndexType k = 0; k < kNumRegs; ++k)
acc[k] = vec_load(&prevAccTile[k]);
// Difference calculation for the deactivated features
for (const auto index : removed_indices[perspective]) {
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_indices[perspective]) {
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]);
}
}
for (IndexType k = 0; k < kNumRegs; ++k)
vec_store(&accTile[k], acc[k]);
}
}
#if defined(USE_MMX)
_mm_empty();
#endif
#else
for (Color perspective : { WHITE, BLACK }) {
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;
for (IndexType j = 0; j < kHalfDimensions; ++j)
accumulator.accumulation[perspective][i][j] -= weights_[offset + j];
}
}
{ // Difference calculation for the activated features
for (const auto index : added_indices[perspective]) {
const IndexType offset = kHalfDimensions * index;
for (IndexType j = 0; j < kHalfDimensions; ++j)
accumulator.accumulation[perspective][i][j] += weights_[offset + j];
}
}
}
#endif
accumulator.computed_accumulation = true;
} }
using BiasType = std::int16_t; using BiasType = std::int16_t;
@@ -382,6 +412,6 @@ namespace Eval::NNUE {
WeightType weights_[kHalfDimensions * kInputDimensions]; WeightType weights_[kHalfDimensions * kInputDimensions];
}; };
} // namespace Eval::NNUE } // namespace Stockfish::Eval::NNUE
#endif // #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED #endif // #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED
+47 -24
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,35 +24,38 @@
#include "position.h" #include "position.h"
#include "thread.h" #include "thread.h"
namespace Stockfish {
namespace { namespace {
#define V Value #define V Value
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// Pawn penalties // Pawn penalties
constexpr Score Backward = S( 8, 27); constexpr Score Backward = S( 9, 22);
constexpr Score Doubled = S(11, 55); constexpr Score Doubled = S(13, 51);
constexpr Score Isolated = S( 5, 17); constexpr Score DoubledEarly = S(20, 7);
constexpr Score WeakLever = S( 2, 54); constexpr Score Isolated = S( 3, 15);
constexpr Score WeakUnopposed = S(15, 25); constexpr Score WeakLever = S( 4, 58);
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(-13, -4), S(-4, 3) }; constexpr Score BlockedPawn[2] = { S(-17, -6), S(-9, 2) };
constexpr Score BlockedStorm[RANK_NB] = { constexpr Score BlockedStorm[RANK_NB] = {
S(0, 0), S(0, 0), S(76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2) S(0, 0), S(0, 0), S(75, 78), S(-8, 16), S(-6, 10), S(-6, 6), S(0, 2)
}; };
// Connected pawn bonus // Connected pawn bonus
constexpr int Connected[RANK_NB] = { 0, 7, 8, 11, 24, 45, 85 }; constexpr int Connected[RANK_NB] = { 0, 5, 7, 11, 23, 48, 87 };
// 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( -6), V( 81), V( 93), V( 58), V( 39), V( 18), V( 25) }, { V( -5), V( 82), V( 92), V( 54), V( 36), V( 22), V( 28) },
{ V(-43), V( 61), V( 35), V(-49), V(-29), V(-11), V( -63) }, { V(-44), V( 63), V( 33), V(-50), V(-30), V(-12), V( -62) },
{ V(-10), V( 75), V( 23), V( -2), V( 32), V( 3), V( -45) }, { V(-11), V( 77), V( 22), V( -6), V( 31), V( 8), V( -45) },
{ V(-39), V(-13), V(-29), V(-52), V(-48), V(-67), V(-166) } { V(-39), V(-12), V(-29), V(-50), V(-43), V(-68), V(-164) }
}; };
// 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].
@@ -60,12 +63,18 @@ 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( 85), V(-289), V(-166), V(97), V(50), V( 45), V( 50) }, { V( 87), V(-288), V(-168), V( 96), V( 47), V( 44), V( 46) },
{ V( 46), V( -25), V( 122), V(45), V(37), V(-10), V( 20) }, { V( 42), V( -25), V( 120), V( 45), V( 34), V( -9), V( 24) },
{ V( -6), V( 51), V( 168), V(34), V(-2), V(-22), V(-14) }, { V( -8), V( 51), V( 167), V( 35), V( -4), V(-16), V(-12) },
{ V(-15), V( -11), V( 101), V( 4), V(11), V(-15), V(-29) } { V(-17), V( -13), V( 100), V( 4), V( 9), V(-16), V(-31) }
}; };
// 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
@@ -80,13 +89,14 @@ 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;
const Square* pl = pos.squares<PAWN>(Us); Bitboard b = pos.pieces(Us, PAWN);
Bitboard ourPawns = pos.pieces( Us, PAWN); Bitboard ourPawns = pos.pieces( Us, PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN);
@@ -99,8 +109,9 @@ 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 ((s = *pl++) != SQ_NONE) while (b) {
{ 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);
@@ -116,6 +127,13 @@ 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))
@@ -147,7 +165,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))
+ 21 * popcount(support); + 22 * popcount(support);
score += make_score(v, v * (r - 2) / 4); score += make_score(v, v * (r - 2) / 4);
} }
@@ -165,14 +183,14 @@ namespace {
else if (backward) else if (backward)
score -= Backward score -= Backward
+ WeakUnopposed * !opposed; + WeakUnopposed * !opposed * bool(~(FileABB | FileHBB) & s);
if (!support) if (!support)
score -= Doubled * doubled score -= Doubled * doubled
+ WeakLever * more_than_one(lever); + WeakLever * more_than_one(lever);
if (blocked && r > RANK_4) if (blocked && r >= RANK_5)
score += BlockedPawn[r-4]; score += BlockedPawn[r - RANK_5];
} }
return score; return score;
@@ -237,6 +255,9 @@ 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;
} }
@@ -279,3 +300,5 @@ template Score Entry::do_king_safety<WHITE>(const Position& pos);
template Score Entry::do_king_safety<BLACK>(const Position& pos); template Score Entry::do_king_safety<BLACK>(const Position& pos);
} // namespace Pawns } // namespace Pawns
} // namespace Stockfish
+3 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,7 +23,7 @@
#include "position.h" #include "position.h"
#include "types.h" #include "types.h"
namespace Pawns { namespace Stockfish::Pawns {
/// Pawns::Entry contains various information about a pawn structure. A lookup /// Pawns::Entry contains various information about a pawn structure. A lookup
/// to the pawn hash table (performed by calling the probe function) returns a /// to the pawn hash table (performed by calling the probe function) returns a
@@ -65,6 +65,6 @@ typedef HashTable<Entry, 131072> Table;
Entry* probe(const Position& pos); Entry* probe(const Position& pos);
} // namespace Pawns } // namespace Stockfish::Pawns
#endif // #ifndef PAWNS_H_INCLUDED #endif // #ifndef PAWNS_H_INCLUDED
+70 -72
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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
@@ -34,6 +34,8 @@
using std::string; using std::string;
namespace Stockfish {
namespace Zobrist { namespace Zobrist {
Key psq[PIECE_NB][SQUARE_NB]; Key psq[PIECE_NB][SQUARE_NB];
@@ -77,6 +79,8 @@ 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;
@@ -195,7 +199,6 @@ 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;
ss >> std::noskipws; ss >> std::noskipws;
@@ -248,6 +251,8 @@ 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;
@@ -261,12 +266,24 @@ 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))));
} }
if (!enpassant) // It's necessary for st->previous to be intialized in this way because legality check relies on its existence
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
@@ -278,7 +295,8 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
chess960 = isChess960; chess960 = isChess960;
thisThread = th; thisThread = th;
set_state(st); st->accumulator.state[WHITE] = Eval::NNUE::INIT;
st->accumulator.state[BLACK] = Eval::NNUE::INIT;
assert(pos_is_ok()); assert(pos_is_ok());
@@ -392,7 +410,7 @@ Position& Position::set(const string& code, Color c, StateInfo* si) {
/// Position::fen() returns a FEN representation of the position. In case of /// Position::fen() returns a FEN representation of the position. In case of
/// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function. /// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function.
const string Position::fen() const { string Position::fen() const {
int emptyCnt; int emptyCnt;
std::ostringstream ss; std::ostringstream ss;
@@ -499,23 +517,11 @@ 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));
// En passant captures are a tricky special case. Because they are rather // st->previous->blockersForKing consider capsq as empty.
// uncommon, we do it simply by testing whether the king is attacked after // If pinned, it has to move along the king ray.
// the move is made. if (type_of(m) == EN_PASSANT)
if (type_of(m) == ENPASSANT) return !(st->previous->blockersForKing[sideToMove] & from)
{ || 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!
@@ -530,11 +536,9 @@ 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 that when moving the castling rook we do // In case of Chess960, verify if the Rook blocks some checks
// 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 return !chess960 || !(blockers_for_king(us) & to_sq(m));
|| !(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
@@ -544,8 +548,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));
} }
@@ -561,8 +565,10 @@ 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 MoveList<LEGAL>(*this).contains(m); return checkers() ? MoveList< EVASIONS>(*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)
@@ -648,31 +654,24 @@ 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);
// En passant capture with check? We have already handled the case // The double-pushed pawn blocked a check? En Passant will remove the blocker.
// of direct checks and ordinary discovered check, so the only case we // The only discovery check that wasn't handle is through capsq and fromsq
// need to handle is the unusual case of a discovered check through // So the King must be in the same rank as fromsq to consider this possibility.
// the captured pawn. // st->previous->blockersForKing consider capsq as empty.
case ENPASSANT: case EN_PASSANT:
{ return st->previous->checkersBB
Square capsq = make_square(file_of(to), rank_of(from)); || ( rank_of(square<KING>(~sideToMove)) == rank_of(from)
Bitboard b = (pieces() ^ from ^ capsq) | to; && st->previous->blockersForKing[~sideToMove] & from);
return (attacks_bb< ROOK>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK)) default: //CASTLING
| (attacks_bb<BISHOP>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP));
}
case CASTLING:
{ {
Square kfrom = from; // Castling is encoded as 'king captures the rook'
Square rfrom = to; // Castling is encoded as 'king captures the rook' Square ksq = square<KING>(~sideToMove);
Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1); Square rto = relative_square(sideToMove, to > from ? SQ_F1 : SQ_D1);
Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1);
return (attacks_bb<ROOK>(rto) & square<KING>(~sideToMove)) return (attacks_bb<ROOK>(rto) & ksq)
&& (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove)); && (attacks_bb<ROOK>(rto, pieces() ^ from ^ to) & ksq);
} }
default:
assert(false);
return false;
} }
} }
@@ -703,7 +702,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
++st->pliesFromNull; ++st->pliesFromNull;
// Used by NNUE // Used by NNUE
st->accumulator.computed_accumulation = false; st->accumulator.state[WHITE] = Eval::NNUE::EMPTY;
st->accumulator.state[BLACK] = Eval::NNUE::EMPTY;
auto& dp = st->dirtyPiece; auto& dp = st->dirtyPiece;
dp.dirty_num = 1; dp.dirty_num = 1;
@@ -712,7 +712,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) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to); Piece captured = type_of(m) == EN_PASSANT ? 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));
@@ -738,7 +738,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) == ENPASSANT) if (type_of(m) == EN_PASSANT)
{ {
capsq -= pawn_push(us); capsq -= pawn_push(us);
@@ -765,7 +765,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// Update board and piece lists // Update board and piece lists
remove_piece(capsq); remove_piece(capsq);
if (type_of(m) == ENPASSANT) if (type_of(m) == EN_PASSANT)
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
@@ -811,7 +811,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)))
{ {
@@ -934,7 +934,7 @@ void Position::undo_move(Move m) {
{ {
Square capsq = to; Square capsq = to;
if (type_of(m) == ENPASSANT) if (type_of(m) == EN_PASSANT)
{ {
capsq -= pawn_push(us); capsq -= pawn_push(us);
@@ -996,16 +996,16 @@ void Position::do_null_move(StateInfo& newSt) {
assert(!checkers()); assert(!checkers());
assert(&newSt != st); assert(&newSt != st);
if (Eval::useNNUE) std::memcpy(&newSt, st, offsetof(StateInfo, accumulator));
{
std::memcpy(&newSt, st, sizeof(StateInfo));
}
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)];
@@ -1013,7 +1013,7 @@ void Position::do_null_move(StateInfo& newSt) {
} }
st->key ^= Zobrist::side; st->key ^= Zobrist::side;
prefetch(TT.first_entry(st->key)); prefetch(TT.first_entry(key()));
++st->rule50; ++st->rule50;
st->pliesFromNull = 0; st->pliesFromNull = 0;
@@ -1038,7 +1038,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 {
@@ -1063,7 +1063,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;
@@ -1315,21 +1315,17 @@ 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})
{ {
@@ -1344,3 +1340,5 @@ bool Position::pos_is_ok() const {
return true; return true;
} }
} // namespace Stockfish
+16 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,10 +26,12 @@
#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"
namespace Stockfish {
/// StateInfo struct stores information needed to restore a Position object to /// StateInfo struct stores information needed to restore a Position object to
/// its previous state when we retract a move. Whenever a move is made on the /// its previous state when we retract a move. Whenever a move is made on the
@@ -86,7 +88,7 @@ public:
// FEN string input/output // FEN string input/output
Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th); Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th);
Position& set(const std::string& code, Color c, StateInfo* si); Position& set(const std::string& code, Color c, StateInfo* si);
const std::string fen() const; std::string fen() const;
// Position representation // Position representation
Bitboard pieces(PieceType pt) const; Bitboard pieces(PieceType pt) const;
@@ -99,7 +101,6 @@ 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;
@@ -114,7 +115,7 @@ public:
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; 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;
@@ -190,8 +191,6 @@ private:
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];
@@ -203,10 +202,6 @@ private:
bool chess960; bool chess960;
}; };
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 {
@@ -254,13 +249,9 @@ 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(pieceCount[make_piece(c, Pt)] == 1); assert(count<Pt>(c) == 1);
return squares<Pt>(c)[0]; return lsb(pieces(c, Pt));
} }
inline Square Position::ep_square() const { inline Square Position::ep_square() const {
@@ -311,7 +302,7 @@ inline Bitboard Position::check_squares(PieceType pt) const {
return st->checkSquares[pt]; return st->checkSquares[pt];
} }
inline bool Position::is_discovery_check_on_king(Color c, Move m) const { inline bool Position::is_discovered_check_on_king(Color c, Move m) const {
return st->blockersForKing[c] & from_sq(m); return st->blockersForKing[c] & from_sq(m);
} }
@@ -321,7 +312,7 @@ inline bool Position::pawn_passed(Color c, Square s) const {
inline bool Position::advanced_pawn_push(Move m) const { inline bool Position::advanced_pawn_push(Move m) const {
return type_of(moved_piece(m)) == PAWN return type_of(moved_piece(m)) == PAWN
&& relative_rank(sideToMove, to_sq(m)) > RANK_5; && relative_rank(sideToMove, to_sq(m)) > RANK_6;
} }
inline int Position::pawns_on_same_color_squares(Color c, Square s) const { inline int Position::pawns_on_same_color_squares(Color c, Square s) const {
@@ -329,7 +320,8 @@ 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->key; return st->rule50 < 14 ? st->key
: st->key ^ make_key((st->rule50 - 14) / 8);
} }
inline Key Position::pawn_key() const { inline Key Position::pawn_key() const {
@@ -378,7 +370,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) == ENPASSANT; return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == EN_PASSANT;
} }
inline Piece Position::captured_piece() const { inline Piece Position::captured_piece() const {
@@ -394,35 +386,25 @@ 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;
index[s] = pieceCount[pc]++; 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 */
Square lastSquare = pieceList[pc][--pieceCount[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;
@@ -430,8 +412,6 @@ 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];
} }
@@ -444,4 +424,6 @@ inline StateInfo* Position::state() const {
return st; return st;
} }
} // namespace Stockfish
#endif // #ifndef POSITION_H_INCLUDED #endif // #ifndef POSITION_H_INCLUDED
+44 -35
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,19 +16,23 @@
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 "types.h"
#include "bitboard.h" #include "bitboard.h"
#include "types.h"
namespace PSQT { namespace Stockfish {
#define S(mg, eg) make_score(mg, eg) namespace
{
// Bonus[PieceType][Square / 2] contains Piece-Square scores. For each piece auto constexpr S = make_score;
// type on a given square a (middlegame, endgame) score pair is assigned. Table
// is defined for files A..D and white side: it is symmetric for black side and // 'Bonus' contains Piece-Square parameters.
// second half of the files. // Scores are explicit for files A to D, implicitly mirrored for E to H.
constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
{ }, { },
{ }, { },
@@ -43,14 +47,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(-53,-57), S( -5,-30), S( -8,-37), S(-23,-12) }, { S(-37,-40), S(-4 ,-21), S( -6,-26), S(-16, -8) },
{ S(-15,-37), S( 8,-13), S( 19,-17), S( 4, 1) }, { S(-11,-26), S( 6, -9), S( 13,-12), S( 3, 1) },
{ S( -7,-16), S( 21, -1), S( -5, -2), S( 17, 10) }, { S(-5 ,-11), S( 15, -1), S( -4, -1), S( 12, 7) },
{ S( -5,-20), S( 11, -6), S( 25, 0), S( 39, 17) }, { S(-4 ,-14), S( 8, -4), S( 18, 0), S( 27, 12) },
{ S(-12,-17), S( 29, -1), S( 22,-14), S( 31, 15) }, { S(-8 ,-12), S( 20, -1), S( 15,-10), S( 22, 11) },
{ S(-16,-30), S( 6, 6), S( 1, 4), S( 11, 6) }, { S(-11,-21), S( 4, 4), S( 1, 3), S( 8, 4) },
{ S(-17,-31), S(-14,-20), S( 5, -1), S( 0, 1) }, { S(-12,-22), S(-10,-14), S( 4, -1), S( 0, 1) },
{ S(-48,-46), S( 1,-42), S(-14,-37), S(-23,-24) } { S(-34,-32), S( 1,-29), S(-10,-26), S(-16,-17) }
}, },
{ // 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) },
@@ -64,13 +68,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,-55), S( 5,-31), S( 8,-22), S(12, -4) }, { S(-3,-54), 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,-12), S( 8, 1) }, { S(-4,-38), S(10,-18), S( 6,-11), 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,-75), S(-2,-52), S( 1,-43), S(-2,-36) } { S(-2,-74), S(-2,-52), S( 1,-43), S(-2,-34) }
}, },
{ // 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) },
@@ -87,19 +91,22 @@ 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( 3,-10), S( 3, -6), S( 10, 10), S( 19, 0), S( 16, 14), S( 19, 7), S( 7, -5), S( -5,-19) }, { 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( -9,-10), S(-15,-10), S( 11,-10), S( 15, 4), S( 32, 4), S( 22, 3), S( 5, -6), S(-22, -4) }, { S( -9, -9), S(-15, -7), S( 11,-10), S( 15, 5), S( 31, 2), S( 23, 3), S( 6, -8), S(-20, -5) },
{ 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( -3, 7), S(-20, 1), S( 8, -8), S( 19, -2), S( 39,-14), S( 17,-13), S( 2,-11), S( -5, -6) },
{ 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( 11, 12), S( -4, 6), S(-11, 2), S( 2, -6), S( 11, -5), S( 0, -4), S(-12, 14), S( 5, 9) },
{ S( 5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S( -8, 13) }, { S( 3, 27), S(-11, 18), S( -6, 19), S( 22, 29), S( -8, 30), S( -5, 9), S(-14, 8), S(-11, 14) },
{ S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) } { S( -7, -1), S( 6,-14), S( -2, 13), S(-11, 22), S( 4, 24), S(-14, 17), S( 10, 7), S( -9, 7) }
}; };
#undef S } // namespace
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.
@@ -107,16 +114,18 @@ 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];
} }
} }
} }
} // namespace PSQT } // namespace PSQT
} // namespace Stockfish
+38
View File
@@ -0,0 +1,38 @@
/*
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 Stockfish::PSQT
{
extern Score psq[PIECE_NB][SQUARE_NB];
// Fill psqt array from a set of internally linked parameters
extern void init();
} // namespace Stockfish::PSQT
#endif // PSQT_H_INCLUDED
+185 -116
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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
@@ -36,6 +36,8 @@
#include "uci.h" #include "uci.h"
#include "syzygy/tbprobe.h" #include "syzygy/tbprobe.h"
namespace Stockfish {
namespace Search { namespace Search {
LimitsType Limits; LimitsType Limits;
@@ -63,10 +65,9 @@ namespace {
constexpr uint64_t TtHitAverageWindow = 4096; constexpr uint64_t TtHitAverageWindow = 4096;
constexpr uint64_t TtHitAverageResolution = 1024; constexpr uint64_t TtHitAverageResolution = 1024;
// Razor and futility margins // Futility margin
constexpr int RazorMargin = 510;
Value futility_margin(Depth d, bool improving) { Value futility_margin(Depth d, bool improving) {
return Value(223 * (d - improving)); return Value(234 * (d - improving));
} }
// Reductions lookup table, initialized at startup // Reductions lookup table, initialized at startup
@@ -74,7 +75,7 @@ namespace {
Depth reduction(bool i, Depth d, int mn) { Depth reduction(bool i, Depth d, int mn) {
int r = Reductions[d] * Reductions[mn]; int r = Reductions[d] * Reductions[mn];
return (r + 509) / 1024 + (!i && r > 894); return (r + 503) / 1024 + (!i && r > 915);
} }
constexpr int futility_move_count(bool improving, Depth depth) { constexpr int futility_move_count(bool improving, Depth depth) {
@@ -83,10 +84,10 @@ namespace {
// History and stats update bonus, based on depth // History and stats update bonus, based on depth
int stat_bonus(Depth d) { int stat_bonus(Depth d) {
return d > 13 ? 29 : 17 * d * d + 134 * d - 134; return d > 14 ? 66 : 6 * d * d + 231 * d - 206;
} }
// Add a small random component to draw evaluations to avoid 3fold-blindness // Add a small random component to draw evaluations to avoid 3-fold blindness
Value value_draw(Thread* thisThread) { Value value_draw(Thread* thisThread) {
return VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1); return VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1);
} }
@@ -165,6 +166,8 @@ namespace {
uint64_t perft(Position& pos, Depth depth) { uint64_t perft(Position& pos, Depth depth) {
StateInfo st; StateInfo st;
ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize);
uint64_t cnt, nodes = 0; uint64_t cnt, nodes = 0;
const bool leaf = (depth == 2); const bool leaf = (depth == 2);
@@ -193,7 +196,7 @@ namespace {
void Search::init() { void Search::init() {
for (int i = 1; i < MAX_MOVES; ++i) for (int i = 1; i < MAX_MOVES; ++i)
Reductions[i] = int((22.0 + std::log(Threads.size())) * std::log(i)); Reductions[i] = int((21.3 + 2 * std::log(Threads.size())) * std::log(i + 0.25 * std::log(i)));
} }
@@ -228,7 +231,7 @@ void MainThread::search() {
Time.init(Limits, us, rootPos.game_ply()); Time.init(Limits, us, rootPos.game_ply());
TT.new_search(); TT.new_search();
Eval::verify_NNUE(); Eval::NNUE::verify();
if (rootMoves.empty()) if (rootMoves.empty())
{ {
@@ -438,7 +441,7 @@ void Thread::search() {
beta = std::min(prev + delta, VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE);
// Adjust contempt based on root move's previousScore (dynamic contempt) // Adjust contempt based on root move's previousScore (dynamic contempt)
int dct = ct + (105 - ct / 2) * prev / (abs(prev) + 149); int dct = ct + (113 - ct / 2) * prev / (abs(prev) + 147);
contempt = (us == WHITE ? make_score(dct, dct / 2) contempt = (us == WHITE ? make_score(dct, dct / 2)
: -make_score(dct, dct / 2)); : -make_score(dct, dct / 2));
@@ -447,11 +450,11 @@ void Thread::search() {
// Start with a small aspiration window and, in the case of a fail // Start with a small aspiration window and, in the case of a fail
// high/low, re-search with a bigger window until we don't fail // high/low, re-search with a bigger window until we don't fail
// high/low anymore. // high/low anymore.
int failedHighCnt = 0; failedHighCnt = 0;
while (true) while (true)
{ {
Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - searchAgainCounter); Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - searchAgainCounter);
bestValue = ::search<PV>(rootPos, ss, alpha, beta, adjustedDepth, false); bestValue = Stockfish::search<PV>(rootPos, ss, alpha, beta, adjustedDepth, false);
// Bring the best move to the front. It is critical that sorting // Bring the best move to the front. It is critical that sorting
// is done with a stable algorithm because all the values but the // is done with a stable algorithm because all the values but the
@@ -496,10 +499,7 @@ void Thread::search() {
++failedHighCnt; ++failedHighCnt;
} }
else else
{
++rootMoves[pvIdx].bestMoveCount;
break; break;
}
delta += delta / 4 + 5; delta += delta / 4 + 5;
@@ -559,10 +559,14 @@ void Thread::search() {
} }
double bestMoveInstability = 1 + 2 * totBestMoveChanges / Threads.size(); double bestMoveInstability = 1 + 2 * totBestMoveChanges / Threads.size();
double totalTime = rootMoves.size() == 1 ? 0 : double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability;
Time.optimum() * fallingEval * reduction * bestMoveInstability;
// Stop the search if we have exceeded the totalTime, at least 1ms search // Cap used time in case of a single legal move for a better viewer experience in tournaments
// yielding correct scores and sufficiently fast moves.
if (rootMoves.size() == 1)
totalTime = std::min(500.0, totalTime);
// Stop the search if we have exceeded the totalTime
if (Time.elapsed() > totalTime) if (Time.elapsed() > totalTime)
{ {
// If we are allowed to ponder do not stop the search now but // If we are allowed to ponder do not stop the search now but
@@ -605,6 +609,7 @@ namespace {
constexpr bool PvNode = NT == PV; constexpr bool PvNode = NT == PV;
const bool rootNode = PvNode && ss->ply == 0; const bool rootNode = PvNode && ss->ply == 0;
const Depth maxNextDepth = rootNode ? depth : depth + 1;
// Check if we have an upcoming move which draws by repetition, or // Check if we have an upcoming move which draws by repetition, or
// if the opponent had an alternative move earlier to this position. // if the opponent had an alternative move earlier to this position.
@@ -629,6 +634,8 @@ namespace {
Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[64]; Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[64];
StateInfo st; StateInfo st;
ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize);
TTEntry* tte; TTEntry* tte;
Key posKey; Key posKey;
Move ttMove, move, excludedMove, bestMove; Move ttMove, move, excludedMove, bestMove;
@@ -648,6 +655,7 @@ namespace {
moveCount = captureCount = quietCount = ss->moveCount = 0; moveCount = captureCount = quietCount = ss->moveCount = 0;
bestValue = -VALUE_INFINITE; bestValue = -VALUE_INFINITE;
maxValue = VALUE_INFINITE; maxValue = VALUE_INFINITE;
ss->distanceFromPv = (PvNode ? 0 : ss->distanceFromPv);
// Check for the available remaining time // Check for the available remaining time
if (thisThread == Threads.main()) if (thisThread == Threads.main())
@@ -707,6 +715,7 @@ namespace {
ss->ttPv = PvNode || (ss->ttHit && tte->is_pv()); ss->ttPv = PvNode || (ss->ttHit && tte->is_pv());
formerPv = ss->ttPv && !PvNode; formerPv = ss->ttPv && !PvNode;
// Update low ply history for previous move if we are near root and position is or has been in PV
if ( ss->ttPv if ( ss->ttPv
&& depth > 12 && depth > 12
&& ss->ply - 1 < MAX_LPH && ss->ply - 1 < MAX_LPH
@@ -731,6 +740,7 @@ namespace {
{ {
if (ttValue >= beta) if (ttValue >= beta)
{ {
// Bonus for a quiet ttMove that fails high
if (!pos.capture_or_promotion(ttMove)) if (!pos.capture_or_promotion(ttMove))
update_quiet_stats(pos, ss, ttMove, stat_bonus(depth), depth); update_quiet_stats(pos, ss, ttMove, stat_bonus(depth), depth);
@@ -747,6 +757,8 @@ namespace {
} }
} }
// Partial workaround for the graph history interaction problem
// For high rule50 counts don't produce transposition table cutoffs.
if (pos.rule50_count() < 90) if (pos.rule50_count() < 90)
return ttValue; return ttValue;
} }
@@ -821,6 +833,7 @@ namespace {
if (eval == VALUE_NONE) if (eval == VALUE_NONE)
ss->staticEval = eval = evaluate(pos); ss->staticEval = eval = evaluate(pos);
// Randomize draw evaluation
if (eval == VALUE_DRAW) if (eval == VALUE_DRAW)
eval = value_draw(thisThread); eval = value_draw(thisThread);
@@ -831,40 +844,48 @@ namespace {
} }
else else
{ {
// In case of null move search use previous static eval with a different sign
// and addition of two tempos
if ((ss-1)->currentMove != MOVE_NULL) if ((ss-1)->currentMove != MOVE_NULL)
ss->staticEval = eval = evaluate(pos); ss->staticEval = eval = evaluate(pos);
else else
ss->staticEval = eval = -(ss-1)->staticEval + 2 * Tempo; ss->staticEval = eval = -(ss-1)->staticEval + 2 * Tempo;
// Save static evaluation into transposition table
Cluster::save(thisThread, tte, Cluster::save(thisThread, tte,
posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE,
eval); eval);
} }
// Step 7. Razoring (~1 Elo) // Use static evaluation difference to improve quiet move ordering
if ( !rootNode // The required rootNode PV handling is not available in qsearch if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture)
&& depth == 1 {
&& eval <= alpha - RazorMargin) int bonus = std::clamp(-depth * 4 * int((ss-1)->staticEval + ss->staticEval - 2 * Tempo), -1000, 1000);
return qsearch<NT>(pos, ss, alpha, beta); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus;
}
// Set up improving flag that is used in various pruning heuristics
// We define position as improving if static evaluation of position is better
// Than the previous static evaluation at our turn
// In case of us being in check at our previous move we look at move prior to it
improving = (ss-2)->staticEval == VALUE_NONE improving = (ss-2)->staticEval == VALUE_NONE
? ss->staticEval > (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE ? ss->staticEval > (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE
: ss->staticEval > (ss-2)->staticEval; : ss->staticEval > (ss-2)->staticEval;
// Step 8. Futility pruning: child node (~50 Elo) // Step 7. Futility pruning: child node (~50 Elo)
if ( !PvNode if ( !PvNode
&& depth < 8 && depth < 9
&& eval - futility_margin(depth, improving) >= beta && eval - futility_margin(depth, improving) >= beta
&& eval < VALUE_KNOWN_WIN) // Do not return unproven wins && eval < VALUE_KNOWN_WIN) // Do not return unproven wins
return eval; return eval;
// Step 9. Null move search with verification search (~40 Elo) // Step 8. Null move search with verification search (~40 Elo)
if ( !PvNode if ( !PvNode
&& (ss-1)->currentMove != MOVE_NULL && (ss-1)->currentMove != MOVE_NULL
&& (ss-1)->statScore < 22977 && (ss-1)->statScore < 24185
&& eval >= beta && eval >= beta
&& eval >= ss->staticEval && eval >= ss->staticEval
&& ss->staticEval >= beta - 30 * depth - 28 * improving + 84 * ss->ttPv + 182 && ss->staticEval >= beta - 24 * depth - 34 * improving + 162 * ss->ttPv + 159
&& !excludedMove && !excludedMove
&& pos.non_pawn_material(us) && pos.non_pawn_material(us)
&& (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
@@ -872,7 +893,7 @@ namespace {
assert(eval - beta >= 0); assert(eval - beta >= 0);
// Null move dynamic reduction based on depth and value // Null move dynamic reduction based on depth and value
Depth R = (817 + 71 * depth) / 213 + std::min(int(eval - beta) / 192, 3); Depth R = (1062 + 68 * depth) / 256 + std::min(int(eval - beta) / 190, 3);
ss->currentMove = MOVE_NULL; ss->currentMove = MOVE_NULL;
ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0];
@@ -889,7 +910,7 @@ namespace {
if (nullValue >= VALUE_TB_WIN_IN_MAX_PLY) if (nullValue >= VALUE_TB_WIN_IN_MAX_PLY)
nullValue = beta; nullValue = beta;
if (thisThread->nmpMinPly || (abs(beta) < VALUE_KNOWN_WIN && depth < 13)) if (thisThread->nmpMinPly || (abs(beta) < VALUE_KNOWN_WIN && depth < 14))
return nullValue; return nullValue;
assert(!thisThread->nmpMinPly); // Recursive verification is not allowed assert(!thisThread->nmpMinPly); // Recursive verification is not allowed
@@ -908,9 +929,9 @@ namespace {
} }
} }
probCutBeta = beta + 176 - 49 * improving; probCutBeta = beta + 209 - 44 * improving;
// Step 10. ProbCut (~10 Elo) // Step 9. ProbCut (~10 Elo)
// If we have a good enough capture and a reduced search returns a value // If we have a good enough capture and a reduced search returns a value
// much above beta, we can (almost) safely prune the previous move. // much above beta, we can (almost) safely prune the previous move.
if ( !PvNode if ( !PvNode
@@ -974,7 +995,7 @@ namespace {
if ( !(ss->ttHit if ( !(ss->ttHit
&& tte->depth() >= depth - 3 && tte->depth() >= depth - 3
&& ttValue != VALUE_NONE)) && ttValue != VALUE_NONE))
tte->save(posKey, value_to_tt(value, ss->ply), ttPv, Cluster::save(thisThread, tte, posKey, value_to_tt(value, ss->ply), ttPv,
BOUND_LOWER, BOUND_LOWER,
depth - 3, move, ss->staticEval); depth - 3, move, ss->staticEval);
return value; return value;
@@ -983,7 +1004,7 @@ namespace {
ss->ttPv = ttPv; ss->ttPv = ttPv;
} }
// Step 11. If the position is not in TT, decrease depth by 2 // Step 10. If the position is not in TT, decrease depth by 2
if ( PvNode if ( PvNode
&& depth >= 6 && depth >= 6
&& !ttMove) && !ttMove)
@@ -991,6 +1012,23 @@ namespace {
moves_loop: // When in check, search starts from here moves_loop: // When in check, search starts from here
ttCapture = ttMove && pos.capture_or_promotion(ttMove);
// Step 11. A small Probcut idea, when we are in check
probCutBeta = beta + 400;
if ( ss->inCheck
&& !PvNode
&& depth >= 4
&& ttCapture
&& (tte->bound() & BOUND_LOWER)
&& tte->depth() >= depth - 3
&& ttValue >= probCutBeta
&& abs(ttValue) <= VALUE_KNOWN_WIN
&& abs(beta) <= VALUE_KNOWN_WIN
)
return probCutBeta;
const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory,
nullptr , (ss-4)->continuationHistory, nullptr , (ss-4)->continuationHistory,
nullptr , (ss-6)->continuationHistory }; nullptr , (ss-6)->continuationHistory };
@@ -1007,7 +1045,6 @@ moves_loop: // When in check, search starts from here
value = bestValue; value = bestValue;
singularQuietLMR = moveCountPruning = false; singularQuietLMR = moveCountPruning = false;
ttCapture = ttMove && pos.capture_or_promotion(ttMove);
// Mark this node as being searched // Mark this node as being searched
ThreadHolding th(thisThread, posKey, ss->ply); ThreadHolding th(thisThread, posKey, ss->ply);
@@ -1047,6 +1084,14 @@ moves_loop: // When in check, search starts from here
movedPiece = pos.moved_piece(move); movedPiece = pos.moved_piece(move);
givesCheck = pos.gives_check(move); givesCheck = pos.gives_check(move);
// Indicate PvNodes that will probably fail low if node was searched with non-PV search
// at depth equal or greater to current depth and result of this search was far below alpha
bool likelyFailLow = PvNode
&& ttMove
&& (tte->bound() & BOUND_UPPER)
&& ttValue < alpha + 200 + 100 * depth
&& tte->depth() >= depth;
// Calculate new depth for this move // Calculate new depth for this move
newDepth = depth - 1; newDepth = depth - 1;
@@ -1061,8 +1106,20 @@ moves_loop: // When in check, search starts from here
// Reduced depth of the next LMR search // Reduced depth of the next LMR search
int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0); int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0);
if ( !captureOrPromotion if ( captureOrPromotion
&& !givesCheck) || givesCheck)
{
// Capture history based pruning when the move doesn't give check
if ( !givesCheck
&& lmrDepth < 1
&& captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0)
continue;
// SEE based pruning
if (!pos.see_ge(move, Value(-218) * depth)) // (~25 Elo)
continue;
}
else
{ {
// Countermoves based pruning (~20 Elo) // Countermoves based pruning (~20 Elo)
if ( lmrDepth < 4 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1) if ( lmrDepth < 4 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1)
@@ -1073,36 +1130,15 @@ moves_loop: // When in check, search starts from here
// Futility pruning: parent node (~5 Elo) // Futility pruning: parent node (~5 Elo)
if ( lmrDepth < 7 if ( lmrDepth < 7
&& !ss->inCheck && !ss->inCheck
&& ss->staticEval + 283 + 170 * lmrDepth <= alpha && ss->staticEval + 174 + 157 * lmrDepth <= alpha
&& (*contHist[0])[movedPiece][to_sq(move)] && (*contHist[0])[movedPiece][to_sq(move)]
+ (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)]
+ (*contHist[3])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)]
+ (*contHist[5])[movedPiece][to_sq(move)] / 2 < 27376) + (*contHist[5])[movedPiece][to_sq(move)] / 3 < 28255)
continue; continue;
// Prune moves with negative SEE (~20 Elo) // Prune moves with negative SEE (~20 Elo)
if (!pos.see_ge(move, Value(-(29 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) if (!pos.see_ge(move, Value(-(30 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth)))
continue;
}
else
{
// Capture history based pruning when the move doesn't give check
if ( !givesCheck
&& lmrDepth < 1
&& captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0)
continue;
// Futility pruning for captures
if ( !givesCheck
&& lmrDepth < 6
&& !(PvNode && abs(bestValue) < 2)
&& !ss->inCheck
&& ss->staticEval + 169 + 244 * lmrDepth
+ PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha)
continue;
// See based pruning
if (!pos.see_ge(move, Value(-221) * depth)) // (~25 Elo)
continue; continue;
} }
} }
@@ -1158,7 +1194,7 @@ moves_loop: // When in check, search starts from here
// Check extension (~2 Elo) // Check extension (~2 Elo)
else if ( givesCheck else if ( givesCheck
&& (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move))) && (pos.is_discovered_check_on_king(~us, move) || pos.see_ge(move)))
extension = 1; extension = 1;
// Last captures extension // Last captures extension
@@ -1166,12 +1202,6 @@ moves_loop: // When in check, search starts from here
&& pos.non_pawn_material() <= 2 * RookValueMg) && pos.non_pawn_material() <= 2 * RookValueMg)
extension = 1; extension = 1;
// Late irreversible move extension
if ( move == ttMove
&& pos.rule50_count() > 80
&& (captureOrPromotion || type_of(movedPiece) == PAWN))
extension = 2;
// Add extension to new depth // Add extension to new depth
newDepth += extension; newDepth += extension;
@@ -1188,30 +1218,41 @@ moves_loop: // When in check, search starts from here
// Step 15. Make the move // Step 15. Make the move
pos.do_move(move, st, givesCheck); pos.do_move(move, st, givesCheck);
// Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be (ss+1)->distanceFromPv = ss->distanceFromPv + moveCount - 1;
// re-searched at full depth.
// Step 16. Late moves reduction / extension (LMR, ~200 Elo)
// We use various heuristics for the sons of a node after the first son has
// been searched. In general we would like to reduce them, but there are many
// cases where we extend a son if it has good chances to be "interesting".
if ( depth >= 3 if ( depth >= 3
&& moveCount > 1 + 2 * rootNode + 2 * (PvNode && abs(bestValue) < 2) && moveCount > 1 + 2 * rootNode
&& ( !captureOrPromotion && ( !captureOrPromotion
|| moveCountPruning || moveCountPruning
|| ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha
|| cutNode || cutNode
|| thisThread->ttHitAverage < 427 * TtHitAverageResolution * TtHitAverageWindow / 1024)) || (!PvNode && !formerPv && captureHistory[movedPiece][to_sq(move)][type_of(pos.captured_piece())] < 3678)
|| thisThread->ttHitAverage < 432 * TtHitAverageResolution * TtHitAverageWindow / 1024))
{ {
Depth r = reduction(improving, depth, moveCount); Depth r = reduction(improving, depth, moveCount);
// Decrease reduction if the ttHit running average is large // Decrease reduction if the ttHit running average is large
if (thisThread->ttHitAverage > 509 * TtHitAverageResolution * TtHitAverageWindow / 1024) if (thisThread->ttHitAverage > 537 * TtHitAverageResolution * TtHitAverageWindow / 1024)
r--; r--;
// Reduction if other threads are searching this position // Increase reduction if other threads are searching this position
if (th.marked()) if (th.marked())
r++; r++;
// Decrease reduction if position is or has been on the PV (~10 Elo) // Decrease reduction if position is or has been on the PV
if (ss->ttPv) // and node is not likely to fail low. (~10 Elo)
if (ss->ttPv && !likelyFailLow)
r -= 2; r -= 2;
// Increase reduction at root and non-PV nodes when the best move does not change frequently
if ((rootNode || !PvNode) && thisThread->rootDepth > 10 && thisThread->bestMoveChanges <= 2)
r++;
// More reductions for late moves if position was not in previous PV
if (moveCountPruning && !formerPv) if (moveCountPruning && !formerPv)
r++; r++;
@@ -1223,12 +1264,22 @@ moves_loop: // When in check, search starts from here
if (singularQuietLMR) if (singularQuietLMR)
r--; r--;
if (!captureOrPromotion) if (captureOrPromotion)
{
// Unless giving check, this capture is likely bad
if ( !givesCheck
&& ss->staticEval + PieceValue[EG][pos.captured_piece()] + 210 * depth <= alpha)
r++;
}
else
{ {
// Increase reduction if ttMove is a capture (~5 Elo) // Increase reduction if ttMove is a capture (~5 Elo)
if (ttCapture) if (ttCapture)
r++; r++;
// Increase reduction at root if failing high
r += rootNode ? thisThread->failedHighCnt * thisThread->failedHighCnt * moveCount / 512 : 0;
// Increase reduction for cut nodes (~10 Elo) // Increase reduction for cut nodes (~10 Elo)
if (cutNode) if (cutNode)
r += 2; r += 2;
@@ -1244,42 +1295,39 @@ moves_loop: // When in check, search starts from here
+ (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[0])[movedPiece][to_sq(move)]
+ (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)]
+ (*contHist[3])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)]
- 5287; - 4741;
// Decrease/increase reduction by comparing opponent's stat score (~10 Elo) // Decrease/increase reduction by comparing opponent's stat score (~10 Elo)
if (ss->statScore >= -106 && (ss-1)->statScore < -104) if (ss->statScore >= -89 && (ss-1)->statScore < -116)
r--; r--;
else if ((ss-1)->statScore >= -119 && ss->statScore < -140) else if ((ss-1)->statScore >= -112 && ss->statScore < -100)
r++; r++;
// Decrease/increase reduction for moves with a good/bad history (~30 Elo) // Decrease/increase reduction for moves with a good/bad history (~30 Elo)
r -= ss->statScore / 14884; // If we are not in check use statScore, but if we are in check we use
} // the sum of main history and first continuation history with an offset.
else if (ss->inCheck)
{ r -= (thisThread->mainHistory[us][from_to(move)]
// Increase reduction for captures/promotions if late move and at low depth + (*contHist[0])[movedPiece][to_sq(move)] - 3833) / 16384;
if (depth < 8 && moveCount > 2) else
r++; r -= ss->statScore / 14790;
// Unless giving check, this capture is likely bad
if ( !givesCheck
&& ss->staticEval + PieceValue[EG][pos.captured_piece()] + 213 * depth <= alpha)
r++;
} }
Depth d = std::clamp(newDepth - r, 1, newDepth); // In general we want to cap the LMR depth search at newDepth. But for nodes
// close to the principal variation the cap is at (newDepth + 1), which will
// allow these nodes to be searched deeper than the pv (up to 4 plies deeper).
Depth d = std::clamp(newDepth - r, 1, newDepth + ((ss+1)->distanceFromPv <= 4));
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true); value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true);
doFullDepthSearch = value > alpha && d != newDepth; // If the son is reduced and fails high it will be re-searched at full depth
doFullDepthSearch = value > alpha && d < newDepth;
didLMR = true; didLMR = true;
} }
else else
{ {
doFullDepthSearch = !PvNode || moveCount > 1; doFullDepthSearch = !PvNode || moveCount > 1;
didLMR = false; didLMR = false;
} }
@@ -1288,14 +1336,12 @@ moves_loop: // When in check, search starts from here
{ {
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode);
// If the move passed LMR update its stats
if (didLMR && !captureOrPromotion) if (didLMR && !captureOrPromotion)
{ {
int bonus = value > alpha ? stat_bonus(newDepth) int bonus = value > alpha ? stat_bonus(newDepth)
: -stat_bonus(newDepth); : -stat_bonus(newDepth);
if (move == ss->killers[0])
bonus += bonus / 4;
update_continuation_histories(ss, movedPiece, to_sq(move), bonus); update_continuation_histories(ss, movedPiece, to_sq(move), bonus);
} }
} }
@@ -1308,7 +1354,8 @@ moves_loop: // When in check, search starts from here
(ss+1)->pv = pv; (ss+1)->pv = pv;
(ss+1)->pv[0] = MOVE_NONE; (ss+1)->pv[0] = MOVE_NONE;
value = -search<PV>(pos, ss+1, -beta, -alpha, newDepth, false); value = -search<PV>(pos, ss+1, -beta, -alpha,
std::min(maxNextDepth, newDepth), false);
} }
// Step 18. Undo move // Step 18. Undo move
@@ -1341,8 +1388,7 @@ moves_loop: // When in check, search starts from here
rm.pv.push_back(*m); rm.pv.push_back(*m);
// We record how often the best move has been changed in each // We record how often the best move has been changed in each
// iteration. This information is used for time management: when // iteration. This information is used for time management and LMR
// the best move changes frequently, we allocate some more time.
if (moveCount > 1) if (moveCount > 1)
++thisThread->bestMoveChanges; ++thisThread->bestMoveChanges;
} }
@@ -1375,6 +1421,7 @@ moves_loop: // When in check, search starts from here
} }
} }
// If the move is worse than some previously searched move, remember it to update its stats later
if (move != bestMove) if (move != bestMove)
{ {
if (captureOrPromotion && captureCount < 32) if (captureOrPromotion && captureCount < 32)
@@ -1404,6 +1451,7 @@ moves_loop: // When in check, search starts from here
bestValue = excludedMove ? alpha bestValue = excludedMove ? alpha
: ss->inCheck ? mated_in(ss->ply) : VALUE_DRAW; : ss->inCheck ? mated_in(ss->ply) : VALUE_DRAW;
// If there is a move which produces search value greater than alpha we update stats of searched moves
else if (bestMove) else if (bestMove)
update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq, update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq,
quietsSearched, quietCount, capturesSearched, captureCount, depth); quietsSearched, quietCount, capturesSearched, captureCount, depth);
@@ -1425,6 +1473,7 @@ moves_loop: // When in check, search starts from here
else if (depth > 3) else if (depth > 3)
ss->ttPv = ss->ttPv && (ss+1)->ttPv; ss->ttPv = ss->ttPv && (ss+1)->ttPv;
// Write gathered information in transposition table
if (!excludedMove && !(rootNode && thisThread->pvIdx)) if (!excludedMove && !(rootNode && thisThread->pvIdx))
Cluster::save(thisThread, tte, Cluster::save(thisThread, tte,
posKey, value_to_tt(bestValue, ss->ply), ss->ttPv, posKey, value_to_tt(bestValue, ss->ply), ss->ttPv,
@@ -1451,6 +1500,8 @@ moves_loop: // When in check, search starts from here
Move pv[MAX_PLY+1]; Move pv[MAX_PLY+1];
StateInfo st; StateInfo st;
ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize);
TTEntry* tte; TTEntry* tte;
Key posKey; Key posKey;
Move ttMove, move, bestMove; Move ttMove, move, bestMove;
@@ -1519,6 +1570,8 @@ moves_loop: // When in check, search starts from here
bestValue = ttValue; bestValue = ttValue;
} }
else else
// In case of null move search use previous static eval with a different sign
// and addition of two tempos
ss->staticEval = bestValue = ss->staticEval = bestValue =
(ss-1)->currentMove != MOVE_NULL ? evaluate(pos) (ss-1)->currentMove != MOVE_NULL ? evaluate(pos)
: -(ss-1)->staticEval + 2 * Tempo; : -(ss-1)->staticEval + 2 * Tempo;
@@ -1526,6 +1579,7 @@ moves_loop: // When in check, search starts from here
// Stand pat. Return immediately if static value is at least beta // Stand pat. Return immediately if static value is at least beta
if (bestValue >= beta) if (bestValue >= beta)
{ {
// Save gathered info in transposition table
if (!ss->ttHit) if (!ss->ttHit)
Cluster::save(thisThread, tte, Cluster::save(thisThread, tte,
posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER,
@@ -1537,7 +1591,7 @@ moves_loop: // When in check, search starts from here
if (PvNode && bestValue > alpha) if (PvNode && bestValue > alpha)
alpha = bestValue; alpha = bestValue;
futilityBase = bestValue + 145; futilityBase = bestValue + 155;
} }
const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory,
@@ -1563,15 +1617,13 @@ moves_loop: // When in check, search starts from here
moveCount++; moveCount++;
// Futility pruning // Futility pruning and moveCount pruning
if ( !ss->inCheck if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY
&& !givesCheck && !givesCheck
&& futilityBase > -VALUE_KNOWN_WIN && futilityBase > -VALUE_KNOWN_WIN
&& !pos.advanced_pawn_push(move)) && !pos.advanced_pawn_push(move))
{ {
assert(type_of(move) != ENPASSANT); // Due to !pos.advanced_pawn_push
// moveCount pruning
if (moveCount > 2) if (moveCount > 2)
continue; continue;
@@ -1591,8 +1643,7 @@ moves_loop: // When in check, search starts from here
} }
// Do not search moves with negative SEE values // Do not search moves with negative SEE values
if ( !ss->inCheck if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY
&& !(givesCheck && pos.is_discovery_check_on_king(~pos.side_to_move(), move))
&& !pos.see_ge(move)) && !pos.see_ge(move))
continue; continue;
@@ -1612,8 +1663,9 @@ moves_loop: // When in check, search starts from here
[pos.moved_piece(move)] [pos.moved_piece(move)]
[to_sq(move)]; [to_sq(move)];
// CounterMove based pruning
if ( !captureOrPromotion if ( !captureOrPromotion
&& moveCount && bestValue > VALUE_TB_LOSS_IN_MAX_PLY
&& (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold
&& (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold) && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold)
continue; continue;
@@ -1648,8 +1700,13 @@ moves_loop: // When in check, search starts from here
// All legal moves have been searched. A special case: if we're in check // All legal moves have been searched. A special case: if we're in check
// and no legal moves were found, it is checkmate. // and no legal moves were found, it is checkmate.
if (ss->inCheck && bestValue == -VALUE_INFINITE) if (ss->inCheck && bestValue == -VALUE_INFINITE)
return mated_in(ss->ply); // Plies to mate from the root {
assert(!MoveList<LEGAL>(pos).size());
return mated_in(ss->ply); // Plies to mate from the root
}
// Save gathered info in transposition table
Cluster::save(thisThread, tte, Cluster::save(thisThread, tte,
posKey, value_to_tt(bestValue, ss->ply), pvHit, posKey, value_to_tt(bestValue, ss->ply), pvHit,
bestValue >= beta ? BOUND_LOWER : bestValue >= beta ? BOUND_LOWER :
@@ -1729,14 +1786,15 @@ moves_loop: // When in check, search starts from here
PieceType captured = type_of(pos.piece_on(to_sq(bestMove))); PieceType captured = type_of(pos.piece_on(to_sq(bestMove)));
bonus1 = stat_bonus(depth + 1); bonus1 = stat_bonus(depth + 1);
bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus
: stat_bonus(depth); // smaller bonus : std::min(bonus1, stat_bonus(depth)); // smaller bonus
if (!pos.capture_or_promotion(bestMove)) if (!pos.capture_or_promotion(bestMove))
{ {
// Increase stats for the best move in case it was a quiet move
update_quiet_stats(pos, ss, bestMove, bonus2, depth); update_quiet_stats(pos, ss, bestMove, bonus2, depth);
// Decrease all the non-best quiet moves // Decrease stats for all non-best quiet moves
for (int i = 0; i < quietCount; ++i) for (int i = 0; i < quietCount; ++i)
{ {
thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bonus2; thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bonus2;
@@ -1744,14 +1802,16 @@ moves_loop: // When in check, search starts from here
} }
} }
else else
// Increase stats for the best move in case it was a capture move
captureHistory[moved_piece][to_sq(bestMove)][captured] << bonus1; captureHistory[moved_piece][to_sq(bestMove)][captured] << bonus1;
// Extra penalty for a quiet early move that was not a TT move or main killer move in previous ply when it gets refuted // Extra penalty for a quiet early move that was not a TT move or
// main killer move in previous ply when it gets refuted.
if ( ((ss-1)->moveCount == 1 + (ss-1)->ttHit || ((ss-1)->currentMove == (ss-1)->killers[0])) if ( ((ss-1)->moveCount == 1 + (ss-1)->ttHit || ((ss-1)->currentMove == (ss-1)->killers[0]))
&& !pos.captured_piece()) && !pos.captured_piece())
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -bonus1); update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -bonus1);
// Decrease all the non-best capture moves // Decrease stats for all non-best capture moves
for (int i = 0; i < captureCount; ++i) for (int i = 0; i < captureCount; ++i)
{ {
moved_piece = pos.moved_piece(capturesSearched[i]); moved_piece = pos.moved_piece(capturesSearched[i]);
@@ -1768,6 +1828,7 @@ moves_loop: // When in check, search starts from here
for (int i : {1, 2, 4, 6}) for (int i : {1, 2, 4, 6})
{ {
// Only update first 2 continuation histories if we are in check
if (ss->inCheck && i > 2) if (ss->inCheck && i > 2)
break; break;
if (is_ok((ss-i)->currentMove)) if (is_ok((ss-i)->currentMove))
@@ -1780,6 +1841,7 @@ moves_loop: // When in check, search starts from here
void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus, int depth) { void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus, int depth) {
// Update killers
if (ss->killers[0] != move) if (ss->killers[0] != move)
{ {
ss->killers[1] = ss->killers[0]; ss->killers[1] = ss->killers[0];
@@ -1791,15 +1853,18 @@ moves_loop: // When in check, search starts from here
thisThread->mainHistory[us][from_to(move)] << bonus; thisThread->mainHistory[us][from_to(move)] << bonus;
update_continuation_histories(ss, pos.moved_piece(move), to_sq(move), bonus); update_continuation_histories(ss, pos.moved_piece(move), to_sq(move), bonus);
// Penalty for reversed move in case of moved piece not being a pawn
if (type_of(pos.moved_piece(move)) != PAWN) if (type_of(pos.moved_piece(move)) != PAWN)
thisThread->mainHistory[us][from_to(reverse_move(move))] << -bonus; thisThread->mainHistory[us][from_to(reverse_move(move))] << -bonus;
// Update countermove history
if (is_ok((ss-1)->currentMove)) if (is_ok((ss-1)->currentMove))
{ {
Square prevSq = to_sq((ss-1)->currentMove); Square prevSq = to_sq((ss-1)->currentMove);
thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move; thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move;
} }
// Update low ply history
if (depth > 11 && ss->ply < MAX_LPH) if (depth > 11 && ss->ply < MAX_LPH)
thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 7); thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 7);
} }
@@ -1946,6 +2011,8 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) {
bool RootMove::extract_ponder_from_tt(Position& pos) { bool RootMove::extract_ponder_from_tt(Position& pos) {
StateInfo st; StateInfo st;
ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize);
bool ttHit; bool ttHit;
assert(pv.size() == 1); assert(pv.size() == 1);
@@ -2013,3 +2080,5 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) {
m.tbRank = 0; m.tbRank = 0;
} }
} }
} // namespace Stockfish
+6 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,6 +26,8 @@
#include "types.h" #include "types.h"
#include "cluster.h" #include "cluster.h"
namespace Stockfish {
class Position; class Position;
namespace Search { namespace Search {
@@ -48,6 +50,7 @@ struct Stack {
Value staticEval; Value staticEval;
int statScore; int statScore;
int moveCount; int moveCount;
int distanceFromPv;
bool inCheck; bool inCheck;
bool ttPv; bool ttPv;
bool ttHit; bool ttHit;
@@ -72,7 +75,6 @@ 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;
}; };
@@ -108,4 +110,6 @@ void clear();
} // namespace Search } // namespace Search
} // namespace Stockfish
#endif // #ifndef SEARCH_H_INCLUDED #endif // #ifndef SEARCH_H_INCLUDED
+10 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,9 +51,11 @@
#include <windows.h> #include <windows.h>
#endif #endif
using namespace Tablebases; using namespace Stockfish::Tablebases;
int Tablebases::MaxCardinality; int Stockfish::Tablebases::MaxCardinality;
namespace Stockfish {
namespace { namespace {
@@ -1001,7 +1003,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 http://www.eecs.harvard.edu/~michaelm/E210/huffman.pdf // See https://en.wikipedia.org/wiki/Huffman_coding
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;
@@ -1142,7 +1144,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::unique_lock<std::mutex> lk(mutex); std::scoped_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;
@@ -1442,7 +1444,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-movecounter <= 100. // dtz + 50-move-counter <= 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.
@@ -1612,3 +1614,5 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) {
return true; return true;
} }
} // namespace Stockfish
+3 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,7 +23,7 @@
#include "../search.h" #include "../search.h"
namespace Tablebases { namespace Stockfish::Tablebases {
enum WDLScore { enum WDLScore {
WDLLoss = -2, // Loss WDLLoss = -2, // Loss
@@ -73,6 +73,6 @@ inline std::ostream& operator<<(std::ostream& os, const ProbeState v) {
return os; return os;
} }
} } // namespace Stockfish::Tablebases
#endif #endif
+15 -11
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,6 +26,8 @@
#include "syzygy/tbprobe.h" #include "syzygy/tbprobe.h"
#include "tt.h" #include "tt.h"
namespace Stockfish {
ThreadPool Threads; // Global object ThreadPool Threads; // Global object
@@ -229,16 +231,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;
@@ -263,3 +265,5 @@ void ThreadPool::wait_for_search_finished() const {
if (th != front()) if (th != front())
th->wait_for_search_finished(); th->wait_for_search_finished();
} }
} // namespace Stockfish
+5 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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
@@ -33,6 +33,7 @@
#include "search.h" #include "search.h"
#include "thread_win32_osx.h" #include "thread_win32_osx.h"
namespace Stockfish {
/// Thread class keeps together all the thread-related stuff. We use /// Thread class keeps together all the thread-related stuff. We use
/// per-thread pawn and material hash tables so that once we get a /// per-thread pawn and material hash tables so that once we get a
@@ -74,7 +75,7 @@ public:
CapturePieceToHistory captureHistory; CapturePieceToHistory captureHistory;
ContinuationHistory continuationHistory[2][2]; ContinuationHistory continuationHistory[2][2];
Score contempt; Score contempt;
int failedHighCnt;
#ifdef USE_MPI #ifdef USE_MPI
struct { struct {
std::mutex mutex; std::mutex mutex;
@@ -136,4 +137,6 @@ private:
extern ThreadPool Threads; extern ThreadPool Threads;
} // namespace Stockfish
#endif // #ifndef THREAD_H_INCLUDED #endif // #ifndef THREAD_H_INCLUDED
+9 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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
@@ -31,6 +31,8 @@
#include <pthread.h> #include <pthread.h>
namespace Stockfish {
static const size_t TH_STACK_SIZE = 8 * 1024 * 1024; static const size_t TH_STACK_SIZE = 8 * 1024 * 1024;
template <class T, class P = std::pair<T*, void(T::*)()>> template <class T, class P = std::pair<T*, void(T::*)()>>
@@ -57,10 +59,16 @@ public:
void join() { pthread_join(thread, NULL); } void join() { pthread_join(thread, NULL); }
}; };
} // namespace Stockfish
#else // Default case: use STL classes #else // Default case: use STL classes
namespace Stockfish {
typedef std::thread NativeThread; typedef std::thread NativeThread;
} // namespace Stockfish
#endif #endif
#endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED #endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED
+6 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,6 +24,8 @@
#include "timeman.h" #include "timeman.h"
#include "uci.h" #include "uci.h"
namespace Stockfish {
TimeManagement Time; // Our global time management object TimeManagement Time; // Our global time management object
@@ -75,7 +77,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
// game time for the current move, so also cap to 20% of available game time. // game time for the current move, so also cap to 20% of available game time.
if (limits.movestogo == 0) if (limits.movestogo == 0)
{ {
optScale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0, optScale = std::min(0.0084 + std::pow(ply + 3.0, 0.5) * 0.0042,
0.2 * limits.time[us] / double(timeLeft)); 0.2 * limits.time[us] / double(timeLeft));
maxScale = std::min(7.0, 4.0 + ply / 12.0); maxScale = std::min(7.0, 4.0 + ply / 12.0);
} }
@@ -95,3 +97,5 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
if (Options["Ponder"]) if (Options["Ponder"])
optimumTime += optimumTime / 4; optimumTime += optimumTime / 4;
} }
} // namespace Stockfish
+5 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,6 +23,8 @@
#include "search.h" #include "search.h"
#include "cluster.h" #include "cluster.h"
namespace Stockfish {
/// The TimeManagement class computes the optimal time to think depending on /// The TimeManagement class computes the optimal time to think depending on
/// the maximum available time, the game move number and other parameters. /// the maximum available time, the game move number and other parameters.
@@ -44,4 +46,6 @@ private:
extern TimeManagement Time; extern TimeManagement Time;
} // namespace Stockfish
#endif // #ifndef TIMEMAN_H_INCLUDED #endif // #ifndef TIMEMAN_H_INCLUDED
+17 -11
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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,6 +26,8 @@
#include "tt.h" #include "tt.h"
#include "uci.h" #include "uci.h"
namespace Stockfish {
TranspositionTable TT; // Our global transposition table TranspositionTable TT; // Our global transposition table
/// TTEntry::save() populates the TTEntry with a new node's data, possibly /// TTEntry::save() populates the TTEntry with a new node's data, possibly
@@ -62,11 +64,12 @@ void TranspositionTable::resize(size_t mbSize) {
Threads.main()->wait_for_search_finished(); Threads.main()->wait_for_search_finished();
aligned_ttmem_free(mem); aligned_large_pages_free(table);
clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster);
table = static_cast<Cluster*>(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), mem));
if (!mem) table = static_cast<Cluster*>(aligned_large_pages_alloc(clusterCount * sizeof(Cluster)));
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;
@@ -122,7 +125,7 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
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 == key16 || !tte[i].depth8)
{ {
tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & 0x7)); // Refresh tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & (GENERATION_DELTA - 1))); // Refresh
return found = (bool)tte[i].depth8, &tte[i]; return found = (bool)tte[i].depth8, &tte[i];
} }
@@ -131,11 +134,12 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
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 263 (256 is the modulus plus 7 to keep the unrelated // nature we add GENERATION_CYCLE (256 is the modulus, plus what
// lowest three bits from affecting the result) to calculate the entry // is needed to keep the unrelated lowest n bits from affecting
// age correctly even after generation8 overflows into the next cycle. // the result) to calculate the entry age correctly even after
if ( replace->depth8 - ((263 + generation8 - replace->genBound8) & 0xF8) // generation8 overflows into the next cycle.
> tte[i].depth8 - ((263 + generation8 - tte[i].genBound8) & 0xF8)) if ( replace->depth8 - ((GENERATION_CYCLE + generation8 - replace->genBound8) & GENERATION_MASK)
> 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;
@@ -150,7 +154,9 @@ 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 & 0xF8) == generation8; cnt += table[i].entry[j].depth8 && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8;
return cnt / ClusterSize; return cnt / ClusterSize;
} }
} // namespace Stockfish
+14 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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
@@ -22,10 +22,12 @@
#include "misc.h" #include "misc.h"
#include "types.h" #include "types.h"
namespace Stockfish {
//void Cluster::init();
namespace Cluster { namespace Cluster {
void init(); void init();
} }
//void Cluster::init();
/// TTEntry struct is the 10 bytes transposition table entry, defined as below: /// TTEntry struct is the 10 bytes transposition table entry, defined as below:
/// ///
@@ -80,9 +82,15 @@ 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_ttmem_free(mem); } ~TranspositionTable() { aligned_large_pages_free(table); }
void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound void new_search() { generation8 += GENERATION_DELTA; } // Lower bits are used for other things
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);
@@ -97,10 +105,11 @@ 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
}; };
extern TranspositionTable TT; extern TranspositionTable TT;
} // namespace Stockfish
#endif // #ifndef TT_H_INCLUDED #endif // #ifndef TT_H_INCLUDED
+9 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,6 +26,8 @@
using std::string; using std::string;
namespace Stockfish {
bool Tune::update_on_last; bool Tune::update_on_last;
const UCI::Option* LastOption = nullptr; const UCI::Option* LastOption = nullptr;
BoolConditions Conditions; BoolConditions Conditions;
@@ -126,6 +128,8 @@ void BoolConditions::set() {
sync_cout << binary[i] << sync_endl; sync_cout << binary[i] << sync_endl;
} }
} // namespace Stockfish
// Init options with tuning session results instead of default values. Useful to // Init options with tuning session results instead of default values. Useful to
// get correct bench signature after a tuning session or to test tuned values. // get correct bench signature after a tuning session or to test tuned values.
@@ -138,7 +142,11 @@ void BoolConditions::set() {
#include <cmath> #include <cmath>
namespace Stockfish {
void Tune::read_results() { void Tune::read_results() {
/* ...insert your values here... */ /* ...insert your values here... */
} }
} // namespace Stockfish
+8 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,6 +24,8 @@
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
namespace Stockfish {
typedef std::pair<int, int> Range; // Option's min-max values typedef std::pair<int, int> Range; // Option's min-max values
typedef Range (RangeFun) (int); typedef Range (RangeFun) (int);
@@ -130,9 +132,9 @@ class Tune {
SetRange range; SetRange range;
}; };
// Our facilty to fill the container, each Entry corresponds to a parameter to tune. // Our facility to fill the container, each Entry corresponds to a parameter
// We use variadic templates to deal with an unspecified number of entries, each one // to tune. We use variadic templates to deal with an unspecified number of
// of a possible different type. // entries, each one 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; }
@@ -190,4 +192,6 @@ public:
#define TUNE_CONDITIONS() int UNIQUE(c, __LINE__) = (Conditions.init(__COUNTER__), 0); \ #define TUNE_CONDITIONS() int UNIQUE(c, __LINE__) = (Conditions.init(__COUNTER__), 0); \
TUNE(Conditions, set_conditions) TUNE(Conditions, set_conditions)
} // namespace Stockfish
#endif // #ifndef TUNE_H_INCLUDED #endif // #ifndef TUNE_H_INCLUDED
+15 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,6 +57,12 @@
/// _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
@@ -77,6 +83,8 @@
# define pext(b, m) 0 # define pext(b, m) 0
#endif #endif
namespace Stockfish {
#ifdef USE_POPCNT #ifdef USE_POPCNT
constexpr bool HasPopCnt = true; constexpr bool HasPopCnt = true;
#else #else
@@ -107,7 +115,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
@@ -121,7 +129,7 @@ enum Move : int {
enum MoveType { enum MoveType {
NORMAL, NORMAL,
PROMOTION = 1 << 14, PROMOTION = 1 << 14,
ENPASSANT = 2 << 14, EN_PASSANT = 2 << 14,
CASTLING = 3 << 14 CASTLING = 3 << 14
}; };
@@ -196,8 +204,8 @@ enum PieceType {
enum Piece { enum Piece {
NO_PIECE, NO_PIECE,
W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, W_PAWN = PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING, B_PAWN = PAWN + 8, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
PIECE_NB = 16 PIECE_NB = 16
}; };
@@ -476,6 +484,8 @@ constexpr Key make_key(uint64_t seed) {
return seed * 6364136223846793005ULL + 1442695040888963407ULL; return seed * 6364136223846793005ULL + 1442695040888963407ULL;
} }
} // namespace Stockfish
#endif // #ifndef TYPES_H_INCLUDED #endif // #ifndef TYPES_H_INCLUDED
#include "tune.h" // Global visibility to tuning setup #include "tune.h" // Global visibility to tuning setup
+7 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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
@@ -35,6 +35,8 @@
using namespace std; using namespace std;
namespace Stockfish {
extern vector<string> setup_bench(const Position&, istream&); extern vector<string> setup_bench(const Position&, istream&);
namespace { namespace {
@@ -86,7 +88,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::verify_NNUE(); Eval::NNUE::verify();
sync_cout << "\n" << Eval::trace(p) << sync_endl; sync_cout << "\n" << Eval::trace(p) << sync_endl;
} }
@@ -283,7 +285,7 @@ void UCI::loop(int argc, char* argv[]) {
trace_eval(pos); trace_eval(pos);
else if (token == "compiler" && Cluster::is_root()) else if (token == "compiler" && Cluster::is_root())
sync_cout << compiler_info() << sync_endl; sync_cout << compiler_info() << sync_endl;
else if (Cluster::is_root()) else if (!token.empty() && token[0] != '#' && Cluster::is_root())
sync_cout << "Unknown command: " << cmd << sync_endl; sync_cout << "Unknown command: " << cmd << sync_endl;
} while (token != "quit" && argc == 1); // Command line args are one-shot } while (token != "quit" && argc == 1); // Command line args are one-shot
@@ -377,3 +379,5 @@ Move UCI::to_move(const Position& pos, string& str) {
return MOVE_NONE; return MOVE_NONE;
} }
} // namespace Stockfish
+5 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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,6 +24,8 @@
#include "types.h" #include "types.h"
namespace Stockfish {
class Position; class Position;
namespace UCI { namespace UCI {
@@ -78,4 +80,6 @@ Move to_move(const Position& pos, std::string& str);
extern UCI::OptionsMap Options; extern UCI::OptionsMap Options;
} // namespace Stockfish
#endif // #ifndef UCI_H_INCLUDED #endif // #ifndef UCI_H_INCLUDED
+7 -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-2020 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2021 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
@@ -31,6 +31,8 @@
using std::string; using std::string;
namespace Stockfish {
UCI::OptionsMap Options; // Global object UCI::OptionsMap Options; // Global object
namespace UCI { namespace UCI {
@@ -41,8 +43,8 @@ void on_hash_size(const Option& o) { TT.resize(size_t(o)); }
void on_logger(const Option& o) { start_logger(o); } void on_logger(const Option& o) { start_logger(o); }
void on_threads(const Option& o) { Threads.set(size_t(o)); } void on_threads(const Option& o) { Threads.set(size_t(o)); }
void on_tb_path(const Option& o) { Tablebases::init(o); } void on_tb_path(const Option& o) { Tablebases::init(o); }
void on_use_NNUE(const Option& ) { Eval::init_NNUE(); } void on_use_NNUE(const Option& ) { Eval::NNUE::init(); }
void on_eval_file(const Option& ) { Eval::init_NNUE(); } void on_eval_file(const Option& ) { Eval::NNUE::init(); }
/// 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 {
@@ -190,3 +192,5 @@ Option& Option::operator=(const string& v) {
} }
} // namespace UCI } // namespace UCI
} // namespace Stockfish
+10 -10
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 --error-exitcode=42' exeprefix='valgrind --fair-sched=try --error-exitcode=42'
postfix='1>/dev/null' postfix='1>/dev/null'
threads="2" threads="2"
;; ;;
@@ -39,16 +39,16 @@ case $1 in
threads="2" threads="2"
cat << EOF > tsan.supp cat << EOF > tsan.supp
race:TTEntry::move race:Stockfish::TTEntry::move
race:TTEntry::depth race:Stockfish::TTEntry::depth
race:TTEntry::bound race:Stockfish::TTEntry::bound
race:TTEntry::save race:Stockfish::TTEntry::save
race:TTEntry::value race:Stockfish::TTEntry::value
race:TTEntry::eval race:Stockfish::TTEntry::eval
race:TTEntry::is_pv race:Stockfish::TTEntry::is_pv
race:TranspositionTable::probe race:Stockfish::TranspositionTable::probe
race:TranspositionTable::hashfull race:Stockfish::TranspositionTable::hashfull
EOF EOF