mirror of
https://github.com/opelly27/Stockfish.git
synced 2026-05-20 07:27:46 +00:00
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:
+12
@@ -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
|
||||||
|
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user