mirror of
https://github.com/sockspls/badfish
synced 2025-04-30 08:43:09 +00:00
Merge branch 'master' of github.com:official-stockfish/Stockfish
# Conflicts: # src/Makefile # src/position.cpp # src/position.h # src/search.cpp # src/types.h # src/uci.cpp
This commit is contained in:
commit
5c936572e9
44 changed files with 2512 additions and 1705 deletions
|
@ -1,5 +1,4 @@
|
|||
language: cpp
|
||||
sudo: required
|
||||
dist: xenial
|
||||
|
||||
matrix:
|
||||
|
@ -51,14 +50,11 @@ script:
|
|||
- echo "Reference bench:" $benchref
|
||||
#
|
||||
# Verify bench number against various builds
|
||||
- export CXXFLAGS=-Werror
|
||||
- export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG"
|
||||
- make clean && make -j2 ARCH=x86-64 optimize=no debug=yes build && ../tests/signature.sh $benchref
|
||||
- make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref
|
||||
- make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref
|
||||
|
||||
# Verify bench number is ONE_PLY independent by doubling its value
|
||||
- sed -i'.bak' 's/.*\(ONE_PLY = [0-9]*\),.*/\1 * 2,/g' types.h
|
||||
- make clean && make -j2 ARCH=x86-64 build && ../tests/signature.sh $benchref
|
||||
#
|
||||
# Check perft and reproducible search
|
||||
- ../tests/perft.sh
|
||||
|
|
68
AUTHORS
68
AUTHORS
|
@ -1,4 +1,4 @@
|
|||
# List of authors for Stockfish, updated for version 10
|
||||
# List of authors for Stockfish, as of March 30, 2020
|
||||
|
||||
Tord Romstad (romstad)
|
||||
Marco Costalba (mcostalba)
|
||||
|
@ -9,8 +9,9 @@ Aditya (absimaldata)
|
|||
Adrian Petrescu (apetresc)
|
||||
Ajith Chandy Jose (ajithcj)
|
||||
Alain Savard (Rocky640)
|
||||
alayan-stk-2
|
||||
Alayan Feh (Alayan-stk-2)
|
||||
Alexander Kure
|
||||
Alexander Pagel (Lolligerhans)
|
||||
Ali AlZhrani (Cooffe)
|
||||
Andrew Grant (AndyGrant)
|
||||
Andrey Neporada (nepal)
|
||||
|
@ -21,27 +22,34 @@ Auguste Pop
|
|||
Balint Pfliegel
|
||||
Ben Koshy (BKSpurgeon)
|
||||
Bill Henry (VoyagerOne)
|
||||
Bojun Guo (noobpwnftw, Nooby)
|
||||
braich
|
||||
Bojun Guo (noobpwnftw)
|
||||
Brian Sheppard (SapphireBrand)
|
||||
Brian Sheppard (SapphireBrand, briansheppard-toast)
|
||||
Bryan Cross (crossbr)
|
||||
candirufish
|
||||
Chess13234
|
||||
Chris Cain (ceebo)
|
||||
Dan Schmidt
|
||||
Dan Schmidt (dfannius)
|
||||
Daniel Axtens (daxtens)
|
||||
Daniel Dugovic (ddugovic)
|
||||
Dariusz Orzechowski
|
||||
David Zar
|
||||
Daylen Yang (daylen)
|
||||
DiscanX
|
||||
Eelco de Groot
|
||||
double-beep
|
||||
Eduardo Cáceres (eduherminio)
|
||||
Eelco de Groot (KingDefender)
|
||||
Elvin Liu (solarlight2)
|
||||
erbsenzaehler
|
||||
Ernesto Gatti
|
||||
Linmiao Xu (linrock)
|
||||
Fabian Beuke (madnight)
|
||||
Fabian Fichter (ianfab)
|
||||
fanon
|
||||
Fauzi Akram Dabat (FauziAkram)
|
||||
Felix Wittmann
|
||||
gamander
|
||||
Gary Heckman (gheckman)
|
||||
gguliash
|
||||
Gian-Carlo Pascutto (gcp)
|
||||
Gontran Lemaire (gonlem)
|
||||
|
@ -59,16 +67,18 @@ Jacques B. (Timshel)
|
|||
Jan Ondruš (hxim)
|
||||
Jared Kish (Kurtbusch)
|
||||
Jarrod Torriero (DU-jdto)
|
||||
Jean Gauthier (OuaisBla)
|
||||
Jean-Francois Romang (jromang)
|
||||
Jekaa
|
||||
Jerry Donald Watson (jerrydonaldwatson)
|
||||
Jonathan Calovski (Mysseno)
|
||||
Jonathan D. (SFisGOD)
|
||||
Jonathan Dumale (SFisGOD)
|
||||
Joost VandeVondele (vondele)
|
||||
Jörg Oster (joergoster)
|
||||
Joseph Ellis (jhellis3)
|
||||
Joseph R. Prostko
|
||||
jundery
|
||||
Justin Blanchard
|
||||
Justin Blanchard (UncombedCoconut)
|
||||
Kelly Wilson
|
||||
Ken Takusagawa
|
||||
kinderchocolate
|
||||
|
@ -76,36 +86,46 @@ Kiran Panditrao (Krgp)
|
|||
Kojirion
|
||||
Leonardo Ljubičić (ICCF World Champion)
|
||||
Leonid Pechenik (lp--)
|
||||
Linus Arver
|
||||
Linus Arver (listx)
|
||||
loco-loco
|
||||
Lub van den Berg (ElbertoOne)
|
||||
Luca Brivio (lucabrivio)
|
||||
Lucas Braesch (lucasart)
|
||||
Lyudmil Antonov (lantonov)
|
||||
Matthew Lai (matthewlai)
|
||||
Matthew Sullivan
|
||||
Maciej Żenczykowski (zenczykowski)
|
||||
Malcolm Campbell (xoto10)
|
||||
Mark Tenzer (31m059)
|
||||
marotear
|
||||
Matthew Lai (matthewlai)
|
||||
Matthew Sullivan (Matt14916)
|
||||
Michael An (man)
|
||||
Michael Byrne (MichaelB7)
|
||||
Michael Stembera (mstembera)
|
||||
Michael Chaly (Vizvezdenec)
|
||||
Michael Stembera (mstembera)
|
||||
Michael Whiteley (protonspring)
|
||||
Michel Van den Bergh (vdbergh)
|
||||
Miguel Lahoz (miguel-l)
|
||||
Mikael Bäckman (mbootsector)
|
||||
Michael Whiteley (protonspring)
|
||||
Mira
|
||||
Miroslav Fontán (Hexik)
|
||||
Moez Jellouli (MJZ1977)
|
||||
Mohammed Li (tthsqe12)
|
||||
Nathan Rugg (nmrugg)
|
||||
Nick Pelling (nickpelling)
|
||||
Nicklas Persson (NicklasPersson)
|
||||
Niklas Fiekas (niklasf)
|
||||
Nikolay Kostov (NikolayIT)
|
||||
Ondrej Mosnáček (WOnder93)
|
||||
Oskar Werkelin Ahlin
|
||||
Pablo Vazquez
|
||||
Panthee
|
||||
Pascal Romaret
|
||||
Pasquale Pigazzini (ppigazzini)
|
||||
Patrick Jansen (mibere)
|
||||
pellanda
|
||||
Peter Zsifkovits (CoffeeOne)
|
||||
Praveen Kumar Tummala (praveentml)
|
||||
Rahul Dsilva (silversolver1)
|
||||
Ralph Stößer (Ralph Stoesser)
|
||||
Raminder Singh
|
||||
renouve
|
||||
|
@ -113,20 +133,32 @@ Reuven Peleg
|
|||
Richard Lloyd
|
||||
Rodrigo Exterckötter Tjäder
|
||||
Ron Britvich (Britvich)
|
||||
Ronald de Man (syzygy1)
|
||||
Ronald de Man (syzygy1, syzygy)
|
||||
Ryan Schmitt
|
||||
Ryan Takker
|
||||
Sami Kiminki (skiminki)
|
||||
Sebastian Buchwald (UniQP)
|
||||
Sergei Antonov (saproj)
|
||||
Sergei Ivanov (svivanov72)
|
||||
sf-x
|
||||
shane31
|
||||
Steinar Gunderson (sesse)
|
||||
Shane Booth (shane31)
|
||||
Stefan Geschwentner (locutus2)
|
||||
Stefano Cardanobile (Stefano80)
|
||||
Steinar Gunderson (sesse)
|
||||
Stéphane Nicolet (snicolet)
|
||||
Thanar2
|
||||
thaspel
|
||||
theo77186
|
||||
Tom Truscott
|
||||
Tom Vijlbrief (tomtor)
|
||||
Torsten Franz (torfranz)
|
||||
Tomasz Sobczyk (Sopel97)
|
||||
Torsten Franz (torfranz, tfranzer)
|
||||
Tracey Emery (basepr1me)
|
||||
Uri Blass (uriblass)
|
||||
Vince Negri
|
||||
Vince Negri (cuddlestmonkey)
|
||||
|
||||
|
||||
# Additionally, we acknowledge the authors and maintainers of fishtest,
|
||||
# an amazing and essential framework for the development of Stockfish!
|
||||
#
|
||||
# https://github.com/glinscott/fishtest/blob/master/AUTHORS
|
||||
|
|
79
Readme.md
79
Readme.md
|
@ -1,7 +1,7 @@
|
|||
## Overview
|
||||
|
||||
[](https://travis-ci.org/official-stockfish/Stockfish)
|
||||
[](https://ci.appveyor.com/project/mcostalba/stockfish)
|
||||
[](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master)
|
||||
|
||||
[Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine
|
||||
derived from Glaurung 2.1. It is not a complete chess program and requires a
|
||||
|
@ -34,15 +34,15 @@ Currently, Stockfish has the following UCI options:
|
|||
A positive value for contempt favors middle game positions and avoids draws.
|
||||
|
||||
* #### Analysis Contempt
|
||||
By default, contempt is set to prefer the side to move. Set this option to "White"
|
||||
By default, contempt is set to prefer the side to move. Set this option to "White"
|
||||
or "Black" to analyse with contempt for that side, or "Off" to disable contempt.
|
||||
|
||||
* #### Threads
|
||||
The number of CPU threads used for searching a position. For best performance, set
|
||||
The number of CPU threads used for searching a position. For best performance, set
|
||||
this equal to the number of CPU cores available.
|
||||
|
||||
* #### Hash
|
||||
The size of the hash table in MB.
|
||||
The size of the hash table in MB. It is recommended to set Hash after setting Threads.
|
||||
|
||||
* #### Clear Hash
|
||||
Clear the hash table.
|
||||
|
@ -55,21 +55,30 @@ Currently, Stockfish has the following UCI options:
|
|||
Leave at 1 for best performance.
|
||||
|
||||
* #### Skill Level
|
||||
Lower the Skill Level in order to make Stockfish play weaker.
|
||||
Lower the Skill Level in order to make Stockfish play weaker (see also UCI_LimitStrength).
|
||||
Internally, MultiPV is enabled, and with a certain probability depending on the Skill Level a
|
||||
weaker move will be played.
|
||||
|
||||
* #### UCI_LimitStrength
|
||||
Enable weaker play aiming for an Elo rating as set by UCI_Elo. This option overrides Skill Level.
|
||||
|
||||
* #### UCI_Elo
|
||||
If enabled by UCI_LimitStrength, aim for an engine strength of the given Elo.
|
||||
This Elo rating has been calibrated at a time control of 60s+0.6s and anchored to CCRL 40/4.
|
||||
|
||||
* #### Move Overhead
|
||||
Assume a time delay of x ms due to network and GUI overheads. This is useful to
|
||||
Assume a time delay of x ms due to network and GUI overheads. This is useful to
|
||||
avoid losses on time in those cases.
|
||||
|
||||
* #### Minimum Thinking Time
|
||||
Search for at least x ms per move.
|
||||
Search for at least x ms per move.
|
||||
|
||||
* #### Slow Mover
|
||||
Lower values will make Stockfish take less time in games, higher values will
|
||||
Lower values will make Stockfish take less time in games, higher values will
|
||||
make it think longer.
|
||||
|
||||
* #### nodestime
|
||||
Tells the engine to use nodes searched instead of wall time to account for
|
||||
Tells the engine to use nodes searched instead of wall time to account for
|
||||
elapsed time. Useful for engine testing.
|
||||
|
||||
* #### UCI_Chess960
|
||||
|
@ -79,13 +88,13 @@ Currently, Stockfish has the following UCI options:
|
|||
An option handled by your GUI.
|
||||
|
||||
* #### SyzygyPath
|
||||
Path to the folders/directories storing the Syzygy tablebase files. Multiple
|
||||
directories are to be separated by ";" on Windows and by ":" on Unix-based
|
||||
Path to the folders/directories storing the Syzygy tablebase files. Multiple
|
||||
directories are to be separated by ";" on Windows and by ":" on Unix-based
|
||||
operating systems. Do not use spaces around the ";" or ":".
|
||||
|
||||
|
||||
Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6`
|
||||
|
||||
It is recommended to store .rtbw files on an SSD. There is no loss in storing
|
||||
|
||||
It is recommended to store .rtbw files on an SSD. There is no loss in storing
|
||||
the .rtbz files on a regular HD. It is recommended to verify all md5 checksums
|
||||
of the downloaded tablebase files (`md5sum -c checksum.md5`) as corruption will
|
||||
lead to engine crashes.
|
||||
|
@ -129,6 +138,30 @@ more compact than Nalimov tablebases, while still storing all information
|
|||
needed for optimal play and in addition being able to take into account
|
||||
the 50-move rule.
|
||||
|
||||
## Large Pages
|
||||
|
||||
Stockfish supports large pages on Linux and Windows. Large pages make
|
||||
the hash access more efficient, improving the engine speed, especially
|
||||
on large hash sizes. Typical increases are 5..10% in terms of nps, but
|
||||
speed increases up to 30% have been measured. The support is
|
||||
automatic. Stockfish attempts to use large pages when available and
|
||||
will fall back to regular memory allocation when this is not the case.
|
||||
|
||||
### Support on Linux
|
||||
|
||||
Large page support on Linux is obtained by the Linux kernel
|
||||
transparent huge pages functionality. Typically, transparent huge pages
|
||||
are already enabled and no configuration is needed.
|
||||
|
||||
### Support on Windows
|
||||
|
||||
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)
|
||||
on how to enable this privilege. Logout/login may be needed
|
||||
afterwards. Due to memory fragmentation, it may not always be
|
||||
possible to allocate large pages even when enabled. A reboot
|
||||
might alleviate this problem. To determine whether large pages
|
||||
are in use, see the engine log.
|
||||
|
||||
## Compiling Stockfish yourself from the sources
|
||||
|
||||
|
@ -144,6 +177,14 @@ compile (for instance with Microsoft MSVC) you need to manually
|
|||
set/unset some switches in the compiler command line; see file *types.h*
|
||||
for a quick reference.
|
||||
|
||||
When reporting an issue or a bug, please tell us which version and
|
||||
compiler you used to create your executable. These informations can
|
||||
be found by typing the following commands in a console:
|
||||
|
||||
```
|
||||
./stockfish
|
||||
compiler
|
||||
```
|
||||
|
||||
## Understanding the code base and participating in the project
|
||||
|
||||
|
@ -153,12 +194,12 @@ community effort. There are a few ways to help contribute to its growth.
|
|||
### Donating hardware
|
||||
|
||||
Improving Stockfish requires a massive amount of testing. You can donate
|
||||
your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker)
|
||||
and view the current tests on [Fishtest](http://tests.stockfishchess.org/tests).
|
||||
your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker:-overview)
|
||||
and view the current tests on [Fishtest](https://tests.stockfishchess.org/tests).
|
||||
|
||||
### Improving the code
|
||||
|
||||
If you want to help improve the code, there are several valuable ressources:
|
||||
If you want to help improve the code, there are several valuable resources:
|
||||
|
||||
* [In this wiki,](https://www.chessprogramming.org) many techniques used in
|
||||
Stockfish are explained with a lot of background information.
|
||||
|
@ -169,8 +210,8 @@ generic rather than being focused on Stockfish's precise implementation.
|
|||
Nevertheless, a helpful resource.
|
||||
|
||||
* 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)
|
||||
group and engine testing is done on [Fishtest](http://tests.stockfishchess.org/tests).
|
||||
Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking)
|
||||
group and 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)
|
||||
first, where the basics of Stockfish development are explained.
|
||||
|
||||
|
|
|
@ -1,146 +1,154 @@
|
|||
Contributors with >10,000 CPU hours as of November 4, 2018
|
||||
Contributors with >10,000 CPU hours as of January 7, 2020
|
||||
Thank you!
|
||||
|
||||
Username CPU Hours Games played
|
||||
noobpwnftw 3730975 292309380
|
||||
mibere 535242 43333774
|
||||
crunchy 375564 29121434
|
||||
cw 371664 28748719
|
||||
fastgm 318178 22283584
|
||||
JojoM 295354 20958931
|
||||
dew 215476 17079219
|
||||
ctoks 214031 17312035
|
||||
glinscott 204517 13932027
|
||||
bking_US 187568 12233168
|
||||
velislav 168404 13336219
|
||||
CSU_Dynasty 168069 14417712
|
||||
Thanar 162373 13842179
|
||||
spams 149531 10940322
|
||||
Fisherman 141137 12099359
|
||||
drabel 134441 11180178
|
||||
leszek 133658 9812120
|
||||
marrco 133566 10115202
|
||||
sqrt2 128420 10022279
|
||||
vdbergh 123230 9200516
|
||||
tvijlbrief 123007 9498831
|
||||
vdv 120381 8555423
|
||||
malala 117291 8126488
|
||||
dsmith 114010 7622414
|
||||
BrunoBanani 104938 7448565
|
||||
CoffeeOne 100042 4593596
|
||||
Data 94621 8433010
|
||||
mgrabiak 92248 7787406
|
||||
bcross 89440 8506568
|
||||
brabos 81868 6647613
|
||||
BRAVONE 80811 5341681
|
||||
psk 77195 6156031
|
||||
nordlandia 74833 6231930
|
||||
robal 72818 5969856
|
||||
TueRens 72523 6383294
|
||||
sterni1971 71049 5647590
|
||||
sunu 65855 5360884
|
||||
mhoram 65034 5192880
|
||||
davar 64794 5457564
|
||||
nssy 64607 5371952
|
||||
Pking_cda 64499 5704075
|
||||
biffhero 63557 5480444
|
||||
teddybaer 62147 5585620
|
||||
solarlight 61278 5402642
|
||||
ElbertoOne 60156 5504304
|
||||
jromang 58854 4704502
|
||||
dv8silencer 57421 3961325
|
||||
tinker 56039 4204914
|
||||
Freja 50331 3808121
|
||||
renouve 50318 3544864
|
||||
robnjr 47504 4131742
|
||||
grandphish2 47377 4110003
|
||||
eva42 46857 4075716
|
||||
ttruscott 46802 3811534
|
||||
finfish 46244 3481661
|
||||
rap 46201 3219490
|
||||
ronaldjerum 45641 3964331
|
||||
xoto 44998 4170431
|
||||
gvreuls 44359 3902234
|
||||
bigpen0r 41780 3448224
|
||||
Bobo1239 40767 3657490
|
||||
Antihistamine 39218 2792761
|
||||
mhunt 38991 2697512
|
||||
racerschmacer 38929 3756111
|
||||
VoyagerOne 35896 3378887
|
||||
homyur 35561 3012398
|
||||
rkl 33217 2978536
|
||||
pb00067 33034 2803485
|
||||
speedycpu 32043 2531964
|
||||
SC 31954 2848432
|
||||
EthanOConnor 31638 2143255
|
||||
oryx 30962 2899534
|
||||
gri 30108 2429137
|
||||
csnodgrass 29396 2808611
|
||||
Garf 28887 2873564
|
||||
Pyafue 28885 1986098
|
||||
jkiiski 28014 1923255
|
||||
slakovv 27017 2031279
|
||||
Prcuvu 26300 2307154
|
||||
hyperbolic.tom 26248 2200777
|
||||
jbwiebe 25663 2129063
|
||||
anst 25525 2279159
|
||||
Patrick_G 24222 1835674
|
||||
nabildanial 23524 1586321
|
||||
achambord 23495 1942546
|
||||
Sharaf_DG 22975 1790697
|
||||
chriswk 22876 1947731
|
||||
ncfish1 22689 1830009
|
||||
cuistot 22201 1383031
|
||||
Zirie 21171 1493227
|
||||
Isidor 20634 1736219
|
||||
JanErik 20596 1791991
|
||||
xor12 20535 1819280
|
||||
team-oh 20364 1653708
|
||||
nesoneg 20264 1493435
|
||||
dex 20110 1682756
|
||||
rstoesser 19802 1335177
|
||||
Vizvezdenec 19750 1695579
|
||||
eastorwest 19531 1841839
|
||||
sg4032 18913 1720157
|
||||
horst.prack 18425 1708197
|
||||
cisco2015 18408 1793774
|
||||
ianh2105 18133 1668562
|
||||
MazeOfGalious 18022 1644593
|
||||
ville 17900 1539130
|
||||
j3corre 17607 975954
|
||||
eudhan 17502 1424648
|
||||
jmdana 17351 1287546
|
||||
iisiraider 17175 1118788
|
||||
jundery 17172 1115855
|
||||
wei 16852 1822582
|
||||
SFTUser 16635 1363975
|
||||
purplefishies 16621 1106850
|
||||
DragonLord 16599 1252348
|
||||
chris 15274 1575333
|
||||
IgorLeMasson 15201 1364148
|
||||
dju 15074 914278
|
||||
Flopzee 14700 1331632
|
||||
OssumOpossum 14149 1029265
|
||||
enedene 13762 935618
|
||||
ako027ako 13442 1250249
|
||||
AdrianSA 13324 924980
|
||||
bpfliegel 13318 886523
|
||||
Nikolay.IT 13260 1155612
|
||||
jpulman 12776 854815
|
||||
joster 12438 988413
|
||||
fatmurphy 12015 901134
|
||||
Nesa92 11711 1132245
|
||||
Adrian.Schmidt123 11542 898699
|
||||
modolief 11228 926456
|
||||
Dark_wizzie 11214 1017910
|
||||
mschmidt 10973 818594
|
||||
Andrew Grant 10780 947859
|
||||
infinity 10762 746397
|
||||
SapphireBrand 10692 1024604
|
||||
Thomas A. Anderson 10553 736094
|
||||
basepi 10434 935168
|
||||
lantonov 10325 972610
|
||||
pgontarz 10294 878746
|
||||
Spprtr 10189 823246
|
||||
crocogoat 10115 1017325
|
||||
stocky 10083 718114
|
||||
Username CPU Hours Games played
|
||||
--------------------------------------------------
|
||||
noobpwnftw 9305707 695548021
|
||||
mlang 780050 61648867
|
||||
dew 621626 43921547
|
||||
mibere 524702 42238645
|
||||
crunchy 354587 27344275
|
||||
cw 354495 27274181
|
||||
fastgm 332801 22804359
|
||||
JojoM 295750 20437451
|
||||
CSU_Dynasty 262015 21828122
|
||||
Fisherman 232181 18939229
|
||||
ctoks 218866 17622052
|
||||
glinscott 201989 13780820
|
||||
tvijlbrief 201204 15337115
|
||||
velislav 188630 14348485
|
||||
gvreuls 187164 15149976
|
||||
bking_US 180289 11876016
|
||||
nordlandia 172076 13467830
|
||||
leszek 157152 11443978
|
||||
Thanar 148021 12365359
|
||||
spams 141975 10319326
|
||||
drabel 138073 11121749
|
||||
vdv 137850 9394330
|
||||
mgrabiak 133578 10454324
|
||||
TueRens 132485 10878471
|
||||
bcross 129683 11557084
|
||||
marrco 126078 9356740
|
||||
sqrt2 125830 9724586
|
||||
robal 122873 9593418
|
||||
vdbergh 120766 8926915
|
||||
malala 115926 8002293
|
||||
CoffeeOne 114241 5004100
|
||||
dsmith 113189 7570238
|
||||
BrunoBanani 104644 7436849
|
||||
Data 92328 8220352
|
||||
mhoram 89333 6695109
|
||||
davar 87924 7009424
|
||||
xoto 81094 6869316
|
||||
ElbertoOne 80899 7023771
|
||||
grandphish2 78067 6160199
|
||||
brabos 77212 6186135
|
||||
psk 75733 5984901
|
||||
BRAVONE 73875 5054681
|
||||
sunu 70771 5597972
|
||||
sterni1971 70605 5590573
|
||||
MaZePallas 66886 5188978
|
||||
Vizvezdenec 63708 4967313
|
||||
nssy 63462 5259388
|
||||
jromang 61634 4940891
|
||||
teddybaer 61231 5407666
|
||||
Pking_cda 60099 5293873
|
||||
solarlight 57469 5028306
|
||||
dv8silencer 56913 3883992
|
||||
tinker 54936 4086118
|
||||
renouve 49732 3501516
|
||||
Freja 49543 3733019
|
||||
robnjr 46972 4053117
|
||||
rap 46563 3219146
|
||||
Bobo1239 46036 3817196
|
||||
ttruscott 45304 3649765
|
||||
racerschmacer 44881 3975413
|
||||
finfish 44764 3370515
|
||||
eva42 41783 3599691
|
||||
biffhero 40263 3111352
|
||||
bigpen0r 39817 3291647
|
||||
mhunt 38871 2691355
|
||||
ronaldjerum 38820 3240695
|
||||
Antihistamine 38785 2761312
|
||||
pb00067 38038 3086320
|
||||
speedycpu 37591 3003273
|
||||
rkl 37207 3289580
|
||||
VoyagerOne 37050 3441673
|
||||
jbwiebe 35320 2805433
|
||||
cuistot 34191 2146279
|
||||
homyur 33927 2850481
|
||||
manap 32873 2327384
|
||||
gri 32538 2515779
|
||||
oryx 31267 2899051
|
||||
EthanOConnor 30959 2090311
|
||||
SC 30832 2730764
|
||||
csnodgrass 29505 2688994
|
||||
jmdana 29458 2205261
|
||||
strelock 28219 2067805
|
||||
jkiiski 27832 1904470
|
||||
Pyafue 27533 1902349
|
||||
Garf 27515 2747562
|
||||
eastorwest 27421 2317535
|
||||
slakovv 26903 2021889
|
||||
Prcuvu 24835 2170122
|
||||
anst 24714 2190091
|
||||
hyperbolic.tom 24319 2017394
|
||||
Patrick_G 23687 1801617
|
||||
Sharaf_DG 22896 1786697
|
||||
nabildanial 22195 1519409
|
||||
chriswk 21931 1868317
|
||||
achambord 21665 1767323
|
||||
Zirie 20887 1472937
|
||||
team-oh 20217 1636708
|
||||
Isidor 20096 1680691
|
||||
ncfish1 19931 1520927
|
||||
nesoneg 19875 1463031
|
||||
Spprtr 19853 1548165
|
||||
JanErik 19849 1703875
|
||||
agg177 19478 1395014
|
||||
SFTUser 19231 1567999
|
||||
xor12 19017 1680165
|
||||
sg4032 18431 1641865
|
||||
rstoesser 18118 1293588
|
||||
MazeOfGalious 17917 1629593
|
||||
j3corre 17743 941444
|
||||
cisco2015 17725 1690126
|
||||
ianh2105 17706 1632562
|
||||
dex 17678 1467203
|
||||
jundery 17194 1115855
|
||||
iisiraider 17019 1101015
|
||||
horst.prack 17012 1465656
|
||||
Adrian.Schmidt123 16563 1281436
|
||||
purplefishies 16342 1092533
|
||||
wei 16274 1745989
|
||||
ville 16144 1384026
|
||||
eudhan 15712 1283717
|
||||
OuaisBla 15581 972000
|
||||
DragonLord 15559 1162790
|
||||
dju 14716 875569
|
||||
chris 14479 1487385
|
||||
0xB00B1ES 14079 1001120
|
||||
OssumOpossum 13776 1007129
|
||||
enedene 13460 905279
|
||||
bpfliegel 13346 884523
|
||||
Ente 13198 1156722
|
||||
IgorLeMasson 13087 1147232
|
||||
jpulman 13000 870599
|
||||
ako027ako 12775 1173203
|
||||
Nikolay.IT 12352 1068349
|
||||
Andrew Grant 12327 895539
|
||||
joster 12008 950160
|
||||
AdrianSA 11996 804972
|
||||
Nesa92 11455 1111993
|
||||
fatmurphy 11345 853210
|
||||
Dark_wizzie 11108 1007152
|
||||
modolief 10869 896470
|
||||
mschmidt 10757 803401
|
||||
infinity 10594 727027
|
||||
mabichito 10524 749391
|
||||
Thomas A. Anderson 10474 732094
|
||||
thijsk 10431 719357
|
||||
Flopzee 10339 894821
|
||||
crocogoat 10104 1013854
|
||||
SapphireBrand 10104 969604
|
||||
stocky 10017 699440
|
||||
|
|
64
src/Makefile
64
src/Makefile
|
@ -35,25 +35,29 @@ BINDIR = $(PREFIX)/bin
|
|||
### Built-in benchmark for pgo-builds
|
||||
PGOBENCH = ./$(EXE) bench
|
||||
|
||||
### Object files
|
||||
OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \
|
||||
material.o misc.o movegen.o movepick.o pawns.o position.o psqt.o \
|
||||
search.o thread.o timeman.o tt.o uci.o ucioption.o syzygy/tbprobe.o \
|
||||
eval/evaluate_mir_inv_tools.o \
|
||||
eval/nnue/evaluate_nnue.o \
|
||||
eval/nnue/evaluate_nnue_learner.o \
|
||||
eval/nnue/features/half_kp.o \
|
||||
eval/nnue/features/half_relative_kp.o \
|
||||
eval/nnue/features/k.o \
|
||||
eval/nnue/features/p.o \
|
||||
eval/nnue/features/castling_right.o \
|
||||
eval/nnue/features/enpassant.o \
|
||||
eval/nnue/nnue_test_command.o \
|
||||
extra/sfen_packer.o \
|
||||
learn/gensfen2019.o \
|
||||
learn/learner.o \
|
||||
learn/learning_tools.o \
|
||||
learn/multi_think.o
|
||||
### Source and object files
|
||||
SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \
|
||||
material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \
|
||||
search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
|
||||
eval/evaluate_mir_inv_tools.cpp \
|
||||
eval/nnue/evaluate_nnue.cpp \
|
||||
eval/nnue/evaluate_nnue_learner.cpp \
|
||||
eval/nnue/features/half_kp.cpp \
|
||||
eval/nnue/features/half_relative_kp.cpp \
|
||||
eval/nnue/features/k.cpp \
|
||||
eval/nnue/features/p.cpp \
|
||||
eval/nnue/features/castling_right.cpp \
|
||||
eval/nnue/features/enpassant.cpp \
|
||||
eval/nnue/nnue_test_command.cpp \
|
||||
extra/sfen_packer.cpp \
|
||||
learn/gensfen2019.cpp \
|
||||
learn/learner.cpp \
|
||||
learn/learning_tools.cpp \
|
||||
learn/multi_think.cpp
|
||||
|
||||
OBJS = $(SRCS:.cpp=.o)
|
||||
|
||||
VPATH = syzygy
|
||||
|
||||
### Establish the operating system name
|
||||
KERNEL = $(shell uname -s)
|
||||
|
@ -151,6 +155,8 @@ endif
|
|||
ifeq ($(ARCH),ppc-64)
|
||||
arch = ppc64
|
||||
bits = 64
|
||||
popcnt = yes
|
||||
prefetch = yes
|
||||
endif
|
||||
|
||||
|
||||
|
@ -304,7 +310,7 @@ ifeq ($(optimize),yes)
|
|||
CXXFLAGS += -fno-gcse -mthumb -march=armv7-a -mfloat-abi=softfp
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
ifeq ($(comp),$(filter $(comp),gcc clang icc))
|
||||
ifeq ($(KERNEL),Darwin)
|
||||
CXXFLAGS += -mdynamic-no-pic
|
||||
|
@ -329,7 +335,9 @@ endif
|
|||
|
||||
### 3.6 popcnt
|
||||
ifeq ($(popcnt),yes)
|
||||
ifeq ($(comp),icc)
|
||||
ifeq ($(arch),ppc64)
|
||||
CXXFLAGS += -DUSE_POPCNT
|
||||
else ifeq ($(comp),icc)
|
||||
CXXFLAGS += -msse3 -DUSE_POPCNT
|
||||
else
|
||||
CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT
|
||||
|
@ -391,10 +399,10 @@ help:
|
|||
@echo ""
|
||||
@echo "Supported archs:"
|
||||
@echo ""
|
||||
@echo "x86-64 > x86 64-bit"
|
||||
@echo "x86-64-modern > x86 64-bit with popcnt support"
|
||||
@echo "x86-64-bmi2 > x86 64-bit with pext support"
|
||||
@echo "x86-32 > x86 32-bit with SSE support"
|
||||
@echo "x86-64-bmi2 > x86 64-bit with pext support (also enables SSE4)"
|
||||
@echo "x86-64-modern > x86 64-bit with popcnt support (also enables SSE3)"
|
||||
@echo "x86-64 > x86 64-bit generic"
|
||||
@echo "x86-32 > x86 32-bit (also enables SSE)"
|
||||
@echo "x86-32-old > x86 32-bit fall back for old hardware"
|
||||
@echo "ppc-64 > PPC 64-bit"
|
||||
@echo "ppc-32 > PPC 32-bit"
|
||||
|
@ -417,11 +425,11 @@ help:
|
|||
@echo "Advanced examples, for experienced users: "
|
||||
@echo ""
|
||||
@echo "make build ARCH=x86-64 COMP=clang"
|
||||
@echo "make profile-build ARCH=x86-64-modern COMP=gcc COMPCXX=g++-4.8"
|
||||
@echo "make profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-4.8"
|
||||
@echo ""
|
||||
|
||||
|
||||
.PHONY: help build profile-build strip install clean objclean profileclean help \
|
||||
.PHONY: help build profile-build strip install clean objclean profileclean \
|
||||
config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \
|
||||
clang-profile-use clang-profile-make
|
||||
|
||||
|
@ -462,7 +470,7 @@ objclean:
|
|||
# clean auxiliary profiling files
|
||||
profileclean:
|
||||
@rm -rf profdir
|
||||
@rm -f bench.txt *.gcda ./syzygy/*.gcda *.gcno ./syzygy/*.gcno
|
||||
@rm -f bench.txt *.gcda *.gcno
|
||||
@rm -f stockfish.profdata *.profraw
|
||||
|
||||
default:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -61,6 +61,11 @@ const vector<string> Defaults = {
|
|||
"1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1",
|
||||
"6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1",
|
||||
"8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1",
|
||||
"5rk1/q6p/2p3bR/1pPp1rP1/1P1Pp3/P3B1Q1/1K3P2/R7 w - - 93 90",
|
||||
"4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21",
|
||||
"r3k2r/3nnpbp/q2pp1p1/p7/Pp1PPPP1/4BNN1/1P5P/R2Q1RK1 w kq - 0 16",
|
||||
"3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40",
|
||||
"4k3/3q1r2/1N2r1b1/3ppN2/2nPP3/1B1R2n1/2R1Q3/3K4 w - - 5 1",
|
||||
|
||||
// 5-man positions
|
||||
"8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate
|
||||
|
@ -113,7 +118,7 @@ vector<string> setup_bench(const Position& current, istream& is) {
|
|||
string fenFile = (is >> token) ? token : "default";
|
||||
string limitType = (is >> token) ? token : "depth";
|
||||
|
||||
go = "go " + limitType + " " + limit;
|
||||
go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit;
|
||||
|
||||
if (fenFile == "default")
|
||||
fens = Defaults;
|
||||
|
@ -139,9 +144,9 @@ vector<string> setup_bench(const Position& current, istream& is) {
|
|||
file.close();
|
||||
}
|
||||
|
||||
list.emplace_back("ucinewgame");
|
||||
list.emplace_back("setoption name Threads value " + threads);
|
||||
list.emplace_back("setoption name Hash value " + ttSize);
|
||||
list.emplace_back("ucinewgame");
|
||||
|
||||
for (const string& fen : fens)
|
||||
if (fen.find("setoption") != string::npos)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -19,8 +19,8 @@
|
|||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
#include <bitset>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "types.h"
|
||||
|
@ -31,8 +31,7 @@ namespace {
|
|||
// Positions with the pawn on files E to H will be mirrored before probing.
|
||||
constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608
|
||||
|
||||
// Each uint32_t stores results of 32 positions, one per bit
|
||||
uint32_t KPKBitbase[MAX_INDEX / 32];
|
||||
std::bitset<MAX_INDEX> KPKBitbase;
|
||||
|
||||
// A KPK bitbase index is an integer in [0, IndexMax] range
|
||||
//
|
||||
|
@ -43,8 +42,8 @@ namespace {
|
|||
// bit 12: side to move (WHITE or BLACK)
|
||||
// bit 13-14: white pawn file (from FILE_A to FILE_D)
|
||||
// bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2)
|
||||
unsigned index(Color us, Square bksq, Square wksq, Square psq) {
|
||||
return wksq | (bksq << 6) | (us << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15);
|
||||
unsigned index(Color stm, Square bksq, Square wksq, Square psq) {
|
||||
return int(wksq) | (bksq << 6) | (stm << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15);
|
||||
}
|
||||
|
||||
enum Result {
|
||||
|
@ -60,12 +59,9 @@ namespace {
|
|||
KPKPosition() = default;
|
||||
explicit KPKPosition(unsigned idx);
|
||||
operator Result() const { return result; }
|
||||
Result classify(const std::vector<KPKPosition>& db)
|
||||
{ return us == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); }
|
||||
Result classify(const std::vector<KPKPosition>& db);
|
||||
|
||||
template<Color Us> Result classify(const std::vector<KPKPosition>& db);
|
||||
|
||||
Color us;
|
||||
Color stm;
|
||||
Square ksq[COLOR_NB], psq;
|
||||
Result result;
|
||||
};
|
||||
|
@ -73,12 +69,11 @@ namespace {
|
|||
} // namespace
|
||||
|
||||
|
||||
bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color us) {
|
||||
bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) {
|
||||
|
||||
assert(file_of(wpsq) <= FILE_D);
|
||||
|
||||
unsigned idx = index(us, bksq, wksq, wpsq);
|
||||
return KPKBitbase[idx / 32] & (1 << (idx & 0x1F));
|
||||
return KPKBitbase[index(stm, bksq, wksq, wpsq)];
|
||||
}
|
||||
|
||||
|
||||
|
@ -97,10 +92,10 @@ void Bitbases::init() {
|
|||
for (repeat = idx = 0; idx < MAX_INDEX; ++idx)
|
||||
repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN);
|
||||
|
||||
// Map 32 results into one KPKBitbase[] entry
|
||||
// Fill the bitbase with the decisive results
|
||||
for (idx = 0; idx < MAX_INDEX; ++idx)
|
||||
if (db[idx] == WIN)
|
||||
KPKBitbase[idx / 32] |= 1 << (idx & 0x1F);
|
||||
KPKBitbase.set(idx);
|
||||
}
|
||||
|
||||
|
||||
|
@ -110,28 +105,28 @@ namespace {
|
|||
|
||||
ksq[WHITE] = Square((idx >> 0) & 0x3F);
|
||||
ksq[BLACK] = Square((idx >> 6) & 0x3F);
|
||||
us = Color ((idx >> 12) & 0x01);
|
||||
stm = Color ((idx >> 12) & 0x01);
|
||||
psq = make_square(File((idx >> 13) & 0x3), Rank(RANK_7 - ((idx >> 15) & 0x7)));
|
||||
|
||||
// Check if two pieces are on the same square or if a king can be captured
|
||||
if ( distance(ksq[WHITE], ksq[BLACK]) <= 1
|
||||
|| ksq[WHITE] == psq
|
||||
|| ksq[BLACK] == psq
|
||||
|| (us == WHITE && (PawnAttacks[WHITE][psq] & ksq[BLACK])))
|
||||
|| (stm == WHITE && (pawn_attacks_bb(WHITE, psq) & ksq[BLACK])))
|
||||
result = INVALID;
|
||||
|
||||
// Immediate win if a pawn can be promoted without getting captured
|
||||
else if ( us == WHITE
|
||||
else if ( stm == WHITE
|
||||
&& rank_of(psq) == RANK_7
|
||||
&& ksq[us] != psq + NORTH
|
||||
&& ( distance(ksq[~us], psq + NORTH) > 1
|
||||
|| (PseudoAttacks[KING][ksq[us]] & (psq + NORTH))))
|
||||
&& ksq[stm] != psq + NORTH
|
||||
&& ( distance(ksq[~stm], psq + NORTH) > 1
|
||||
|| (attacks_bb<KING>(ksq[stm]) & (psq + NORTH))))
|
||||
result = WIN;
|
||||
|
||||
// Immediate draw if it is a stalemate or a king captures undefended pawn
|
||||
else if ( us == BLACK
|
||||
&& ( !(PseudoAttacks[KING][ksq[us]] & ~(PseudoAttacks[KING][ksq[~us]] | PawnAttacks[~us][psq]))
|
||||
|| (PseudoAttacks[KING][ksq[us]] & psq & ~PseudoAttacks[KING][ksq[~us]])))
|
||||
else if ( stm == BLACK
|
||||
&& ( !(attacks_bb<KING>(ksq[stm]) & ~(attacks_bb<KING>(ksq[~stm]) | pawn_attacks_bb(~stm, psq)))
|
||||
|| (attacks_bb<KING>(ksq[stm]) & psq & ~attacks_bb<KING>(ksq[~stm]))))
|
||||
result = DRAW;
|
||||
|
||||
// Position will be classified later
|
||||
|
@ -139,7 +134,6 @@ namespace {
|
|||
result = UNKNOWN;
|
||||
}
|
||||
|
||||
template<Color Us>
|
||||
Result KPKPosition::classify(const std::vector<KPKPosition>& db) {
|
||||
|
||||
// White to move: If one move leads to a position classified as WIN, the result
|
||||
|
@ -151,27 +145,25 @@ namespace {
|
|||
// of the current position is DRAW. If all moves lead to positions classified
|
||||
// as WIN, the position is classified as WIN, otherwise the current position is
|
||||
// classified as UNKNOWN.
|
||||
|
||||
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
constexpr Result Good = (Us == WHITE ? WIN : DRAW);
|
||||
constexpr Result Bad = (Us == WHITE ? DRAW : WIN);
|
||||
const Result Good = (stm == WHITE ? WIN : DRAW);
|
||||
const Result Bad = (stm == WHITE ? DRAW : WIN);
|
||||
|
||||
Result r = INVALID;
|
||||
Bitboard b = PseudoAttacks[KING][ksq[Us]];
|
||||
Bitboard b = attacks_bb<KING>(ksq[stm]);
|
||||
|
||||
while (b)
|
||||
r |= Us == WHITE ? db[index(Them, ksq[Them] , pop_lsb(&b), psq)]
|
||||
: db[index(Them, pop_lsb(&b), ksq[Them] , psq)];
|
||||
r |= stm == WHITE ? db[index(BLACK, ksq[BLACK] , pop_lsb(&b), psq)]
|
||||
: db[index(WHITE, pop_lsb(&b), ksq[WHITE], psq)];
|
||||
|
||||
if (Us == WHITE)
|
||||
if (stm == WHITE)
|
||||
{
|
||||
if (rank_of(psq) < RANK_7) // Single push
|
||||
r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH)];
|
||||
r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH)];
|
||||
|
||||
if ( rank_of(psq) == RANK_2 // Double push
|
||||
&& psq + NORTH != ksq[Us]
|
||||
&& psq + NORTH != ksq[Them])
|
||||
r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH + NORTH)];
|
||||
&& psq + NORTH != ksq[WHITE]
|
||||
&& psq + NORTH != ksq[BLACK])
|
||||
r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH + NORTH)];
|
||||
}
|
||||
|
||||
return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -69,32 +69,14 @@ const std::string Bitboards::pretty(Bitboard b) {
|
|||
void Bitboards::init() {
|
||||
|
||||
for (unsigned i = 0; i < (1 << 16); ++i)
|
||||
PopCnt16[i] = std::bitset<16>(i).count();
|
||||
PopCnt16[i] = uint8_t(std::bitset<16>(i).count());
|
||||
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
SquareBB[s] = (1ULL << s);
|
||||
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
|
||||
|
||||
int steps[][5] = { {}, { 7, 9 }, { 6, 10, 15, 17 }, {}, {}, {}, { 1, 7, 8, 9 } };
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (PieceType pt : { PAWN, KNIGHT, KING })
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
for (int i = 0; steps[pt][i]; ++i)
|
||||
{
|
||||
Square to = s + Direction(c == WHITE ? steps[pt][i] : -steps[pt][i]);
|
||||
|
||||
if (is_ok(to) && distance(s, to) < 3)
|
||||
{
|
||||
if (pt == PAWN)
|
||||
PawnAttacks[c][s] |= to;
|
||||
else
|
||||
PseudoAttacks[pt][s] |= to;
|
||||
}
|
||||
}
|
||||
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
|
||||
|
||||
Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST };
|
||||
Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
|
||||
|
@ -104,6 +86,15 @@ void Bitboards::init() {
|
|||
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||
{
|
||||
PawnAttacks[WHITE][s1] = pawn_attacks_bb<WHITE>(square_bb(s1));
|
||||
PawnAttacks[BLACK][s1] = pawn_attacks_bb<BLACK>(square_bb(s1));
|
||||
|
||||
for (int step : {-9, -8, -7, -1, 1, 7, 8, 9} )
|
||||
PseudoAttacks[KING][s1] |= safe_destination(s1, step);
|
||||
|
||||
for (int step : {-17, -15, -10, -6, 6, 10, 15, 17} )
|
||||
PseudoAttacks[KNIGHT][s1] |= safe_destination(s1, step);
|
||||
|
||||
PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb<BISHOP>(s1, 0);
|
||||
PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0);
|
||||
|
||||
|
@ -119,20 +110,16 @@ namespace {
|
|||
|
||||
Bitboard sliding_attack(Direction directions[], Square sq, Bitboard occupied) {
|
||||
|
||||
Bitboard attack = 0;
|
||||
Bitboard attacks = 0;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
for (Square s = sq + directions[i];
|
||||
is_ok(s) && distance(s, s - directions[i]) == 1;
|
||||
s += directions[i])
|
||||
{
|
||||
attack |= s;
|
||||
{
|
||||
Square s = sq;
|
||||
while(safe_destination(s, directions[i]) && !(occupied & s))
|
||||
attacks |= (s += directions[i]);
|
||||
}
|
||||
|
||||
if (occupied & s)
|
||||
break;
|
||||
}
|
||||
|
||||
return attack;
|
||||
return attacks;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -106,7 +106,7 @@ extern Magic RookMagics[SQUARE_NB];
|
|||
extern Magic BishopMagics[SQUARE_NB];
|
||||
|
||||
inline Bitboard square_bb(Square s) {
|
||||
assert(s >= SQ_A1 && s <= SQ_H8);
|
||||
assert(is_ok(s));
|
||||
return SquareBB[s];
|
||||
}
|
||||
|
||||
|
@ -119,12 +119,18 @@ inline Bitboard operator^( Bitboard b, Square s) { return b ^ square_bb(s); }
|
|||
inline Bitboard& operator|=(Bitboard& b, Square s) { return b |= square_bb(s); }
|
||||
inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); }
|
||||
|
||||
inline Bitboard operator&(Square s, Bitboard b) { return b & s; }
|
||||
inline Bitboard operator|(Square s, Bitboard b) { return b | s; }
|
||||
inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; }
|
||||
|
||||
inline Bitboard operator|(Square s, Square s2) { return square_bb(s) | s2; }
|
||||
|
||||
constexpr bool more_than_one(Bitboard b) {
|
||||
return b & (b - 1);
|
||||
}
|
||||
|
||||
inline bool opposite_colors(Square s1, Square s2) {
|
||||
return bool(DarkSquares & s1) != bool(DarkSquares & s2);
|
||||
constexpr bool opposite_colors(Square s1, Square s2) {
|
||||
return (s1 + rank_of(s1) + s2 + rank_of(s2)) & 1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -148,7 +154,7 @@ inline Bitboard file_bb(Square s) {
|
|||
}
|
||||
|
||||
|
||||
/// shift() moves a bitboard one step along direction D
|
||||
/// shift() moves a bitboard one or two steps as specified by the direction D
|
||||
|
||||
template<Direction D>
|
||||
constexpr Bitboard shift(Bitboard b) {
|
||||
|
@ -170,6 +176,12 @@ constexpr Bitboard pawn_attacks_bb(Bitboard b) {
|
|||
: shift<SOUTH_WEST>(b) | shift<SOUTH_EAST>(b);
|
||||
}
|
||||
|
||||
inline Bitboard pawn_attacks_bb(Color c, Square s) {
|
||||
|
||||
assert(is_ok(s));
|
||||
return PawnAttacks[c][s];
|
||||
}
|
||||
|
||||
|
||||
/// pawn_double_attacks_bb() returns the squares doubly attacked by pawns of the
|
||||
/// given color from the squares in the given bitboard.
|
||||
|
@ -184,8 +196,8 @@ constexpr Bitboard pawn_double_attacks_bb(Bitboard b) {
|
|||
/// adjacent_files_bb() returns a bitboard representing all the squares on the
|
||||
/// adjacent files of the given one.
|
||||
|
||||
inline Bitboard adjacent_files_bb(File f) {
|
||||
return shift<EAST>(file_bb(f)) | shift<WEST>(file_bb(f));
|
||||
inline Bitboard adjacent_files_bb(Square s) {
|
||||
return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s));
|
||||
}
|
||||
|
||||
|
||||
|
@ -193,8 +205,8 @@ inline Bitboard adjacent_files_bb(File f) {
|
|||
/// If the given squares are not on a same file/rank/diagonal, return 0.
|
||||
|
||||
inline Bitboard between_bb(Square s1, Square s2) {
|
||||
return LineBB[s1][s2] & ( (AllSquares << (s1 + (s1 < s2)))
|
||||
^(AllSquares << (s2 + !(s1 < s2))));
|
||||
Bitboard b = LineBB[s1][s2] & ((AllSquares << s1) ^ (AllSquares << s2));
|
||||
return b & (b - 1); //exclude lsb
|
||||
}
|
||||
|
||||
|
||||
|
@ -203,8 +215,8 @@ inline Bitboard between_bb(Square s1, Square s2) {
|
|||
/// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2.
|
||||
|
||||
inline Bitboard forward_ranks_bb(Color c, Square s) {
|
||||
return c == WHITE ? ~Rank1BB << 8 * (rank_of(s) - RANK_1)
|
||||
: ~Rank8BB >> 8 * (RANK_8 - rank_of(s));
|
||||
return c == WHITE ? ~Rank1BB << 8 * relative_rank(WHITE, s)
|
||||
: ~Rank8BB >> 8 * relative_rank(BLACK, s);
|
||||
}
|
||||
|
||||
|
||||
|
@ -221,7 +233,7 @@ inline Bitboard forward_file_bb(Color c, Square s) {
|
|||
/// starting from the given square.
|
||||
|
||||
inline Bitboard pawn_attack_span(Color c, Square s) {
|
||||
return forward_ranks_bb(c, s) & adjacent_files_bb(file_of(s));
|
||||
return forward_ranks_bb(c, s) & adjacent_files_bb(s);
|
||||
}
|
||||
|
||||
|
||||
|
@ -229,7 +241,7 @@ inline Bitboard pawn_attack_span(Color c, Square s) {
|
|||
/// the given color and on the given square is a passed pawn.
|
||||
|
||||
inline Bitboard passed_pawn_span(Color c, Square s) {
|
||||
return forward_ranks_bb(c, s) & (adjacent_files_bb(file_of(s)) | file_bb(s));
|
||||
return forward_ranks_bb(c, s) & (adjacent_files_bb(s) | file_bb(s));
|
||||
}
|
||||
|
||||
|
||||
|
@ -249,23 +261,49 @@ template<> inline int distance<File>(Square x, Square y) { return std::abs(file_
|
|||
template<> inline int distance<Rank>(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); }
|
||||
template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; }
|
||||
|
||||
template<class T> constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
|
||||
return v < lo ? lo : v > hi ? hi : v;
|
||||
inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); }
|
||||
inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); }
|
||||
|
||||
/// Return the target square bitboard if we do not step off the board, empty otherwise
|
||||
|
||||
inline Bitboard safe_destination(Square s, int step)
|
||||
{
|
||||
Square to = Square(s + step);
|
||||
return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
|
||||
}
|
||||
|
||||
/// attacks_bb() returns a bitboard representing all the squares attacked by a
|
||||
/// piece of type Pt (bishop or rook) placed on 's'.
|
||||
/// attacks_bb(Square) returns the pseudo attacks of the give piece type
|
||||
/// assuming an empty board.
|
||||
|
||||
template<PieceType Pt>
|
||||
inline Bitboard attacks_bb(Square s) {
|
||||
|
||||
assert((Pt != PAWN) && (is_ok(s)));
|
||||
|
||||
return PseudoAttacks[Pt][s];
|
||||
}
|
||||
|
||||
/// attacks_bb(Square, Bitboard) returns the attacks by the given piece
|
||||
/// assuming the board is occupied according to the passed Bitboard.
|
||||
/// Sliding piece attacks do not continue passed an occupied square.
|
||||
|
||||
template<PieceType Pt>
|
||||
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
|
||||
|
||||
const Magic& m = Pt == ROOK ? RookMagics[s] : BishopMagics[s];
|
||||
return m.attacks[m.index(occupied)];
|
||||
assert((Pt != PAWN) && (is_ok(s)));
|
||||
|
||||
switch (Pt)
|
||||
{
|
||||
case BISHOP: return BishopMagics[s].attacks[BishopMagics[s].index(occupied)];
|
||||
case ROOK : return RookMagics[s].attacks[ RookMagics[s].index(occupied)];
|
||||
case QUEEN : return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
|
||||
default : return PseudoAttacks[Pt][s];
|
||||
}
|
||||
}
|
||||
|
||||
inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
|
||||
|
||||
assert(pt != PAWN);
|
||||
assert((pt != PAWN) && (is_ok(s)));
|
||||
|
||||
switch (pt)
|
||||
{
|
||||
|
@ -370,16 +408,18 @@ inline Square msb(Bitboard b) {
|
|||
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard
|
||||
|
||||
inline Square pop_lsb(Bitboard* b) {
|
||||
assert(*b);
|
||||
const Square s = lsb(*b);
|
||||
*b &= *b - 1;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/// frontmost_sq() and backmost_sq() return the most/least advanced square in
|
||||
/// the given bitboard relative to the given color.
|
||||
|
||||
inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); }
|
||||
inline Square backmost_sq(Color c, Bitboard b) { return c == WHITE ? lsb(b) : msb(b); }
|
||||
/// frontmost_sq() returns the most advanced square for the given color,
|
||||
/// requires a non-zero bitboard.
|
||||
inline Square frontmost_sq(Color c, Bitboard b) {
|
||||
assert(b);
|
||||
return c == WHITE ? msb(b) : lsb(b);
|
||||
}
|
||||
|
||||
#endif // #ifndef BITBOARD_H_INCLUDED
|
||||
|
|
210
src/endgame.cpp
210
src/endgame.cpp
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -24,42 +24,23 @@
|
|||
#include "endgame.h"
|
||||
#include "movegen.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace {
|
||||
|
||||
// Table used to drive the king towards the edge of the board
|
||||
// Used to drive the king towards the edge of the board
|
||||
// in KX vs K and KQ vs KR endgames.
|
||||
constexpr int PushToEdges[SQUARE_NB] = {
|
||||
100, 90, 80, 70, 70, 80, 90, 100,
|
||||
90, 70, 60, 50, 50, 60, 70, 90,
|
||||
80, 60, 40, 30, 30, 40, 60, 80,
|
||||
70, 50, 30, 20, 20, 30, 50, 70,
|
||||
70, 50, 30, 20, 20, 30, 50, 70,
|
||||
80, 60, 40, 30, 30, 40, 60, 80,
|
||||
90, 70, 60, 50, 50, 60, 70, 90,
|
||||
100, 90, 80, 70, 70, 80, 90, 100
|
||||
};
|
||||
inline int push_to_edge(Square s) {
|
||||
int rd = edge_distance(rank_of(s)), fd = edge_distance(file_of(s));
|
||||
return 90 - (7 * fd * fd / 2 + 7 * rd * rd / 2);
|
||||
}
|
||||
|
||||
// Table used to drive the king towards a corner square of the
|
||||
// right color in KBN vs K endgames.
|
||||
constexpr int PushToCorners[SQUARE_NB] = {
|
||||
6400, 6080, 5760, 5440, 5120, 4800, 4480, 4160,
|
||||
6080, 5760, 5440, 5120, 4800, 4480, 4160, 4480,
|
||||
5760, 5440, 4960, 4480, 4480, 4000, 4480, 4800,
|
||||
5440, 5120, 4480, 3840, 3520, 4480, 4800, 5120,
|
||||
5120, 4800, 4480, 3520, 3840, 4480, 5120, 5440,
|
||||
4800, 4480, 4000, 4480, 4480, 4960, 5440, 5760,
|
||||
4480, 4160, 4480, 4800, 5120, 5440, 5760, 6080,
|
||||
4160, 4480, 4800, 5120, 5440, 5760, 6080, 6400
|
||||
};
|
||||
// Used to drive the king towards A1H8 corners in KBN vs K endgames.
|
||||
inline int push_to_corner(Square s) {
|
||||
return abs(7 - rank_of(s) - file_of(s));
|
||||
}
|
||||
|
||||
// Tables used to drive a piece towards or away from another piece
|
||||
constexpr int PushClose[8] = { 0, 0, 100, 80, 60, 40, 20, 10 };
|
||||
constexpr int PushAway [8] = { 0, 5, 20, 40, 60, 80, 90, 100 };
|
||||
|
||||
// Pawn Rank based scaling factors used in KRPPKRP endgame
|
||||
constexpr int KRPPKRPScaleFactors[RANK_NB] = { 0, 9, 10, 14, 21, 44, 0, 0 };
|
||||
// Drive a piece close to or away from another piece
|
||||
inline int push_close(Square s1, Square s2) { return 140 - 20 * distance(s1, s2); }
|
||||
inline int push_away(Square s1, Square s2) { return 120 - push_close(s1, s2); }
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) {
|
||||
|
@ -74,9 +55,9 @@ namespace {
|
|||
assert(pos.count<PAWN>(strongSide) == 1);
|
||||
|
||||
if (file_of(pos.square<PAWN>(strongSide)) >= FILE_E)
|
||||
sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1
|
||||
sq = flip_file(sq);
|
||||
|
||||
return strongSide == WHITE ? sq : ~sq;
|
||||
return strongSide == WHITE ? sq : flip_rank(sq);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -88,27 +69,26 @@ namespace Endgames {
|
|||
|
||||
void init() {
|
||||
|
||||
add<KPK>("KPK");
|
||||
add<KNNK>("KNNK");
|
||||
add<KBNK>("KBNK");
|
||||
add<KRKP>("KRKP");
|
||||
add<KRKB>("KRKB");
|
||||
add<KRKN>("KRKN");
|
||||
add<KQKP>("KQKP");
|
||||
add<KQKR>("KQKR");
|
||||
add<KNNKP>("KNNKP");
|
||||
add<KPK>("KPK");
|
||||
add<KNNK>("KNNK");
|
||||
add<KBNK>("KBNK");
|
||||
add<KRKP>("KRKP");
|
||||
add<KRKB>("KRKB");
|
||||
add<KRKN>("KRKN");
|
||||
add<KQKP>("KQKP");
|
||||
add<KQKR>("KQKR");
|
||||
add<KNNKP>("KNNKP");
|
||||
|
||||
add<KNPK>("KNPK");
|
||||
add<KNPKB>("KNPKB");
|
||||
add<KRPKR>("KRPKR");
|
||||
add<KRPKB>("KRPKB");
|
||||
add<KBPKB>("KBPKB");
|
||||
add<KBPKN>("KBPKN");
|
||||
add<KBPPKB>("KBPPKB");
|
||||
add<KRPPKRP>("KRPPKRP");
|
||||
add<KRPKR>("KRPKR");
|
||||
add<KRPKB>("KRPKB");
|
||||
add<KBPKB>("KBPKB");
|
||||
add<KBPKN>("KBPKN");
|
||||
add<KBPPKB>("KBPPKB");
|
||||
add<KRPPKRP>("KRPPKRP");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Mate with KX vs K. This function is used to evaluate positions with
|
||||
/// king and plenty of material vs a lone king. It simply gives the
|
||||
/// attacking side a bonus for driving the defending king towards the edge
|
||||
|
@ -128,15 +108,15 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
|
|||
|
||||
Value result = pos.non_pawn_material(strongSide)
|
||||
+ pos.count<PAWN>(strongSide) * PawnValueEg
|
||||
+ PushToEdges[loserKSq]
|
||||
+ PushClose[distance(winnerKSq, loserKSq)];
|
||||
+ push_to_edge(loserKSq)
|
||||
+ push_close(winnerKSq, loserKSq);
|
||||
|
||||
if ( pos.count<QUEEN>(strongSide)
|
||||
|| pos.count<ROOK>(strongSide)
|
||||
||(pos.count<BISHOP>(strongSide) && pos.count<KNIGHT>(strongSide))
|
||||
|| ( (pos.pieces(strongSide, BISHOP) & ~DarkSquares)
|
||||
&& (pos.pieces(strongSide, BISHOP) & DarkSquares)))
|
||||
result = std::min(result + VALUE_KNOWN_WIN, VALUE_MATE_IN_MAX_PLY - 1);
|
||||
result = std::min(result + VALUE_KNOWN_WIN, VALUE_TB_WIN_IN_MAX_PLY - 1);
|
||||
|
||||
return strongSide == pos.side_to_move() ? result : -result;
|
||||
}
|
||||
|
@ -154,19 +134,19 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
|
|||
Square loserKSq = pos.square<KING>(weakSide);
|
||||
Square bishopSq = pos.square<BISHOP>(strongSide);
|
||||
|
||||
// If our Bishop does not attack A1/H8, we flip the enemy king square
|
||||
// If our bishop does not attack A1/H8, we flip the enemy king square
|
||||
// to drive to opposite corners (A8/H1).
|
||||
|
||||
Value result = VALUE_KNOWN_WIN
|
||||
+ PushClose[distance(winnerKSq, loserKSq)]
|
||||
+ PushToCorners[opposite_colors(bishopSq, SQ_A1) ? ~loserKSq : loserKSq];
|
||||
Value result = (VALUE_KNOWN_WIN + 3520)
|
||||
+ push_close(winnerKSq, loserKSq)
|
||||
+ 420 * push_to_corner(opposite_colors(bishopSq, SQ_A1) ? flip_file(loserKSq) : loserKSq);
|
||||
|
||||
assert(abs(result) < VALUE_MATE_IN_MAX_PLY);
|
||||
assert(abs(result) < VALUE_TB_WIN_IN_MAX_PLY);
|
||||
return strongSide == pos.side_to_move() ? result : -result;
|
||||
}
|
||||
|
||||
|
||||
/// KP vs K. This endgame is evaluated with the help of a bitbase.
|
||||
/// KP vs K. This endgame is evaluated with the help of a bitbase
|
||||
template<>
|
||||
Value Endgame<KPK>::operator()(const Position& pos) const {
|
||||
|
||||
|
@ -242,7 +222,7 @@ Value Endgame<KRKB>::operator()(const Position& pos) const {
|
|||
assert(verify_material(pos, strongSide, RookValueMg, 0));
|
||||
assert(verify_material(pos, weakSide, BishopValueMg, 0));
|
||||
|
||||
Value result = Value(PushToEdges[pos.square<KING>(weakSide)]);
|
||||
Value result = Value(push_to_edge(pos.square<KING>(weakSide)));
|
||||
return strongSide == pos.side_to_move() ? result : -result;
|
||||
}
|
||||
|
||||
|
@ -257,7 +237,7 @@ Value Endgame<KRKN>::operator()(const Position& pos) const {
|
|||
|
||||
Square bksq = pos.square<KING>(weakSide);
|
||||
Square bnsq = pos.square<KNIGHT>(weakSide);
|
||||
Value result = Value(PushToEdges[bksq] + PushAway[distance(bksq, bnsq)]);
|
||||
Value result = Value(push_to_edge(bksq) + push_away(bksq, bnsq));
|
||||
return strongSide == pos.side_to_move() ? result : -result;
|
||||
}
|
||||
|
||||
|
@ -276,11 +256,11 @@ Value Endgame<KQKP>::operator()(const Position& pos) const {
|
|||
Square loserKSq = pos.square<KING>(weakSide);
|
||||
Square pawnSq = pos.square<PAWN>(weakSide);
|
||||
|
||||
Value result = Value(PushClose[distance(winnerKSq, loserKSq)]);
|
||||
Value result = Value(push_close(winnerKSq, loserKSq));
|
||||
|
||||
if ( relative_rank(weakSide, pawnSq) != RANK_7
|
||||
|| distance(loserKSq, pawnSq) != 1
|
||||
|| !((FileABB | FileCBB | FileFBB | FileHBB) & pawnSq))
|
||||
|| ((FileBBB | FileDBB | FileEBB | FileGBB) & pawnSq))
|
||||
result += QueenValueEg - PawnValueEg;
|
||||
|
||||
return strongSide == pos.side_to_move() ? result : -result;
|
||||
|
@ -302,23 +282,24 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
|
|||
|
||||
Value result = QueenValueEg
|
||||
- RookValueEg
|
||||
+ PushToEdges[loserKSq]
|
||||
+ PushClose[distance(winnerKSq, loserKSq)];
|
||||
+ push_to_edge(loserKSq)
|
||||
+ push_close(winnerKSq, loserKSq);
|
||||
|
||||
return strongSide == pos.side_to_move() ? result : -result;
|
||||
}
|
||||
|
||||
|
||||
/// KNN vs KP. Simply push the opposing king to the corner
|
||||
/// KNN vs KP. Very drawish, but there are some mate opportunities if we can
|
||||
// press the weakSide King to a corner before the pawn advances too much.
|
||||
template<>
|
||||
Value Endgame<KNNKP>::operator()(const Position& pos) const {
|
||||
|
||||
assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0));
|
||||
assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
|
||||
|
||||
Value result = 2 * KnightValueEg
|
||||
- PawnValueEg
|
||||
+ PushToEdges[pos.square<KING>(weakSide)];
|
||||
Value result = PawnValueEg
|
||||
+ 2 * push_to_edge(pos.square<KING>(weakSide))
|
||||
- 10 * relative_rank(weakSide, pos.square<PAWN>(weakSide));
|
||||
|
||||
return strongSide == pos.side_to_move() ? result : -result;
|
||||
}
|
||||
|
@ -341,30 +322,28 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
|
|||
// No assertions about the material of weakSide, because we want draws to
|
||||
// be detected even when the weaker side has some pawns.
|
||||
|
||||
Bitboard pawns = pos.pieces(strongSide, PAWN);
|
||||
File pawnsFile = file_of(lsb(pawns));
|
||||
Bitboard strongPawns = pos.pieces(strongSide, PAWN);
|
||||
Bitboard allPawns = pos.pieces(PAWN);
|
||||
|
||||
// All pawns are on a single rook file?
|
||||
if ( (pawnsFile == FILE_A || pawnsFile == FILE_H)
|
||||
&& !(pawns & ~file_bb(pawnsFile)))
|
||||
// All strongSide pawns are on a single rook file?
|
||||
if (!(strongPawns & ~FileABB) || !(strongPawns & ~FileHBB))
|
||||
{
|
||||
Square bishopSq = pos.square<BISHOP>(strongSide);
|
||||
Square queeningSq = relative_square(strongSide, make_square(pawnsFile, RANK_8));
|
||||
Square kingSq = pos.square<KING>(weakSide);
|
||||
Square queeningSq = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8));
|
||||
Square weakKingSq = pos.square<KING>(weakSide);
|
||||
|
||||
if ( opposite_colors(queeningSq, bishopSq)
|
||||
&& distance(queeningSq, kingSq) <= 1)
|
||||
&& distance(queeningSq, weakKingSq) <= 1)
|
||||
return SCALE_FACTOR_DRAW;
|
||||
}
|
||||
|
||||
// If all the pawns are on the same B or G file, then it's potentially a draw
|
||||
if ( (pawnsFile == FILE_B || pawnsFile == FILE_G)
|
||||
&& !(pos.pieces(PAWN) & ~file_bb(pawnsFile))
|
||||
if ((!(allPawns & ~FileBBB) || !(allPawns & ~FileGBB))
|
||||
&& pos.non_pawn_material(weakSide) == 0
|
||||
&& pos.count<PAWN>(weakSide) >= 1)
|
||||
{
|
||||
// Get weakSide pawn that is closest to the home rank
|
||||
Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN));
|
||||
// Get the least advanced weakSide pawn
|
||||
Square weakPawnSq = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN));
|
||||
|
||||
Square strongKingSq = pos.square<KING>(strongSide);
|
||||
Square weakKingSq = pos.square<KING>(weakSide);
|
||||
|
@ -373,8 +352,8 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
|
|||
// There's potential for a draw if our pawn is blocked on the 7th rank,
|
||||
// the bishop cannot attack it or they only have one pawn left
|
||||
if ( relative_rank(strongSide, weakPawnSq) == RANK_7
|
||||
&& (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide)))
|
||||
&& (opposite_colors(bishopSq, weakPawnSq) || pos.count<PAWN>(strongSide) == 1))
|
||||
&& (strongPawns & (weakPawnSq + pawn_push(weakSide)))
|
||||
&& (opposite_colors(bishopSq, weakPawnSq) || !more_than_one(strongPawns)))
|
||||
{
|
||||
int strongKingDist = distance(weakPawnSq, strongKingSq);
|
||||
int weakKingDist = distance(weakPawnSq, weakKingSq);
|
||||
|
@ -412,8 +391,8 @@ ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
|
|||
&& relative_rank(weakSide, pos.square<KING>(strongSide)) >= RANK_4
|
||||
&& relative_rank(weakSide, rsq) == RANK_3
|
||||
&& ( pos.pieces(weakSide, PAWN)
|
||||
& pos.attacks_from<KING>(kingSq)
|
||||
& pos.attacks_from<PAWN>(rsq, strongSide)))
|
||||
& attacks_bb<KING>(kingSq)
|
||||
& pawn_attacks_bb(strongSide, rsq)))
|
||||
return SCALE_FACTOR_DRAW;
|
||||
|
||||
return SCALE_FACTOR_NONE;
|
||||
|
@ -556,7 +535,7 @@ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
|
|||
// the corner
|
||||
if ( rk == RANK_6
|
||||
&& distance(psq + 2 * push, ksq) <= 1
|
||||
&& (PseudoAttacks[BISHOP][bsq] & (psq + push))
|
||||
&& (attacks_bb<BISHOP>(bsq) & (psq + push))
|
||||
&& distance<File>(bsq, psq) >= 2)
|
||||
return ScaleFactor(8);
|
||||
}
|
||||
|
@ -587,7 +566,7 @@ ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
|
|||
&& relative_rank(strongSide, bksq) > r)
|
||||
{
|
||||
assert(r > RANK_1 && r < RANK_7);
|
||||
return ScaleFactor(KRPPKRPScaleFactors[r]);
|
||||
return ScaleFactor(7 * r);
|
||||
}
|
||||
return SCALE_FACTOR_NONE;
|
||||
}
|
||||
|
@ -605,11 +584,9 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
|
|||
Square ksq = pos.square<KING>(weakSide);
|
||||
Bitboard pawns = pos.pieces(strongSide, PAWN);
|
||||
|
||||
// If all pawns are ahead of the king, on a single rook file and
|
||||
// the king is within one file of the pawns, it's a draw.
|
||||
if ( !(pawns & ~forward_ranks_bb(weakSide, ksq))
|
||||
&& !((pawns & ~FileABB) && (pawns & ~FileHBB))
|
||||
&& distance<File>(ksq, lsb(pawns)) <= 1)
|
||||
// If all pawns are ahead of the king on a single rook file, it's a draw.
|
||||
if (!((pawns & ~FileABB) || (pawns & ~FileHBB)) &&
|
||||
!(pawns & ~passed_pawn_span(weakSide, ksq)))
|
||||
return SCALE_FACTOR_DRAW;
|
||||
|
||||
return SCALE_FACTOR_NONE;
|
||||
|
@ -632,8 +609,7 @@ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
|
|||
Square weakKingSq = pos.square<KING>(weakSide);
|
||||
|
||||
// Case 1: Defending king blocks the pawn, and cannot be driven away
|
||||
if ( file_of(weakKingSq) == file_of(pawnSq)
|
||||
&& relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq)
|
||||
if ( (forward_file_bb(strongSide, pawnSq) & weakKingSq)
|
||||
&& ( opposite_colors(weakKingSq, strongBishopSq)
|
||||
|| relative_rank(strongSide, weakKingSq) <= RANK_6))
|
||||
return SCALE_FACTOR_DRAW;
|
||||
|
@ -694,14 +670,14 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
|
|||
if ( ksq == blockSq1
|
||||
&& opposite_colors(ksq, wbsq)
|
||||
&& ( bbsq == blockSq2
|
||||
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP))
|
||||
|| (attacks_bb<BISHOP>(blockSq2, pos.pieces()) & pos.pieces(weakSide, BISHOP))
|
||||
|| distance<Rank>(psq1, psq2) >= 2))
|
||||
return SCALE_FACTOR_DRAW;
|
||||
|
||||
else if ( ksq == blockSq2
|
||||
&& opposite_colors(ksq, wbsq)
|
||||
&& ( bbsq == blockSq1
|
||||
|| (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(weakSide, BISHOP))))
|
||||
|| (attacks_bb<BISHOP>(blockSq1, pos.pieces()) & pos.pieces(weakSide, BISHOP))))
|
||||
return SCALE_FACTOR_DRAW;
|
||||
else
|
||||
return SCALE_FACTOR_NONE;
|
||||
|
@ -736,46 +712,6 @@ ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
|
|||
}
|
||||
|
||||
|
||||
/// KNP vs K. There is a single rule: if the pawn is a rook pawn on the 7th rank
|
||||
/// and the defending king prevents the pawn from advancing, the position is drawn.
|
||||
template<>
|
||||
ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
|
||||
|
||||
assert(verify_material(pos, strongSide, KnightValueMg, 1));
|
||||
assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
|
||||
|
||||
// Assume strongSide is white and the pawn is on files A-D
|
||||
Square pawnSq = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
|
||||
Square weakKingSq = normalize(pos, strongSide, pos.square<KING>(weakSide));
|
||||
|
||||
if (pawnSq == SQ_A7 && distance(SQ_A8, weakKingSq) <= 1)
|
||||
return SCALE_FACTOR_DRAW;
|
||||
|
||||
return SCALE_FACTOR_NONE;
|
||||
}
|
||||
|
||||
|
||||
/// KNP vs KB. If knight can block bishop from taking pawn, it's a win.
|
||||
/// Otherwise the position is drawn.
|
||||
template<>
|
||||
ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
|
||||
|
||||
assert(verify_material(pos, strongSide, KnightValueMg, 1));
|
||||
assert(verify_material(pos, weakSide, BishopValueMg, 0));
|
||||
|
||||
Square pawnSq = pos.square<PAWN>(strongSide);
|
||||
Square bishopSq = pos.square<BISHOP>(weakSide);
|
||||
Square weakKingSq = pos.square<KING>(weakSide);
|
||||
|
||||
// King needs to get close to promoting pawn to prevent knight from blocking.
|
||||
// Rules for this are very tricky, so just approximate.
|
||||
if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq))
|
||||
return ScaleFactor(distance(weakKingSq, pawnSq));
|
||||
|
||||
return SCALE_FACTOR_NONE;
|
||||
}
|
||||
|
||||
|
||||
/// KP vs KP. This is done by removing the weakest side's pawn and probing the
|
||||
/// KP vs K bitbase: If the weakest side has a draw without the pawn, it probably
|
||||
/// has at least a draw with the pawn as well. The exception is when the stronger
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -21,10 +21,10 @@
|
|||
#ifndef ENDGAME_H_INCLUDED
|
||||
#define ENDGAME_H_INCLUDED
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "position.h"
|
||||
|
@ -57,8 +57,6 @@ enum EndgameCode {
|
|||
KBPKB, // KBP vs KB
|
||||
KBPPKB, // KBPP vs KB
|
||||
KBPKN, // KBP vs KN
|
||||
KNPK, // KNP vs K
|
||||
KNPKB, // KNP vs KB
|
||||
KPKP // KP vs KP
|
||||
};
|
||||
|
||||
|
@ -91,17 +89,19 @@ struct Endgame : public EndgameBase<T> {
|
|||
};
|
||||
|
||||
|
||||
/// The Endgames class stores the pointers to endgame evaluation and scaling
|
||||
/// The Endgames namespace handles the pointers to endgame evaluation and scaling
|
||||
/// base objects in two std::map. We use polymorphism to invoke the actual
|
||||
/// endgame function by calling its virtual operator().
|
||||
|
||||
namespace Endgames {
|
||||
|
||||
template<typename T> using Ptr = std::unique_ptr<EndgameBase<T>>;
|
||||
template<typename T> using Map = std::map<Key, Ptr<T>>;
|
||||
|
||||
template<typename T> using Map = std::unordered_map<Key, Ptr<T>>;
|
||||
|
||||
extern std::pair<Map<Value>, Map<ScaleFactor>> maps;
|
||||
|
||||
void init();
|
||||
|
||||
template<typename T>
|
||||
Map<T>& map() {
|
||||
return std::get<std::is_same<T, ScaleFactor>::value>(maps);
|
||||
|
@ -117,10 +117,9 @@ namespace Endgames {
|
|||
|
||||
template<typename T>
|
||||
const EndgameBase<T>* probe(Key key) {
|
||||
return map<T>().count(key) ? map<T>()[key].get() : nullptr;
|
||||
auto it = map<T>().find(key);
|
||||
return it != map<T>().end() ? it->second.get() : nullptr;
|
||||
}
|
||||
|
||||
void init();
|
||||
}
|
||||
|
||||
#endif // #ifndef ENDGAME_H_INCLUDED
|
||||
|
|
438
src/evaluate.cpp
438
src/evaluate.cpp
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -80,81 +80,81 @@ namespace {
|
|||
constexpr Value SpaceThreshold = Value(12222);
|
||||
|
||||
// KingAttackWeights[PieceType] contains king attack weights by piece type
|
||||
constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 77, 55, 44, 10 };
|
||||
constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 };
|
||||
|
||||
// Penalties for enemy's safe checks
|
||||
constexpr int QueenSafeCheck = 780;
|
||||
constexpr int RookSafeCheck = 1080;
|
||||
constexpr int BishopSafeCheck = 635;
|
||||
constexpr int KnightSafeCheck = 790;
|
||||
constexpr int QueenSafeCheck = 772;
|
||||
constexpr int RookSafeCheck = 1084;
|
||||
constexpr int BishopSafeCheck = 645;
|
||||
constexpr int KnightSafeCheck = 792;
|
||||
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
|
||||
// MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game,
|
||||
// indexed by piece type and number of attacked squares in the mobility area.
|
||||
constexpr Score MobilityBonus[][32] = {
|
||||
{ S(-62,-81), S(-53,-56), S(-12,-30), S( -4,-14), S( 3, 8), S( 13, 15), // Knights
|
||||
S( 22, 23), S( 28, 27), S( 33, 33) },
|
||||
{ S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishops
|
||||
{ S(-62,-81), S(-53,-56), S(-12,-31), S( -4,-16), S( 3, 5), S( 13, 11), // Knight
|
||||
S( 22, 17), S( 28, 20), S( 33, 25) },
|
||||
{ S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishop
|
||||
S( 55, 54), S( 63, 57), S( 63, 65), S( 68, 73), S( 81, 78), S( 81, 86),
|
||||
S( 91, 88), S( 98, 97) },
|
||||
{ S(-58,-76), S(-27,-18), S(-15, 28), S(-10, 55), S( -5, 69), S( -2, 82), // Rooks
|
||||
S( 9,112), S( 16,118), S( 30,132), S( 29,142), S( 32,155), S( 38,165),
|
||||
S( 46,166), S( 48,169), S( 58,171) },
|
||||
{ S(-39,-36), S(-21,-15), S( 3, 8), S( 3, 18), S( 14, 34), S( 22, 54), // Queens
|
||||
S( 28, 61), S( 41, 73), S( 43, 79), S( 48, 92), S( 56, 94), S( 60,104),
|
||||
S( 60,113), S( 66,120), S( 67,123), S( 70,126), S( 71,133), S( 73,136),
|
||||
S( 79,140), S( 88,143), S( 88,148), S( 99,166), S(102,170), S(102,175),
|
||||
S(106,184), S(109,191), S(113,206), S(116,212) }
|
||||
{ S(-60,-78), S(-20,-17), S( 2, 23), S( 3, 39), S( 3, 70), S( 11, 99), // Rook
|
||||
S( 22,103), S( 31,121), S( 40,134), S( 40,139), S( 41,158), S( 48,164),
|
||||
S( 57,168), S( 57,169), S( 62,172) },
|
||||
{ S(-30,-48), S(-12,-30), S( -8, -7), S( -9, 19), S( 20, 40), S( 23, 55), // Queen
|
||||
S( 23, 59), S( 35, 75), S( 38, 78), S( 53, 96), S( 64, 96), S( 65,100),
|
||||
S( 65,121), S( 66,127), S( 67,131), S( 67,133), S( 72,136), S( 72,141),
|
||||
S( 77,147), S( 79,150), S( 93,151), S(108,168), S(108,168), S(108,171),
|
||||
S(110,182), S(114,182), S(114,192), S(116,219) }
|
||||
};
|
||||
|
||||
// RookOnFile[semiopen/open] contains bonuses for each rook when there is
|
||||
// no (friendly) pawn on the rook file.
|
||||
constexpr Score RookOnFile[] = { S(18, 7), S(44, 20) };
|
||||
constexpr Score RookOnFile[] = { S(19, 7), S(48, 29) };
|
||||
|
||||
// ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to
|
||||
// which piece type attacks which one. Attacks on lesser pieces which are
|
||||
// pawn-defended are not considered.
|
||||
constexpr Score ThreatByMinor[PIECE_TYPE_NB] = {
|
||||
S(0, 0), S(0, 31), S(39, 42), S(57, 44), S(68, 112), S(62, 120)
|
||||
S(0, 0), S(5, 32), S(57, 41), S(77, 56), S(88, 119), S(79, 161)
|
||||
};
|
||||
|
||||
constexpr Score ThreatByRook[PIECE_TYPE_NB] = {
|
||||
S(0, 0), S(0, 24), S(38, 71), S(38, 61), S(0, 38), S(51, 38)
|
||||
S(0, 0), S(3, 46), S(37, 68), S(42, 60), S(0, 38), S(58, 41)
|
||||
};
|
||||
|
||||
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn
|
||||
constexpr Score PassedRank[RANK_NB] = {
|
||||
S(0, 0), S(5, 18), S(12, 23), S(10, 31), S(57, 62), S(163, 167), S(271, 250)
|
||||
};
|
||||
|
||||
// PassedFile[File] contains a bonus according to the file of a passed pawn
|
||||
constexpr Score PassedFile[FILE_NB] = {
|
||||
S( -1, 7), S( 0, 9), S(-9, -8), S(-30,-14),
|
||||
S(-30,-14), S(-9, -8), S( 0, 9), S( -1, 7)
|
||||
S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
|
||||
};
|
||||
|
||||
// Assorted bonuses and penalties
|
||||
constexpr Score BishopPawns = S( 3, 7);
|
||||
constexpr Score CorneredBishop = S( 50, 50);
|
||||
constexpr Score FlankAttacks = S( 8, 0);
|
||||
constexpr Score Hanging = S( 69, 36);
|
||||
constexpr Score KingProtector = S( 7, 8);
|
||||
constexpr Score KnightOnQueen = S( 16, 12);
|
||||
constexpr Score LongDiagonalBishop = S( 45, 0);
|
||||
constexpr Score MinorBehindPawn = S( 18, 3);
|
||||
constexpr Score Outpost = S( 9, 3);
|
||||
constexpr Score PawnlessFlank = S( 17, 95);
|
||||
constexpr Score RestrictedPiece = S( 7, 7);
|
||||
constexpr Score RookOnPawn = S( 10, 32);
|
||||
constexpr Score SliderOnQueen = S( 59, 18);
|
||||
constexpr Score ThreatByKing = S( 24, 89);
|
||||
constexpr Score ThreatByPawnPush = S( 48, 39);
|
||||
constexpr Score ThreatByRank = S( 13, 0);
|
||||
constexpr Score ThreatBySafePawn = S(173, 94);
|
||||
constexpr Score TrappedRook = S( 47, 4);
|
||||
constexpr Score WeakQueen = S( 49, 15);
|
||||
constexpr Score WeakUnopposedPawn = S( 12, 23);
|
||||
constexpr Score BishopPawns = S( 3, 7);
|
||||
constexpr Score BishopOnKingRing = S( 24, 0);
|
||||
constexpr Score BishopXRayPawns = S( 4, 5);
|
||||
constexpr Score CorneredBishop = S( 50, 50);
|
||||
constexpr Score FlankAttacks = S( 8, 0);
|
||||
constexpr Score Hanging = S( 69, 36);
|
||||
constexpr Score BishopKingProtector = S( 6, 9);
|
||||
constexpr Score KnightKingProtector = S( 8, 9);
|
||||
constexpr Score KnightOnQueen = S( 16, 11);
|
||||
constexpr Score LongDiagonalBishop = S( 45, 0);
|
||||
constexpr Score MinorBehindPawn = S( 18, 3);
|
||||
constexpr Score KnightOutpost = S( 56, 36);
|
||||
constexpr Score BishopOutpost = S( 30, 23);
|
||||
constexpr Score ReachableOutpost = S( 31, 22);
|
||||
constexpr Score PassedFile = S( 11, 8);
|
||||
constexpr Score PawnlessFlank = S( 17, 95);
|
||||
constexpr Score RestrictedPiece = S( 7, 7);
|
||||
constexpr Score RookOnKingRing = S( 16, 0);
|
||||
constexpr Score RookOnQueenFile = S( 5, 9);
|
||||
constexpr Score SliderOnQueen = S( 59, 18);
|
||||
constexpr Score ThreatByKing = S( 24, 89);
|
||||
constexpr Score ThreatByPawnPush = S( 48, 39);
|
||||
constexpr Score ThreatBySafePawn = S(173, 94);
|
||||
constexpr Score TrappedRook = S( 55, 13);
|
||||
constexpr Score WeakQueen = S( 51, 14);
|
||||
constexpr Score WeakQueenProtection = S( 15, 0);
|
||||
|
||||
#undef S
|
||||
|
||||
|
@ -176,7 +176,7 @@ namespace {
|
|||
template<Color Us> Score passed() const;
|
||||
template<Color Us> Score space() const;
|
||||
ScaleFactor scale_factor(Value eg) const;
|
||||
Score initiative(Value eg) const;
|
||||
Score initiative(Score score) const;
|
||||
|
||||
const Position& pos;
|
||||
Material::Entry* me;
|
||||
|
@ -193,10 +193,8 @@ namespace {
|
|||
// color, including x-rays. But diagonal x-rays through pawns are not computed.
|
||||
Bitboard attackedBy2[COLOR_NB];
|
||||
|
||||
// kingRing[color] are the squares adjacent to the king, plus (only for a
|
||||
// king on its first rank) the squares two ranks in front. For instance,
|
||||
// if black's king is on g8, kingRing[BLACK] is f8, h8, f7, g7, h7, f6, g6
|
||||
// and h6.
|
||||
// kingRing[color] are the squares adjacent to the king plus some other
|
||||
// very near squares, depending on king position.
|
||||
Bitboard kingRing[COLOR_NB];
|
||||
|
||||
// kingAttackersCount[color] is the number of pieces of the given color
|
||||
|
@ -223,10 +221,10 @@ namespace {
|
|||
template<Tracing T> template<Color Us>
|
||||
void Evaluation<T>::initialize() {
|
||||
|
||||
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
|
||||
constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
|
||||
constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB: Rank7BB | Rank6BB);
|
||||
constexpr Color Them = ~Us;
|
||||
constexpr Direction Up = pawn_push(Us);
|
||||
constexpr Direction Down = -Up;
|
||||
constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB : Rank7BB | Rank6BB);
|
||||
|
||||
const Square ksq = pos.square<KING>(Us);
|
||||
|
||||
|
@ -235,26 +233,20 @@ namespace {
|
|||
// Find our pawns that are blocked or on the first two ranks
|
||||
Bitboard b = pos.pieces(Us, PAWN) & (shift<Down>(pos.pieces()) | LowRanks);
|
||||
|
||||
// Squares occupied by those pawns, by our king or queen or controlled by
|
||||
// enemy pawns are excluded from the mobility area.
|
||||
mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them));
|
||||
// Squares occupied by those pawns, by our king or queen, by blockers to attacks on our king
|
||||
// or controlled by enemy pawns are excluded from the mobility area.
|
||||
mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pos.blockers_for_king(Us) | pe->pawn_attacks(Them));
|
||||
|
||||
// Initialize attackedBy[] for king and pawns
|
||||
attackedBy[Us][KING] = pos.attacks_from<KING>(ksq);
|
||||
attackedBy[Us][KING] = attacks_bb<KING>(ksq);
|
||||
attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
|
||||
attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
|
||||
attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
|
||||
|
||||
// Init our king safety tables
|
||||
kingRing[Us] = attackedBy[Us][KING];
|
||||
if (relative_rank(Us, ksq) == RANK_1)
|
||||
kingRing[Us] |= shift<Up>(kingRing[Us]);
|
||||
|
||||
if (file_of(ksq) == FILE_H)
|
||||
kingRing[Us] |= shift<WEST>(kingRing[Us]);
|
||||
|
||||
else if (file_of(ksq) == FILE_A)
|
||||
kingRing[Us] |= shift<EAST>(kingRing[Us]);
|
||||
Square s = make_square(Utility::clamp(file_of(ksq), FILE_B, FILE_G),
|
||||
Utility::clamp(rank_of(ksq), RANK_2, RANK_7));
|
||||
kingRing[Us] = attacks_bb<KING>(s) | s;
|
||||
|
||||
kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
|
||||
kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
|
||||
|
@ -268,8 +260,8 @@ namespace {
|
|||
template<Tracing T> template<Color Us, PieceType Pt>
|
||||
Score Evaluation<T>::pieces() {
|
||||
|
||||
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
|
||||
constexpr Color Them = ~Us;
|
||||
constexpr Direction Down = -pawn_push(Us);
|
||||
constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
|
||||
: Rank5BB | Rank4BB | Rank3BB);
|
||||
const Square* pl = pos.squares<Pt>(Us);
|
||||
|
@ -284,7 +276,7 @@ namespace {
|
|||
// Find attacked squares, including x-ray attacks for bishops and rooks
|
||||
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))
|
||||
: pos.attacks_from<Pt>(s);
|
||||
: attacks_bb<Pt>(s, pos.pieces());
|
||||
|
||||
if (pos.blockers_for_king(Us) & s)
|
||||
b &= LineBB[pos.square<KING>(Us)][s];
|
||||
|
@ -300,6 +292,12 @@ namespace {
|
|||
kingAttacksCount[Us] += popcount(b & attackedBy[Them][KING]);
|
||||
}
|
||||
|
||||
else if (Pt == ROOK && (file_bb(s) & kingRing[Them]))
|
||||
score += RookOnKingRing;
|
||||
|
||||
else if (Pt == BISHOP && (attacks_bb<BISHOP>(s, pos.pieces(PAWN)) & kingRing[Them]))
|
||||
score += BishopOnKingRing;
|
||||
|
||||
int mob = popcount(b & mobilityArea[Us]);
|
||||
|
||||
mobility[Us] += MobilityBonus[Pt - 2][mob];
|
||||
|
@ -307,60 +305,61 @@ namespace {
|
|||
if (Pt == BISHOP || Pt == KNIGHT)
|
||||
{
|
||||
// Bonus if piece is on an outpost square or can reach one
|
||||
bb = OutpostRanks & ~pe->pawn_attacks_span(Them);
|
||||
bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them);
|
||||
if (bb & s)
|
||||
score += Outpost * (Pt == KNIGHT ? 4 : 2)
|
||||
* ((attackedBy[Us][PAWN] & s) ? 2 : 1);
|
||||
score += (Pt == KNIGHT) ? KnightOutpost : BishopOutpost;
|
||||
else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
|
||||
score += ReachableOutpost;
|
||||
|
||||
else if (bb &= b & ~pos.pieces(Us))
|
||||
score += Outpost * (Pt == KNIGHT ? 2 : 1)
|
||||
* ((attackedBy[Us][PAWN] & bb) ? 2 : 1);
|
||||
|
||||
// Knight and Bishop bonus for being right behind a pawn
|
||||
// Bonus for a knight or bishop shielded by pawn
|
||||
if (shift<Down>(pos.pieces(PAWN)) & s)
|
||||
score += MinorBehindPawn;
|
||||
|
||||
// Penalty if the piece is far from the king
|
||||
score -= KingProtector * distance(s, pos.square<KING>(Us));
|
||||
score -= (Pt == KNIGHT ? KnightKingProtector
|
||||
: BishopKingProtector) * distance(pos.square<KING>(Us), s);
|
||||
|
||||
if (Pt == BISHOP)
|
||||
{
|
||||
// Penalty according to number of pawns on the same color square as the
|
||||
// bishop, bigger when the center files are blocked with pawns.
|
||||
// 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
|
||||
// when the bishop is outside the pawn chain.
|
||||
Bitboard blocked = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces());
|
||||
|
||||
score -= BishopPawns * pos.pawns_on_same_color_squares(Us, s)
|
||||
* (1 + popcount(blocked & CenterFiles));
|
||||
* (!(attackedBy[Us][PAWN] & s) + popcount(blocked & CenterFiles));
|
||||
|
||||
// Penalty for all enemy pawns x-rayed
|
||||
score -= BishopXRayPawns * popcount(attacks_bb<BISHOP>(s) & pos.pieces(Them, PAWN));
|
||||
|
||||
// Bonus for bishop on a long diagonal which can "see" both center squares
|
||||
if (more_than_one(attacks_bb<BISHOP>(s, pos.pieces(PAWN)) & Center))
|
||||
score += LongDiagonalBishop;
|
||||
}
|
||||
|
||||
// An important Chess960 pattern: A cornered bishop blocked by a friendly
|
||||
// pawn diagonally in front of it is a very serious problem, especially
|
||||
// when that pawn is also blocked.
|
||||
if ( Pt == BISHOP
|
||||
&& pos.is_chess960()
|
||||
&& (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
|
||||
{
|
||||
Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST);
|
||||
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
|
||||
score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4
|
||||
: pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? CorneredBishop * 2
|
||||
: CorneredBishop;
|
||||
// An important Chess960 pattern: a cornered bishop blocked by a friendly
|
||||
// pawn diagonally in front of it is a very serious problem, especially
|
||||
// when that pawn is also blocked.
|
||||
if ( pos.is_chess960()
|
||||
&& (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
|
||||
{
|
||||
Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST);
|
||||
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
|
||||
score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4
|
||||
: pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? CorneredBishop * 2
|
||||
: CorneredBishop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Pt == ROOK)
|
||||
{
|
||||
// Bonus for aligning rook with enemy pawns on the same rank/file
|
||||
if (relative_rank(Us, s) >= RANK_5)
|
||||
score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]);
|
||||
// Bonus for rook on the same file as a queen
|
||||
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))
|
||||
score += RookOnFile[bool(pos.is_on_semiopen_file(Them, 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)
|
||||
|
@ -390,11 +389,11 @@ namespace {
|
|||
template<Tracing T> template<Color Us>
|
||||
Score Evaluation<T>::king() const {
|
||||
|
||||
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
constexpr Color Them = ~Us;
|
||||
constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB
|
||||
: AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB);
|
||||
|
||||
Bitboard weak, b1, b2, safe, unsafeChecks = 0;
|
||||
Bitboard weak, b1, b2, b3, safe, unsafeChecks = 0;
|
||||
Bitboard rookChecks, queenChecks, bishopChecks, knightChecks;
|
||||
int kingDanger = 0;
|
||||
const Square ksq = pos.square<KING>(Us);
|
||||
|
@ -416,9 +415,9 @@ namespace {
|
|||
|
||||
// Enemy rooks checks
|
||||
rookChecks = b1 & safe & attackedBy[Them][ROOK];
|
||||
|
||||
if (rookChecks)
|
||||
kingDanger += RookSafeCheck;
|
||||
kingDanger += more_than_one(rookChecks) ? RookSafeCheck * 175/100
|
||||
: RookSafeCheck;
|
||||
else
|
||||
unsafeChecks |= b1 & attackedBy[Them][ROOK];
|
||||
|
||||
|
@ -429,9 +428,9 @@ namespace {
|
|||
& safe
|
||||
& ~attackedBy[Us][QUEEN]
|
||||
& ~rookChecks;
|
||||
|
||||
if (queenChecks)
|
||||
kingDanger += QueenSafeCheck;
|
||||
kingDanger += more_than_one(queenChecks) ? QueenSafeCheck * 145/100
|
||||
: QueenSafeCheck;
|
||||
|
||||
// Enemy bishops checks: we count them only if they are from squares from
|
||||
// which we can't give a queen check, because queen checks are more valuable.
|
||||
|
@ -439,42 +438,41 @@ namespace {
|
|||
& attackedBy[Them][BISHOP]
|
||||
& safe
|
||||
& ~queenChecks;
|
||||
|
||||
if (bishopChecks)
|
||||
kingDanger += BishopSafeCheck;
|
||||
kingDanger += more_than_one(bishopChecks) ? BishopSafeCheck * 3/2
|
||||
: BishopSafeCheck;
|
||||
else
|
||||
unsafeChecks |= b2 & attackedBy[Them][BISHOP];
|
||||
|
||||
// Enemy knights checks
|
||||
knightChecks = pos.attacks_from<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
|
||||
|
||||
knightChecks = attacks_bb<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
|
||||
if (knightChecks & safe)
|
||||
kingDanger += KnightSafeCheck;
|
||||
kingDanger += more_than_one(knightChecks & safe) ? KnightSafeCheck * 162/100
|
||||
: KnightSafeCheck;
|
||||
else
|
||||
unsafeChecks |= knightChecks;
|
||||
|
||||
// Unsafe or occupied checking squares will also be considered, as long as
|
||||
// the square is in the attacker's mobility area.
|
||||
unsafeChecks &= mobilityArea[Them];
|
||||
|
||||
// Find the squares that opponent attacks in our king flank, and the squares
|
||||
// which are attacked twice in that flank.
|
||||
// Find the squares that opponent attacks in our king flank, the squares
|
||||
// which they attack twice in that flank, and the squares that we defend.
|
||||
b1 = attackedBy[Them][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
|
||||
b2 = b1 & attackedBy2[Them];
|
||||
b3 = attackedBy[Us][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
|
||||
|
||||
int kingFlankAttacks = popcount(b1) + popcount(b2);
|
||||
int kingFlankAttack = popcount(b1) + popcount(b2);
|
||||
int kingFlankDefense = popcount(b3);
|
||||
|
||||
kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
|
||||
+ 69 * kingAttacksCount[Them]
|
||||
+ 185 * popcount(kingRing[Us] & weak)
|
||||
- 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
|
||||
- 35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING])
|
||||
+ 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks)
|
||||
- 873 * !pos.count<QUEEN>(Them)
|
||||
- 6 * mg_value(score) / 8
|
||||
+ 148 * popcount(unsafeChecks)
|
||||
+ 98 * popcount(pos.blockers_for_king(Us))
|
||||
+ 69 * kingAttacksCount[Them]
|
||||
+ 3 * kingFlankAttack * kingFlankAttack / 8
|
||||
+ mg_value(mobility[Them] - mobility[Us])
|
||||
+ 5 * kingFlankAttacks * kingFlankAttacks / 16
|
||||
- 7;
|
||||
- 873 * !pos.count<QUEEN>(Them)
|
||||
- 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
|
||||
- 6 * mg_value(score) / 8
|
||||
- 4 * kingFlankDefense
|
||||
+ 37;
|
||||
|
||||
// Transform the kingDanger units into a Score, and subtract it from the evaluation
|
||||
if (kingDanger > 100)
|
||||
|
@ -485,7 +483,7 @@ namespace {
|
|||
score -= PawnlessFlank;
|
||||
|
||||
// Penalty if king flank is under attack, potentially moving toward the king
|
||||
score -= FlankAttacks * kingFlankAttacks;
|
||||
score -= FlankAttacks * kingFlankAttack;
|
||||
|
||||
if (T)
|
||||
Trace::add(KING, Us, score);
|
||||
|
@ -499,8 +497,8 @@ namespace {
|
|||
template<Tracing T> template<Color Us>
|
||||
Score Evaluation<T>::threats() const {
|
||||
|
||||
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
|
||||
constexpr Color Them = ~Us;
|
||||
constexpr Direction Up = pawn_push(Us);
|
||||
constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
|
||||
|
||||
Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe;
|
||||
|
@ -520,29 +518,16 @@ namespace {
|
|||
// Enemies not strongly protected and under our attack
|
||||
weak = pos.pieces(Them) & ~stronglyProtected & attackedBy[Us][ALL_PIECES];
|
||||
|
||||
// Safe or protected squares
|
||||
safe = ~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES];
|
||||
|
||||
// Bonus according to the kind of attacking pieces
|
||||
if (defended | weak)
|
||||
{
|
||||
b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]);
|
||||
while (b)
|
||||
{
|
||||
Square s = pop_lsb(&b);
|
||||
score += ThreatByMinor[type_of(pos.piece_on(s))];
|
||||
if (type_of(pos.piece_on(s)) != PAWN)
|
||||
score += ThreatByRank * (int)relative_rank(Them, s);
|
||||
}
|
||||
score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(&b)))];
|
||||
|
||||
b = weak & attackedBy[Us][ROOK];
|
||||
while (b)
|
||||
{
|
||||
Square s = pop_lsb(&b);
|
||||
score += ThreatByRook[type_of(pos.piece_on(s))];
|
||||
if (type_of(pos.piece_on(s)) != PAWN)
|
||||
score += ThreatByRank * (int)relative_rank(Them, s);
|
||||
}
|
||||
score += ThreatByRook[type_of(pos.piece_on(pop_lsb(&b)))];
|
||||
|
||||
if (weak & attackedBy[Us][KING])
|
||||
score += ThreatByKing;
|
||||
|
@ -550,18 +535,24 @@ namespace {
|
|||
b = ~attackedBy[Them][ALL_PIECES]
|
||||
| (nonPawnEnemies & attackedBy2[Us]);
|
||||
score += Hanging * popcount(weak & b);
|
||||
|
||||
// Additional bonus if weak piece is only protected by a queen
|
||||
score += WeakQueenProtection * popcount(weak & attackedBy[Them][QUEEN]);
|
||||
}
|
||||
|
||||
// Bonus for restricting their piece moves
|
||||
b = attackedBy[Them][ALL_PIECES]
|
||||
& ~stronglyProtected
|
||||
& attackedBy[Us][ALL_PIECES];
|
||||
|
||||
score += RestrictedPiece * popcount(b);
|
||||
|
||||
// Bonus for enemy unopposed weak pawns
|
||||
if (pos.pieces(Us, ROOK, QUEEN))
|
||||
score += WeakUnopposedPawn * pe->weak_unopposed(Them);
|
||||
// Protected or unattacked squares
|
||||
safe = ~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES];
|
||||
|
||||
// Bonus for attacking enemy pieces with our relatively safe pawns
|
||||
b = pos.pieces(Us, PAWN) & safe;
|
||||
b = pawn_attacks_bb<Us>(b) & nonPawnEnemies;
|
||||
score += ThreatBySafePawn * popcount(b);
|
||||
|
||||
// Find squares where our pawns can push on the next move
|
||||
b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces();
|
||||
|
@ -571,14 +562,8 @@ namespace {
|
|||
b &= ~attackedBy[Them][PAWN] & safe;
|
||||
|
||||
// Bonus for safe pawn threats on the next move
|
||||
b = pawn_attacks_bb<Us>(b) & pos.pieces(Them);
|
||||
score += ThreatByPawnPush * popcount(b);
|
||||
|
||||
// Our safe or protected pawns
|
||||
b = pos.pieces(Us, PAWN) & safe;
|
||||
|
||||
b = pawn_attacks_bb<Us>(b) & nonPawnEnemies;
|
||||
score += ThreatBySafePawn * popcount(b);
|
||||
score += ThreatByPawnPush * popcount(b);
|
||||
|
||||
// Bonus for threats on the next moves against enemy queen
|
||||
if (pos.count<QUEEN>(Them) == 1)
|
||||
|
@ -586,12 +571,12 @@ namespace {
|
|||
Square s = pos.square<QUEEN>(Them);
|
||||
safe = mobilityArea[Us] & ~stronglyProtected;
|
||||
|
||||
b = attackedBy[Us][KNIGHT] & pos.attacks_from<KNIGHT>(s);
|
||||
b = attackedBy[Us][KNIGHT] & attacks_bb<KNIGHT>(s);
|
||||
|
||||
score += KnightOnQueen * popcount(b & safe);
|
||||
|
||||
b = (attackedBy[Us][BISHOP] & pos.attacks_from<BISHOP>(s))
|
||||
| (attackedBy[Us][ROOK ] & pos.attacks_from<ROOK >(s));
|
||||
b = (attackedBy[Us][BISHOP] & attacks_bb<BISHOP>(s, pos.pieces()))
|
||||
| (attackedBy[Us][ROOK ] & attacks_bb<ROOK >(s, pos.pieces()));
|
||||
|
||||
score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]);
|
||||
}
|
||||
|
@ -608,18 +593,32 @@ namespace {
|
|||
template<Tracing T> template<Color Us>
|
||||
Score Evaluation<T>::passed() const {
|
||||
|
||||
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
|
||||
constexpr Color Them = ~Us;
|
||||
constexpr Direction Up = pawn_push(Us);
|
||||
constexpr Direction Down = -Up;
|
||||
|
||||
auto king_proximity = [&](Color c, Square s) {
|
||||
return std::min(distance(pos.square<KING>(c), s), 5);
|
||||
};
|
||||
|
||||
Bitboard b, bb, squaresToQueen, defendedSquares, unsafeSquares;
|
||||
Bitboard b, bb, squaresToQueen, unsafeSquares, blockedPassers, helpers;
|
||||
Score score = SCORE_ZERO;
|
||||
|
||||
b = pe->passed_pawns(Us);
|
||||
|
||||
blockedPassers = b & shift<Down>(pos.pieces(Them, PAWN));
|
||||
if (blockedPassers)
|
||||
{
|
||||
helpers = shift<Up>(pos.pieces(Us, PAWN))
|
||||
& ~pos.pieces(Them)
|
||||
& (~attackedBy2[Them] | attackedBy[Us][ALL_PIECES]);
|
||||
|
||||
// Remove blocked candidate passers that don't have help to pass
|
||||
b &= ~blockedPassers
|
||||
| shift<WEST>(helpers)
|
||||
| shift<EAST>(helpers);
|
||||
}
|
||||
|
||||
while (b)
|
||||
{
|
||||
Square s = pop_lsb(&b);
|
||||
|
@ -632,12 +631,12 @@ namespace {
|
|||
|
||||
if (r > RANK_3)
|
||||
{
|
||||
int w = (r-2) * (r-2) + 2;
|
||||
int w = 5 * r - 13;
|
||||
Square blockSq = s + Up;
|
||||
|
||||
// Adjust bonus based on the king's proximity
|
||||
bonus += make_score(0, ( king_proximity(Them, blockSq) * 5
|
||||
- king_proximity(Us, blockSq) * 2) * w);
|
||||
bonus += make_score(0, ( (king_proximity(Them, blockSq) * 19) / 4
|
||||
- king_proximity(Us, blockSq) * 2) * w);
|
||||
|
||||
// If blockSq is not the queening square then consider also a second push
|
||||
if (r != RANK_7)
|
||||
|
@ -646,42 +645,31 @@ namespace {
|
|||
// If the pawn is free to advance, then increase the bonus
|
||||
if (pos.empty(blockSq))
|
||||
{
|
||||
// If there is a rook or queen attacking/defending the pawn from behind,
|
||||
// consider all the squaresToQueen. Otherwise consider only the squares
|
||||
// in the pawn's path attacked or occupied by the enemy.
|
||||
defendedSquares = unsafeSquares = squaresToQueen = forward_file_bb(Us, s);
|
||||
squaresToQueen = forward_file_bb(Us, s);
|
||||
unsafeSquares = passed_pawn_span(Us, s);
|
||||
|
||||
bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN);
|
||||
|
||||
if (!(pos.pieces(Us) & bb))
|
||||
defendedSquares &= attackedBy[Us][ALL_PIECES];
|
||||
|
||||
if (!(pos.pieces(Them) & bb))
|
||||
unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
|
||||
unsafeSquares &= attackedBy[Them][ALL_PIECES];
|
||||
|
||||
// If there aren't any enemy attacks, assign a big bonus. Otherwise
|
||||
// assign a smaller bonus if the block square isn't attacked.
|
||||
int k = !unsafeSquares ? 20 : !(unsafeSquares & blockSq) ? 9 : 0;
|
||||
// If there are no enemy attacks on passed pawn span, assign a big bonus.
|
||||
// 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.
|
||||
int k = !unsafeSquares ? 35 :
|
||||
!(unsafeSquares & squaresToQueen) ? 20 :
|
||||
!(unsafeSquares & blockSq) ? 9 :
|
||||
0 ;
|
||||
|
||||
// If the path to the queen is fully defended, assign a big bonus.
|
||||
// Otherwise assign a smaller bonus if the block square is defended.
|
||||
if (defendedSquares == squaresToQueen)
|
||||
k += 6;
|
||||
|
||||
else if (defendedSquares & blockSq)
|
||||
k += 4;
|
||||
// Assign a larger bonus if the block square is defended
|
||||
if ((pos.pieces(Us) & bb) || (attackedBy[Us][ALL_PIECES] & blockSq))
|
||||
k += 5;
|
||||
|
||||
bonus += make_score(k * w, k * w);
|
||||
}
|
||||
} // r > RANK_3
|
||||
|
||||
// Scale down bonus for candidate passers which need more than one
|
||||
// pawn push to become passed, or have a pawn in front of them.
|
||||
if ( !pos.pawn_passed(Us, s + Up)
|
||||
|| (pos.pieces(PAWN) & forward_file_bb(Us, s)))
|
||||
bonus = bonus / 2;
|
||||
|
||||
score += bonus + PassedFile[file_of(s)];
|
||||
score += bonus - PassedFile * edge_distance(file_of(s));
|
||||
}
|
||||
|
||||
if (T)
|
||||
|
@ -704,8 +692,8 @@ namespace {
|
|||
if (pos.non_pawn_material() < SpaceThreshold)
|
||||
return SCORE_ZERO;
|
||||
|
||||
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
|
||||
constexpr Color Them = ~Us;
|
||||
constexpr Direction Down = -pawn_push(Us);
|
||||
constexpr Bitboard SpaceMask =
|
||||
Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB)
|
||||
: CenterFiles & (Rank7BB | Rank6BB | Rank5BB);
|
||||
|
@ -720,8 +708,8 @@ namespace {
|
|||
behind |= shift<Down>(behind);
|
||||
behind |= shift<Down+Down>(behind);
|
||||
|
||||
int bonus = popcount(safe) + popcount(behind & safe);
|
||||
int weight = pos.count<ALL_PIECES>(Us) - 1;
|
||||
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);
|
||||
Score score = make_score(bonus * weight * weight / 16, 0);
|
||||
|
||||
if (T)
|
||||
|
@ -736,7 +724,7 @@ namespace {
|
|||
// known attacking/defending status of the players.
|
||||
|
||||
template<Tracing T>
|
||||
Score Evaluation<T>::initiative(Value eg) const {
|
||||
Score Evaluation<T>::initiative(Score score) const {
|
||||
|
||||
int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
|
||||
- distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
|
||||
|
@ -744,23 +732,36 @@ namespace {
|
|||
bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide)
|
||||
&& (pos.pieces(PAWN) & KingSide);
|
||||
|
||||
bool almostUnwinnable = outflanking < 0
|
||||
&& !pawnsOnBothFlanks;
|
||||
|
||||
bool infiltration = rank_of(pos.square<KING>(WHITE)) > RANK_4
|
||||
|| rank_of(pos.square<KING>(BLACK)) < RANK_5;
|
||||
|
||||
// Compute the initiative bonus for the attacking side
|
||||
int complexity = 9 * pe->passed_count()
|
||||
+ 11 * pos.count<PAWN>()
|
||||
+ 12 * pos.count<PAWN>()
|
||||
+ 9 * outflanking
|
||||
+ 18 * pawnsOnBothFlanks
|
||||
+ 49 * !pos.non_pawn_material()
|
||||
-103 ;
|
||||
+ 21 * pawnsOnBothFlanks
|
||||
+ 24 * infiltration
|
||||
+ 51 * !pos.non_pawn_material()
|
||||
- 43 * almostUnwinnable
|
||||
- 2 * pos.rule50_count()
|
||||
-110 ;
|
||||
|
||||
// Now apply the bonus: note that we find the attacking side by extracting
|
||||
// the sign of the endgame value, and that we carefully cap the bonus so
|
||||
// that the endgame score will never change sign after the bonus.
|
||||
Value mg = mg_value(score);
|
||||
Value eg = eg_value(score);
|
||||
|
||||
// Now apply the bonus: note that we find the attacking side by extracting the
|
||||
// sign of the midgame or endgame values, and that we carefully cap the bonus
|
||||
// so that the midgame and endgame scores do not change sign after the bonus.
|
||||
int u = ((mg > 0) - (mg < 0)) * Utility::clamp(complexity + 50, -abs(mg), 0);
|
||||
int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg));
|
||||
|
||||
if (T)
|
||||
Trace::add(INITIATIVE, make_score(0, v));
|
||||
Trace::add(INITIATIVE, make_score(u, v));
|
||||
|
||||
return make_score(0, v);
|
||||
return make_score(u, v);
|
||||
}
|
||||
|
||||
|
||||
|
@ -775,12 +776,16 @@ namespace {
|
|||
// If scale is not already specific, scale down the endgame via general heuristics
|
||||
if (sf == SCALE_FACTOR_NORMAL)
|
||||
{
|
||||
if ( pos.opposite_bishops()
|
||||
&& pos.non_pawn_material() == 2 * BishopValueMg)
|
||||
sf = 16 + 4 * pe->passed_count();
|
||||
if (pos.opposite_bishops())
|
||||
{
|
||||
if ( pos.non_pawn_material(WHITE) == BishopValueMg
|
||||
&& pos.non_pawn_material(BLACK) == BishopValueMg)
|
||||
sf = 18 + 4 * popcount(pe->passed_pawns(strongSide));
|
||||
else
|
||||
sf = 22 + 3 * pos.count<ALL_PIECES>(strongSide);
|
||||
}
|
||||
else
|
||||
sf = std::min(40 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide), sf);
|
||||
|
||||
sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide));
|
||||
}
|
||||
|
||||
return ScaleFactor(sf);
|
||||
|
@ -815,7 +820,7 @@ namespace {
|
|||
|
||||
// Early exit if score is high
|
||||
Value v = (mg_value(score) + eg_value(score)) / 2;
|
||||
if (abs(v) > (LazyThreshold + pos.non_pawn_material() / 64))
|
||||
if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64)
|
||||
return pos.side_to_move() == WHITE ? v : -v;
|
||||
|
||||
// Main evaluation begins here
|
||||
|
@ -823,7 +828,8 @@ namespace {
|
|||
initialize<WHITE>();
|
||||
initialize<BLACK>();
|
||||
|
||||
// Pieces should be evaluated first (populate attack tables)
|
||||
// Pieces evaluated first (also populates attackedBy, attackedBy2).
|
||||
// Note that the order of evaluation of the terms is left unspecified
|
||||
score += pieces<WHITE, KNIGHT>() - pieces<BLACK, KNIGHT>()
|
||||
+ pieces<WHITE, BISHOP>() - pieces<BLACK, BISHOP>()
|
||||
+ pieces<WHITE, ROOK >() - pieces<BLACK, ROOK >()
|
||||
|
@ -831,12 +837,13 @@ namespace {
|
|||
|
||||
score += mobility[WHITE] - mobility[BLACK];
|
||||
|
||||
// More complex interactions that require fully populated attack bitboards
|
||||
score += king< WHITE>() - king< BLACK>()
|
||||
+ threats<WHITE>() - threats<BLACK>()
|
||||
+ passed< WHITE>() - passed< BLACK>()
|
||||
+ space< WHITE>() - space< BLACK>();
|
||||
|
||||
score += initiative(eg_value(score));
|
||||
score += initiative(score);
|
||||
|
||||
// Interpolate between a middlegame and a (scaled by 'sf') endgame score
|
||||
ScaleFactor sf = scale_factor(eg_value(score));
|
||||
|
@ -855,8 +862,8 @@ namespace {
|
|||
Trace::add(TOTAL, score);
|
||||
}
|
||||
|
||||
return (pos.side_to_move() == WHITE ? v : -v) // Side to move point of view
|
||||
+ Eval::Tempo;
|
||||
// Side to move point of view
|
||||
return (pos.side_to_move() == WHITE ? v : -v) + Tempo;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -878,6 +885,9 @@ Value Eval::evaluate(const Position& pos) {
|
|||
|
||||
std::string Eval::trace(const Position& pos) {
|
||||
|
||||
if (pos.checkers())
|
||||
return "Total evaluation: none (in check)";
|
||||
|
||||
std::memset(scores, 0, sizeof(scores));
|
||||
|
||||
pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -29,8 +29,6 @@ class Position;
|
|||
|
||||
namespace Eval {
|
||||
|
||||
constexpr Value Tempo = Value(28); // Must be visible to search
|
||||
|
||||
std::string trace(const Position& pos);
|
||||
|
||||
Value evaluate(const Position& pos);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -21,12 +21,12 @@
|
|||
#include <iostream>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "endgame.h"
|
||||
#include "position.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
#include "tt.h"
|
||||
#include "uci.h"
|
||||
#include "endgame.h"
|
||||
#include "syzygy/tbprobe.h"
|
||||
|
||||
namespace PSQT {
|
||||
|
@ -38,13 +38,13 @@ int main(int argc, char* argv[]) {
|
|||
std::cout << engine_info() << std::endl;
|
||||
|
||||
UCI::init(Options);
|
||||
Tune::init();
|
||||
PSQT::init();
|
||||
Bitboards::init();
|
||||
Position::init();
|
||||
Bitbases::init();
|
||||
Search::init();
|
||||
Endgames::init();
|
||||
Threads.set(Options["Threads"]);
|
||||
Threads.set(size_t(Options["Threads"]));
|
||||
Search::clear(); // After threads are up
|
||||
|
||||
UCI::loop(argc, argv);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -84,7 +84,7 @@ namespace {
|
|||
template<Color Us>
|
||||
int imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
|
||||
|
||||
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
constexpr Color Them = ~Us;
|
||||
|
||||
int bonus = 0;
|
||||
|
||||
|
@ -129,7 +129,7 @@ Entry* probe(const Position& pos) {
|
|||
|
||||
Value npm_w = pos.non_pawn_material(WHITE);
|
||||
Value npm_b = pos.non_pawn_material(BLACK);
|
||||
Value npm = clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
|
||||
Value npm = Utility::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
|
||||
|
||||
// Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME]
|
||||
e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
|
||||
|
@ -140,7 +140,7 @@ Entry* probe(const Position& pos) {
|
|||
if ((e->evaluationFunction = Endgames::probe<Value>(key)) != nullptr)
|
||||
return e;
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (Color c : { WHITE, BLACK })
|
||||
if (is_KXK(pos, c))
|
||||
{
|
||||
e->evaluationFunction = &EvaluateKXK[c];
|
||||
|
@ -160,7 +160,7 @@ Entry* probe(const Position& pos) {
|
|||
// We didn't find any specialized scaling function, so fall back on generic
|
||||
// ones that refer to more than one material distribution. Note that in this
|
||||
// case we don't return after setting the function.
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (Color c : { WHITE, BLACK })
|
||||
{
|
||||
if (is_KBPsK(pos, c))
|
||||
e->scalingFunction[c] = &ScaleKBPsK[c];
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
213
src/misc.cpp
213
src/misc.cpp
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -48,6 +48,11 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
|
|||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "misc.h"
|
||||
#include "thread.h"
|
||||
|
||||
|
@ -103,6 +108,13 @@ public:
|
|||
if (!fname.empty() && !l.file.is_open())
|
||||
{
|
||||
l.file.open(fname, ifstream::out);
|
||||
|
||||
if (!l.file.is_open())
|
||||
{
|
||||
cerr << "Unable to open debug log file " << fname << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
cin.rdbuf(&l.in);
|
||||
cout.rdbuf(&l.out);
|
||||
}
|
||||
|
@ -145,8 +157,79 @@ const string engine_info(bool to_uci) {
|
|||
}
|
||||
|
||||
|
||||
/// compiler_info() returns a string trying to describe the compiler we use
|
||||
|
||||
const std::string compiler_info() {
|
||||
|
||||
#define stringify2(x) #x
|
||||
#define stringify(x) stringify2(x)
|
||||
#define make_version_string(major, minor, patch) stringify(major) "." stringify(minor) "." stringify(patch)
|
||||
|
||||
/// Predefined macros hell:
|
||||
///
|
||||
/// __GNUC__ Compiler is gcc, Clang or Intel on Linux
|
||||
/// __INTEL_COMPILER Compiler is Intel
|
||||
/// _MSC_VER Compiler is MSVC or Intel on Windows
|
||||
/// _WIN32 Building on Windows (any)
|
||||
/// _WIN64 Building on Windows 64 bit
|
||||
|
||||
std::string compiler = "\nCompiled by ";
|
||||
|
||||
#ifdef __clang__
|
||||
compiler += "clang++ ";
|
||||
compiler += make_version_string(__clang_major__, __clang_minor__, __clang_patchlevel__);
|
||||
#elif __INTEL_COMPILER
|
||||
compiler += "Intel compiler ";
|
||||
compiler += "(version ";
|
||||
compiler += stringify(__INTEL_COMPILER) " update " stringify(__INTEL_COMPILER_UPDATE);
|
||||
compiler += ")";
|
||||
#elif _MSC_VER
|
||||
compiler += "MSVC ";
|
||||
compiler += "(version ";
|
||||
compiler += stringify(_MSC_FULL_VER) "." stringify(_MSC_BUILD);
|
||||
compiler += ")";
|
||||
#elif __GNUC__
|
||||
compiler += "g++ (GNUC) ";
|
||||
compiler += make_version_string(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
|
||||
#else
|
||||
compiler += "Unknown compiler ";
|
||||
compiler += "(unknown version)";
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
compiler += " on Apple";
|
||||
#elif defined(__CYGWIN__)
|
||||
compiler += " on Cygwin";
|
||||
#elif defined(__MINGW64__)
|
||||
compiler += " on MinGW64";
|
||||
#elif defined(__MINGW32__)
|
||||
compiler += " on MinGW32";
|
||||
#elif defined(__ANDROID__)
|
||||
compiler += " on Android";
|
||||
#elif defined(__linux__)
|
||||
compiler += " on Linux";
|
||||
#elif defined(_WIN64)
|
||||
compiler += " on Microsoft Windows 64-bit";
|
||||
#elif defined(_WIN32)
|
||||
compiler += " on Microsoft Windows 32-bit";
|
||||
#else
|
||||
compiler += " on unknown system";
|
||||
#endif
|
||||
|
||||
compiler += "\n __VERSION__ macro expands to: ";
|
||||
#ifdef __VERSION__
|
||||
compiler += __VERSION__;
|
||||
#else
|
||||
compiler += "(undefined macro)";
|
||||
#endif
|
||||
compiler += "\n";
|
||||
|
||||
return compiler;
|
||||
}
|
||||
|
||||
|
||||
/// Debug functions used mainly to collect run-time statistics
|
||||
static int64_t hits[2], means[2];
|
||||
static std::atomic<int64_t> hits[2], means[2];
|
||||
|
||||
void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; }
|
||||
void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); }
|
||||
|
@ -169,7 +252,7 @@ void dbg_print() {
|
|||
|
||||
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
|
||||
|
||||
static Mutex m;
|
||||
static std::mutex m;
|
||||
|
||||
if (sc == IO_LOCK)
|
||||
m.lock();
|
||||
|
@ -211,6 +294,130 @@ void prefetch(void* addr) {
|
|||
|
||||
#endif
|
||||
|
||||
|
||||
/// aligned_ttmem_alloc will return suitably aligned memory, and if possible use large pages.
|
||||
/// The returned pointer is the aligned one, while the mem argument is the one that needs to be passed to free.
|
||||
/// With c++17 some of this functionality can be simplified.
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
|
||||
void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
|
||||
|
||||
constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page sizes
|
||||
size_t size = ((allocSize + alignment - 1) / alignment) * alignment; // multiple of alignment
|
||||
if (posix_memalign(&mem, alignment, size))
|
||||
mem = nullptr;
|
||||
madvise(mem, allocSize, MADV_HUGEPAGE);
|
||||
return mem;
|
||||
}
|
||||
|
||||
#elif defined(_WIN64)
|
||||
|
||||
static void* aligned_ttmem_alloc_large_pages(size_t allocSize) {
|
||||
|
||||
HANDLE hProcessToken { };
|
||||
LUID luid { };
|
||||
void* mem = nullptr;
|
||||
|
||||
const size_t largePageSize = GetLargePageMinimum();
|
||||
if (!largePageSize)
|
||||
return nullptr;
|
||||
|
||||
// We need SeLockMemoryPrivilege, so try to enable it for the process
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken))
|
||||
return nullptr;
|
||||
|
||||
if (LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luid))
|
||||
{
|
||||
TOKEN_PRIVILEGES tp { };
|
||||
TOKEN_PRIVILEGES prevTp { };
|
||||
DWORD prevTpLen = 0;
|
||||
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
// Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds,
|
||||
// we still need to query GetLastError() to ensure that the privileges were actually obtained...
|
||||
if (AdjustTokenPrivileges(
|
||||
hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) &&
|
||||
GetLastError() == ERROR_SUCCESS)
|
||||
{
|
||||
// round up size to full pages and allocate
|
||||
allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1);
|
||||
mem = VirtualAlloc(
|
||||
NULL, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
|
||||
|
||||
// privilege no longer needed, restore previous state
|
||||
AdjustTokenPrivileges(hProcessToken, FALSE, &prevTp, 0, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hProcessToken);
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
|
||||
|
||||
static bool firstCall = true;
|
||||
|
||||
// try to allocate large pages
|
||||
mem = aligned_ttmem_alloc_large_pages(allocSize);
|
||||
|
||||
// Suppress info strings on the first call. The first call occurs before 'uci'
|
||||
// is received and in that case this output confuses some GUIs.
|
||||
if (!firstCall)
|
||||
{
|
||||
if (mem)
|
||||
sync_cout << "info string Hash table allocation: Windows large pages used." << sync_endl;
|
||||
else
|
||||
sync_cout << "info string Hash table allocation: Windows large pages not used." << sync_endl;
|
||||
}
|
||||
firstCall = false;
|
||||
|
||||
// fall back to regular, page aligned, allocation if necessary
|
||||
if (!mem)
|
||||
mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
|
||||
|
||||
constexpr size_t alignment = 64; // assumed cache line size
|
||||
size_t size = allocSize + alignment - 1; // allocate some extra space
|
||||
mem = malloc(size);
|
||||
void* ret = reinterpret_cast<void*>((uintptr_t(mem) + alignment - 1) & ~uintptr_t(alignment - 1));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// aligned_ttmem_free will free the previously allocated ttmem
|
||||
#if defined(_WIN64)
|
||||
|
||||
void aligned_ttmem_free(void* mem) {
|
||||
|
||||
if (mem && !VirtualFree(mem, 0, MEM_RELEASE))
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
std::cerr << "Failed to free transposition table. Error code: 0x" <<
|
||||
std::hex << err << std::dec << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void aligned_ttmem_free(void *mem) {
|
||||
free(mem);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
namespace WinProcGroup {
|
||||
|
||||
#ifndef _WIN32
|
||||
|
|
20
src/misc.h
20
src/misc.h
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -25,6 +25,7 @@
|
|||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -33,8 +34,11 @@
|
|||
#include "thread_win32_osx.h"
|
||||
|
||||
const std::string engine_info(bool to_uci = false);
|
||||
const std::string compiler_info();
|
||||
void prefetch(void* addr);
|
||||
void start_logger(const std::string& fname);
|
||||
void* aligned_ttmem_alloc(size_t size, void*& mem);
|
||||
void aligned_ttmem_free(void* mem); // nop if mem == nullptr
|
||||
|
||||
void dbg_hit_on(bool b);
|
||||
void dbg_hit_on(bool c, bool b);
|
||||
|
@ -65,6 +69,14 @@ std::ostream& operator<<(std::ostream&, SyncCout);
|
|||
#define sync_cout std::cout << IO_LOCK
|
||||
#define sync_endl std::endl << IO_UNLOCK
|
||||
|
||||
namespace Utility {
|
||||
|
||||
/// Clamp a value between lo and hi. Available in c++17.
|
||||
template<class T> constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
|
||||
return v < lo ? lo : v > hi ? hi : v;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// xorshift64star Pseudo-Random Number Generator
|
||||
/// This class is based on original code written and dedicated
|
||||
|
@ -161,13 +173,13 @@ struct AsyncPRNG
|
|||
AsyncPRNG(uint64_t seed) : prng(seed) { assert(seed); }
|
||||
// [ASYNC] 乱数を一つ取り出す。
|
||||
template<typename T> T rand() {
|
||||
std::unique_lock<Mutex> lk(mutex);
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
return prng.rand<T>();
|
||||
}
|
||||
|
||||
// [ASYNC] 0からn-1までの乱数を返す。(一様分布ではないが現実的にはこれで十分)
|
||||
uint64_t rand(uint64_t n) {
|
||||
std::unique_lock<Mutex> lk(mutex);
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
return prng.rand(n);
|
||||
}
|
||||
|
||||
|
@ -175,7 +187,7 @@ struct AsyncPRNG
|
|||
uint64_t get_seed() const { return prng.get_seed(); }
|
||||
|
||||
protected:
|
||||
Mutex mutex;
|
||||
std::mutex mutex;
|
||||
PRNG prng;
|
||||
};
|
||||
|
||||
|
|
122
src/movegen.cpp
122
src/movegen.cpp
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -40,7 +40,7 @@ namespace {
|
|||
|
||||
// Knight promotion is the only promotion that can give a direct check
|
||||
// that's not already included in the queen promotion.
|
||||
if (Type == QUIET_CHECKS && (PseudoAttacks[KNIGHT][to] & ksq))
|
||||
if (Type == QUIET_CHECKS && (attacks_bb<KNIGHT>(to) & ksq))
|
||||
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
|
||||
else
|
||||
(void)ksq; // Silence a warning under MSVC
|
||||
|
@ -52,14 +52,14 @@ namespace {
|
|||
template<Color Us, GenType Type>
|
||||
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
|
||||
|
||||
// Compute some compile time parameters relative to the white side
|
||||
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
constexpr Color Them = ~Us;
|
||||
constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
|
||||
constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
|
||||
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
|
||||
constexpr Direction Up = pawn_push(Us);
|
||||
constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
|
||||
constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
|
||||
|
||||
const Square ksq = pos.square<KING>(Them);
|
||||
Bitboard emptySquares;
|
||||
|
||||
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
|
||||
|
@ -84,10 +84,8 @@ namespace {
|
|||
|
||||
if (Type == QUIET_CHECKS)
|
||||
{
|
||||
Square ksq = pos.square<KING>(Them);
|
||||
|
||||
b1 &= pos.attacks_from<PAWN>(ksq, Them);
|
||||
b2 &= pos.attacks_from<PAWN>(ksq, Them);
|
||||
b1 &= pawn_attacks_bb(Them, ksq);
|
||||
b2 &= pawn_attacks_bb(Them, ksq);
|
||||
|
||||
// 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
|
||||
|
@ -130,8 +128,6 @@ namespace {
|
|||
Bitboard b2 = shift<UpLeft >(pawnsOn7) & enemies;
|
||||
Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares;
|
||||
|
||||
Square ksq = pos.square<KING>(Them);
|
||||
|
||||
while (b1)
|
||||
moveList = make_promotions<Type, UpRight>(moveList, pop_lsb(&b1), ksq);
|
||||
|
||||
|
@ -170,7 +166,7 @@ namespace {
|
|||
if (Type == EVASIONS && !(target & (pos.ep_square() - Up)))
|
||||
return moveList;
|
||||
|
||||
b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them);
|
||||
b1 = pawnsNotOn7 & pawn_attacks_bb(Them, pos.ep_square());
|
||||
|
||||
assert(b1);
|
||||
|
||||
|
@ -183,27 +179,26 @@ namespace {
|
|||
}
|
||||
|
||||
|
||||
template<PieceType Pt, bool Checks>
|
||||
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us,
|
||||
Bitboard target) {
|
||||
template<Color Us, PieceType Pt, bool Checks>
|
||||
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
|
||||
|
||||
assert(Pt != KING && Pt != PAWN);
|
||||
static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()");
|
||||
|
||||
const Square* pl = pos.squares<Pt>(us);
|
||||
const Square* pl = pos.squares<Pt>(Us);
|
||||
|
||||
for (Square from = *pl; from != SQ_NONE; from = *++pl)
|
||||
{
|
||||
if (Checks)
|
||||
{
|
||||
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
|
||||
&& !(PseudoAttacks[Pt][from] & target & pos.check_squares(Pt)))
|
||||
&& !(attacks_bb<Pt>(from) & target & pos.check_squares(Pt)))
|
||||
continue;
|
||||
|
||||
if (pos.blockers_for_king(~us) & from)
|
||||
if (pos.blockers_for_king(~Us) & from)
|
||||
continue;
|
||||
}
|
||||
|
||||
Bitboard b = pos.attacks_from<Pt>(from) & target;
|
||||
Bitboard b = attacks_bb<Pt>(from, pos.pieces()) & target;
|
||||
|
||||
if (Checks)
|
||||
b &= pos.check_squares(Pt);
|
||||
|
@ -217,33 +212,49 @@ namespace {
|
|||
|
||||
|
||||
template<Color Us, GenType Type>
|
||||
ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) {
|
||||
|
||||
constexpr CastlingRight OO = Us | KING_SIDE;
|
||||
constexpr CastlingRight OOO = Us | QUEEN_SIDE;
|
||||
ExtMove* generate_all(const Position& pos, ExtMove* moveList) {
|
||||
constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations
|
||||
Bitboard target;
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case CAPTURES:
|
||||
target = pos.pieces(~Us);
|
||||
break;
|
||||
case QUIETS:
|
||||
case QUIET_CHECKS:
|
||||
target = ~pos.pieces();
|
||||
break;
|
||||
case EVASIONS:
|
||||
{
|
||||
Square checksq = lsb(pos.checkers());
|
||||
target = between_bb(pos.square<KING>(Us), checksq) | checksq;
|
||||
break;
|
||||
}
|
||||
case NON_EVASIONS:
|
||||
target = ~pos.pieces(Us);
|
||||
break;
|
||||
default:
|
||||
static_assert(true, "Unsupported type in generate_all()");
|
||||
}
|
||||
|
||||
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
|
||||
moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target);
|
||||
moveList = generate_moves<BISHOP, Checks>(pos, moveList, Us, target);
|
||||
moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target);
|
||||
moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target);
|
||||
moveList = generate_moves<Us, KNIGHT, Checks>(pos, moveList, target);
|
||||
moveList = generate_moves<Us, BISHOP, Checks>(pos, moveList, target);
|
||||
moveList = generate_moves<Us, ROOK, Checks>(pos, moveList, target);
|
||||
moveList = generate_moves<Us, QUEEN, Checks>(pos, moveList, target);
|
||||
|
||||
if (Type != QUIET_CHECKS && Type != EVASIONS)
|
||||
{
|
||||
Square ksq = pos.square<KING>(Us);
|
||||
Bitboard b = pos.attacks_from<KING>(ksq) & target;
|
||||
Bitboard b = attacks_bb<KING>(ksq) & target;
|
||||
while (b)
|
||||
*moveList++ = make_move(ksq, pop_lsb(&b));
|
||||
|
||||
if (Type != CAPTURES && pos.can_castle(CastlingRight(OO | OOO)))
|
||||
{
|
||||
if (!pos.castling_impeded(OO) && pos.can_castle(OO))
|
||||
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OO));
|
||||
|
||||
if (!pos.castling_impeded(OOO) && pos.can_castle(OOO))
|
||||
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OOO));
|
||||
}
|
||||
if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING))
|
||||
for(CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } )
|
||||
if (!pos.castling_impeded(cr) && pos.can_castle(cr))
|
||||
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(cr));
|
||||
}
|
||||
|
||||
return moveList;
|
||||
|
@ -261,17 +272,13 @@ namespace {
|
|||
template<GenType Type>
|
||||
ExtMove* generate(const Position& pos, ExtMove* moveList) {
|
||||
|
||||
assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
|
||||
static_assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS, "Unsupported type in generate()");
|
||||
assert(!pos.checkers());
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
|
||||
Bitboard target = Type == CAPTURES ? pos.pieces(~us)
|
||||
: Type == QUIETS ? ~pos.pieces()
|
||||
: Type == NON_EVASIONS ? ~pos.pieces(us) : 0;
|
||||
|
||||
return us == WHITE ? generate_all<WHITE, Type>(pos, moveList, target)
|
||||
: generate_all<BLACK, Type>(pos, moveList, target);
|
||||
return us == WHITE ? generate_all<WHITE, Type>(pos, moveList)
|
||||
: generate_all<BLACK, Type>(pos, moveList);
|
||||
}
|
||||
|
||||
// Explicit template instantiations
|
||||
|
@ -288,27 +295,24 @@ ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
|
|||
assert(!pos.checkers());
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
Bitboard dc = pos.blockers_for_king(~us) & pos.pieces(us);
|
||||
Bitboard dc = pos.blockers_for_king(~us) & pos.pieces(us) & ~pos.pieces(PAWN);
|
||||
|
||||
while (dc)
|
||||
{
|
||||
Square from = pop_lsb(&dc);
|
||||
PieceType pt = type_of(pos.piece_on(from));
|
||||
|
||||
if (pt == PAWN)
|
||||
continue; // Will be generated together with direct checks
|
||||
|
||||
Bitboard b = pos.attacks_from(pt, from) & ~pos.pieces();
|
||||
Bitboard b = attacks_bb(pt, from, pos.pieces()) & ~pos.pieces();
|
||||
|
||||
if (pt == KING)
|
||||
b &= ~PseudoAttacks[QUEEN][pos.square<KING>(~us)];
|
||||
b &= ~attacks_bb<QUEEN>(pos.square<KING>(~us));
|
||||
|
||||
while (b)
|
||||
*moveList++ = make_move(from, pop_lsb(&b));
|
||||
}
|
||||
|
||||
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList, ~pos.pieces())
|
||||
: generate_all<BLACK, QUIET_CHECKS>(pos, moveList, ~pos.pieces());
|
||||
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList)
|
||||
: generate_all<BLACK, QUIET_CHECKS>(pos, moveList);
|
||||
}
|
||||
|
||||
|
||||
|
@ -328,13 +332,10 @@ ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* moveList) {
|
|||
// the king evasions in order to skip known illegal moves, which avoids any
|
||||
// useless legality checks later on.
|
||||
while (sliders)
|
||||
{
|
||||
Square checksq = pop_lsb(&sliders);
|
||||
sliderAttacks |= LineBB[checksq][ksq] ^ checksq;
|
||||
}
|
||||
sliderAttacks |= LineBB[ksq][pop_lsb(&sliders)] & ~pos.checkers();
|
||||
|
||||
// Generate evasions for king, capture and non capture moves
|
||||
Bitboard b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
|
||||
Bitboard b = attacks_bb<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
|
||||
while (b)
|
||||
*moveList++ = make_move(ksq, pop_lsb(&b));
|
||||
|
||||
|
@ -342,11 +343,8 @@ ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* moveList) {
|
|||
return moveList; // Double check, only a king move can save the day
|
||||
|
||||
// Generate blocking evasions or captures of the checking piece
|
||||
Square checksq = lsb(pos.checkers());
|
||||
Bitboard target = between_bb(checksq, ksq) | checksq;
|
||||
|
||||
return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList, target)
|
||||
: generate_all<BLACK, EVASIONS>(pos, moveList, target);
|
||||
return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList)
|
||||
: generate_all<BLACK, EVASIONS>(pos, moveList);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -56,45 +56,39 @@ namespace {
|
|||
/// ordering is at the current node.
|
||||
|
||||
/// MovePicker constructor for the main search
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
|
||||
const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, Move* killers)
|
||||
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch),
|
||||
refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) {
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const LowPlyHistory* lp,
|
||||
const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, Move* killers, int pl)
|
||||
: pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch),
|
||||
ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d), ply(pl) {
|
||||
|
||||
assert(d > DEPTH_ZERO);
|
||||
assert(d > 0);
|
||||
|
||||
stage = pos.checkers() ? EVASION_TT : MAIN_TT;
|
||||
ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
|
||||
stage += (ttMove == MOVE_NONE);
|
||||
stage = (pos.checkers() ? EVASION_TT : MAIN_TT) +
|
||||
!(ttm && pos.pseudo_legal(ttm));
|
||||
}
|
||||
|
||||
/// MovePicker constructor for quiescence search
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
|
||||
const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs)
|
||||
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), recaptureSquare(rs), depth(d) {
|
||||
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d) {
|
||||
|
||||
assert(d <= DEPTH_ZERO);
|
||||
assert(d <= 0);
|
||||
|
||||
stage = pos.checkers() ? EVASION_TT : QSEARCH_TT;
|
||||
ttMove = ttm
|
||||
&& (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare)
|
||||
&& pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
|
||||
stage += (ttMove == MOVE_NONE);
|
||||
stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) +
|
||||
!(ttm && (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare)
|
||||
&& pos.pseudo_legal(ttm));
|
||||
}
|
||||
|
||||
/// MovePicker constructor for ProbCut: we generate captures with SEE greater
|
||||
/// than or equal to the given threshold.
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph)
|
||||
: pos(p), captureHistory(cph), threshold(th) {
|
||||
: pos(p), captureHistory(cph), ttMove(ttm), threshold(th) {
|
||||
|
||||
assert(!pos.checkers());
|
||||
|
||||
stage = PROBCUT_TT;
|
||||
ttMove = ttm
|
||||
&& pos.capture(ttm)
|
||||
&& pos.pseudo_legal(ttm)
|
||||
&& pos.see_ge(ttm, threshold) ? ttm : MOVE_NONE;
|
||||
stage += (ttMove == MOVE_NONE);
|
||||
stage = PROBCUT_TT + !(ttm && pos.capture(ttm)
|
||||
&& pos.pseudo_legal(ttm)
|
||||
&& pos.see_ge(ttm, threshold));
|
||||
}
|
||||
|
||||
/// MovePicker::score() assigns a numerical value to each move in a list, used
|
||||
|
@ -107,15 +101,16 @@ void MovePicker::score() {
|
|||
|
||||
for (auto& m : *this)
|
||||
if (Type == CAPTURES)
|
||||
m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
|
||||
+ (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))] / 8;
|
||||
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)))];
|
||||
|
||||
else if (Type == QUIETS)
|
||||
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
|
||||
+ (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)] / 2;
|
||||
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
|
||||
+ 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ 2 * (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ 2 * (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (ply < MAX_LPH ? std::min(4, depth / 3) * (*lowPlyHistory)[ply][from_to(m)] : 0);
|
||||
|
||||
else // Type == EVASIONS
|
||||
{
|
||||
|
@ -174,7 +169,7 @@ top:
|
|||
|
||||
case GOOD_CAPTURE:
|
||||
if (select<Best>([&](){
|
||||
return pos.see_ge(*cur, Value(-55 * cur->value / 1024)) ?
|
||||
return pos.see_ge(*cur, Value(-69 * cur->value / 1024)) ?
|
||||
// Move losing capture to endBadCaptures to be tried later
|
||||
true : (*endBadCaptures++ = *cur, false); }))
|
||||
return *(cur - 1);
|
||||
|
@ -200,11 +195,15 @@ top:
|
|||
/* fallthrough */
|
||||
|
||||
case QUIET_INIT:
|
||||
cur = endBadCaptures;
|
||||
endMoves = generate<QUIETS>(pos, cur);
|
||||
if (!skipQuiets)
|
||||
{
|
||||
cur = endBadCaptures;
|
||||
endMoves = generate<QUIETS>(pos, cur);
|
||||
|
||||
score<QUIETS>();
|
||||
partial_insertion_sort(cur, endMoves, -3000 * depth);
|
||||
}
|
||||
|
||||
score<QUIETS>();
|
||||
partial_insertion_sort(cur, endMoves, -4000 * depth / ONE_PLY);
|
||||
++stage;
|
||||
/* fallthrough */
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -80,7 +80,7 @@ struct Stats<T, D, Size> : public std::array<StatsEntry<T, D>, Size> {};
|
|||
|
||||
/// In stats table, D=0 means that the template parameter is not used
|
||||
enum StatsParams { NOT_USED = 0 };
|
||||
|
||||
enum StatsType { NoCaptures, Captures };
|
||||
|
||||
/// ButterflyHistory records how often quiet moves have been successful or
|
||||
/// unsuccessful during the current search, and is used for reduction and move
|
||||
|
@ -88,6 +88,12 @@ enum StatsParams { NOT_USED = 0 };
|
|||
/// 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;
|
||||
|
||||
/// LowPlyHistory at higher depths records successful quiet moves on plies 0 to 3
|
||||
/// and quiet moves which are/were in the PV (ttPv)
|
||||
/// It get cleared with each new search and get filled during iterative deepening
|
||||
constexpr int MAX_LPH = 4;
|
||||
typedef Stats<int16_t, 10692, MAX_LPH, int(SQUARE_NB) * int(SQUARE_NB)> LowPlyHistory;
|
||||
|
||||
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
|
||||
/// move, see www.chessprogramming.org/Countermove_Heuristic
|
||||
typedef Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB> CounterMoveHistory;
|
||||
|
@ -123,10 +129,12 @@ public:
|
|||
const PieceToHistory**,
|
||||
Square);
|
||||
MovePicker(const Position&, Move, Depth, const ButterflyHistory*,
|
||||
const LowPlyHistory*,
|
||||
const CapturePieceToHistory*,
|
||||
const PieceToHistory**,
|
||||
Move,
|
||||
Move*);
|
||||
Move*,
|
||||
int);
|
||||
Move next_move(bool skipQuiets = false);
|
||||
|
||||
private:
|
||||
|
@ -137,6 +145,7 @@ private:
|
|||
|
||||
const Position& pos;
|
||||
const ButterflyHistory* mainHistory;
|
||||
const LowPlyHistory* lowPlyHistory;
|
||||
const CapturePieceToHistory* captureHistory;
|
||||
const PieceToHistory** continuationHistory;
|
||||
Move ttMove;
|
||||
|
@ -145,6 +154,7 @@ private:
|
|||
Square recaptureSquare;
|
||||
Value threshold;
|
||||
Depth depth;
|
||||
int ply;
|
||||
ExtMove moves[MAX_MOVES];
|
||||
};
|
||||
|
||||
|
|
177
src/pawns.cpp
177
src/pawns.cpp
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -32,9 +32,13 @@ namespace {
|
|||
#define S(mg, eg) make_score(mg, eg)
|
||||
|
||||
// Pawn penalties
|
||||
constexpr Score Backward = S( 9, 24);
|
||||
constexpr Score Doubled = S(11, 56);
|
||||
constexpr Score Isolated = S( 5, 15);
|
||||
constexpr Score Backward = S( 9, 24);
|
||||
constexpr Score Doubled = S(11, 56);
|
||||
constexpr Score Isolated = S( 5, 15);
|
||||
constexpr Score WeakLever = S( 0, 56);
|
||||
constexpr Score WeakUnopposed = S(13, 27);
|
||||
|
||||
constexpr Score BlockedStorm[RANK_NB] = {S( 0, 0), S( 0, 0), S( 76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2)};
|
||||
|
||||
// Connected pawn bonus
|
||||
constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 };
|
||||
|
@ -50,12 +54,13 @@ namespace {
|
|||
|
||||
// Danger of enemy pawns moving toward our king by [distance from edge][rank].
|
||||
// RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn
|
||||
// is behind our king.
|
||||
// is behind our king. Note that UnblockedStorm[0][1-2] accommodate opponent pawn
|
||||
// on edge, likely blocked by our king.
|
||||
constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = {
|
||||
{ V( 89), V(107), V(123), V(93), V(57), V( 45), V( 51) },
|
||||
{ V( 44), V(-18), V(123), V(46), V(39), V( -7), V( 23) },
|
||||
{ V( 4), V( 52), V(162), V(37), V( 7), V(-14), V( -2) },
|
||||
{ V(-10), V(-14), V( 90), V(15), V( 2), V( -7), V(-16) }
|
||||
{ V( 85), V(-289), V(-166), V(97), V(50), V( 45), V( 50) },
|
||||
{ V( 46), V( -25), V( 122), V(45), V(37), V(-10), V( 20) },
|
||||
{ V( -6), V( 51), V( 168), V(34), V(-2), V(-22), V(-14) },
|
||||
{ V(-15), V( -11), V( 101), V( 4), V(11), V(-15), V(-29) }
|
||||
};
|
||||
|
||||
#undef S
|
||||
|
@ -64,81 +69,98 @@ namespace {
|
|||
template<Color Us>
|
||||
Score evaluate(const Position& pos, Pawns::Entry* e) {
|
||||
|
||||
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
|
||||
constexpr Color Them = ~Us;
|
||||
constexpr Direction Up = pawn_push(Us);
|
||||
|
||||
Bitboard b, neighbours, stoppers, doubled, support, phalanx;
|
||||
Bitboard lever, leverPush;
|
||||
Bitboard neighbours, stoppers, support, phalanx, opposed;
|
||||
Bitboard lever, leverPush, blocked;
|
||||
Square s;
|
||||
bool opposed, backward;
|
||||
bool backward, passed, doubled;
|
||||
Score score = SCORE_ZERO;
|
||||
const Square* pl = pos.squares<PAWN>(Us);
|
||||
|
||||
Bitboard ourPawns = pos.pieces( Us, PAWN);
|
||||
Bitboard theirPawns = pos.pieces(Them, PAWN);
|
||||
|
||||
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = e->weakUnopposed[Us] = 0;
|
||||
e->kingSquares[Us] = SQ_NONE;
|
||||
e->pawnAttacks[Us] = pawn_attacks_bb<Us>(ourPawns);
|
||||
Bitboard doubleAttackThem = pawn_double_attacks_bb<Them>(theirPawns);
|
||||
|
||||
e->passedPawns[Us] = 0;
|
||||
e->kingSquares[Us] = SQ_NONE;
|
||||
e->pawnAttacks[Us] = e->pawnAttacksSpan[Us] = pawn_attacks_bb<Us>(ourPawns);
|
||||
e->blockedCount += popcount(shift<Up>(ourPawns) & (theirPawns | doubleAttackThem));
|
||||
|
||||
// Loop through all pawns of the current color and score each pawn
|
||||
while ((s = *pl++) != SQ_NONE)
|
||||
{
|
||||
assert(pos.piece_on(s) == make_piece(Us, PAWN));
|
||||
|
||||
File f = file_of(s);
|
||||
Rank r = relative_rank(Us, s);
|
||||
|
||||
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
|
||||
|
||||
// Flag the pawn
|
||||
opposed = theirPawns & forward_file_bb(Us, s);
|
||||
blocked = theirPawns & (s + Up);
|
||||
stoppers = theirPawns & passed_pawn_span(Us, s);
|
||||
lever = theirPawns & PawnAttacks[Us][s];
|
||||
leverPush = theirPawns & PawnAttacks[Us][s + Up];
|
||||
lever = theirPawns & pawn_attacks_bb(Us, s);
|
||||
leverPush = theirPawns & pawn_attacks_bb(Us, s + Up);
|
||||
doubled = ourPawns & (s - Up);
|
||||
neighbours = ourPawns & adjacent_files_bb(f);
|
||||
neighbours = ourPawns & adjacent_files_bb(s);
|
||||
phalanx = neighbours & rank_bb(s);
|
||||
support = neighbours & rank_bb(s - Up);
|
||||
|
||||
// A pawn is backward when it is behind all pawns of the same color
|
||||
// on the adjacent files and cannot be safely advanced.
|
||||
backward = !(ourPawns & pawn_attack_span(Them, s + Up))
|
||||
&& (stoppers & (leverPush | (s + Up)));
|
||||
// A pawn is backward when it is behind all pawns of the same color on
|
||||
// the adjacent files and cannot safely advance.
|
||||
backward = !(neighbours & forward_ranks_bb(Them, s + Up))
|
||||
&& (leverPush | blocked);
|
||||
|
||||
// Passed pawns will be properly scored in evaluation because we need
|
||||
// full attack info to evaluate them. Include also not passed pawns
|
||||
// which could become passed after one or two pawn pushes when are
|
||||
// not attacked more times than defended.
|
||||
if ( !(stoppers ^ lever ^ leverPush)
|
||||
&& (support || !more_than_one(lever))
|
||||
&& popcount(phalanx) >= popcount(leverPush))
|
||||
// Compute additional span if pawn is not backward nor blocked
|
||||
if (!backward && !blocked)
|
||||
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
|
||||
|
||||
// A pawn is passed if one of the three following conditions is true:
|
||||
// (a) there is no stoppers except some levers
|
||||
// (b) the only stoppers are the leverPush, but we outnumber them
|
||||
// (c) there is only one front stopper which can be levered.
|
||||
// (Refined in Evaluation::passed)
|
||||
passed = !(stoppers ^ lever)
|
||||
|| ( !(stoppers ^ leverPush)
|
||||
&& popcount(phalanx) >= popcount(leverPush))
|
||||
|| ( stoppers == blocked && r >= RANK_5
|
||||
&& (shift<Up>(support) & ~(theirPawns | doubleAttackThem)));
|
||||
|
||||
passed &= !(forward_file_bb(Us, s) & ourPawns);
|
||||
|
||||
// Passed pawns will be properly scored later in evaluation when we have
|
||||
// full attack info.
|
||||
if (passed)
|
||||
e->passedPawns[Us] |= s;
|
||||
|
||||
else if (stoppers == square_bb(s + Up) && r >= RANK_5)
|
||||
{
|
||||
b = shift<Up>(support) & ~theirPawns;
|
||||
while (b)
|
||||
if (!more_than_one(theirPawns & PawnAttacks[Us][pop_lsb(&b)]))
|
||||
e->passedPawns[Us] |= s;
|
||||
}
|
||||
|
||||
// Score this pawn
|
||||
if (support | phalanx)
|
||||
{
|
||||
int v = Connected[r] * (phalanx ? 3 : 2) / (opposed ? 2 : 1)
|
||||
+ 17 * popcount(support);
|
||||
int v = Connected[r] * (4 + 2 * bool(phalanx) - 2 * bool(opposed) - bool(blocked)) / 2
|
||||
+ 21 * popcount(support);
|
||||
|
||||
score += make_score(v, v * (r - 2) / 4);
|
||||
}
|
||||
|
||||
else if (!neighbours)
|
||||
score -= Isolated, e->weakUnopposed[Us] += !opposed;
|
||||
{
|
||||
if ( opposed
|
||||
&& (ourPawns & forward_file_bb(Them, s))
|
||||
&& !(theirPawns & adjacent_files_bb(s)))
|
||||
score -= Doubled;
|
||||
else
|
||||
score -= Isolated
|
||||
+ WeakUnopposed * !opposed;
|
||||
}
|
||||
|
||||
else if (backward)
|
||||
score -= Backward, e->weakUnopposed[Us] += !opposed;
|
||||
score -= Backward
|
||||
+ WeakUnopposed * !opposed;
|
||||
|
||||
if (doubled && !support)
|
||||
score -= Doubled;
|
||||
if (!support)
|
||||
score -= Doubled * doubled
|
||||
+ WeakLever * more_than_one(lever);
|
||||
}
|
||||
|
||||
return score;
|
||||
|
@ -162,6 +184,7 @@ Entry* probe(const Position& pos) {
|
|||
return e;
|
||||
|
||||
e->key = key;
|
||||
e->blockedCount = 0;
|
||||
e->scores[WHITE] = evaluate<WHITE>(pos, e);
|
||||
e->scores[BLACK] = evaluate<BLACK>(pos, e);
|
||||
|
||||
|
@ -173,40 +196,35 @@ Entry* probe(const Position& pos) {
|
|||
/// penalty for a king, looking at the king file and the two closest files.
|
||||
|
||||
template<Color Us>
|
||||
void Entry::evaluate_shelter(const Position& pos, Square ksq, Score& shelter) {
|
||||
Score Entry::evaluate_shelter(const Position& pos, Square ksq) {
|
||||
|
||||
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
|
||||
constexpr Bitboard BlockSquares = (Rank1BB | Rank2BB | Rank7BB | Rank8BB)
|
||||
& (FileABB | FileHBB);
|
||||
constexpr Color Them = ~Us;
|
||||
|
||||
Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq);
|
||||
Bitboard ourPawns = b & pos.pieces(Us);
|
||||
Bitboard ourPawns = b & pos.pieces(Us) & ~pawnAttacks[Them];
|
||||
Bitboard theirPawns = b & pos.pieces(Them);
|
||||
|
||||
Value bonus[] = { (shift<Down>(theirPawns) & BlockSquares & ksq) ? Value(374) : Value(5),
|
||||
VALUE_ZERO };
|
||||
Score bonus = make_score(5, 5);
|
||||
|
||||
File center = clamp(file_of(ksq), FILE_B, FILE_G);
|
||||
File center = Utility::clamp(file_of(ksq), FILE_B, FILE_G);
|
||||
for (File f = File(center - 1); f <= File(center + 1); ++f)
|
||||
{
|
||||
b = ourPawns & file_bb(f);
|
||||
Rank ourRank = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
|
||||
int ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0;
|
||||
|
||||
b = theirPawns & file_bb(f);
|
||||
Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
|
||||
int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0;
|
||||
|
||||
int d = std::min(f, ~f);
|
||||
bonus[MG] += ShelterStrength[d][ourRank];
|
||||
int d = edge_distance(f);
|
||||
bonus += make_score(ShelterStrength[d][ourRank], 0);
|
||||
|
||||
if (ourRank && (ourRank == theirRank - 1))
|
||||
bonus[MG] -= 82 * (theirRank == RANK_3), bonus[EG] -= 82 * (theirRank == RANK_3);
|
||||
bonus -= BlockedStorm[theirRank];
|
||||
else
|
||||
bonus[MG] -= UnblockedStorm[d][theirRank];
|
||||
bonus -= make_score(UnblockedStorm[d][theirRank], 0);
|
||||
}
|
||||
|
||||
if (bonus[MG] > mg_value(shelter))
|
||||
shelter = make_score(bonus[MG], bonus[EG]);
|
||||
return bonus;
|
||||
}
|
||||
|
||||
|
||||
|
@ -219,27 +237,28 @@ Score Entry::do_king_safety(const Position& pos) {
|
|||
Square ksq = pos.square<KING>(Us);
|
||||
kingSquares[Us] = ksq;
|
||||
castlingRights[Us] = pos.castling_rights(Us);
|
||||
auto compare = [](Score a, Score b) { return mg_value(a) < mg_value(b); };
|
||||
|
||||
Score shelter = evaluate_shelter<Us>(pos, ksq);
|
||||
|
||||
// If we can castle use the bonus after castling if it is bigger
|
||||
|
||||
if (pos.can_castle(Us & KING_SIDE))
|
||||
shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1)), compare);
|
||||
|
||||
if (pos.can_castle(Us & QUEEN_SIDE))
|
||||
shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1)), compare);
|
||||
|
||||
// In endgame we like to bring our king near our closest pawn
|
||||
Bitboard pawns = pos.pieces(Us, PAWN);
|
||||
int minPawnDist = pawns ? 8 : 0;
|
||||
int minPawnDist = 6;
|
||||
|
||||
if (pawns & PseudoAttacks[KING][ksq])
|
||||
if (pawns & attacks_bb<KING>(ksq))
|
||||
minPawnDist = 1;
|
||||
|
||||
else while (pawns)
|
||||
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns)));
|
||||
|
||||
Score shelter = make_score(-VALUE_INFINITE, VALUE_ZERO);
|
||||
evaluate_shelter<Us>(pos, ksq, shelter);
|
||||
|
||||
// If we can castle use the bonus after the castling if it is bigger
|
||||
if (pos.can_castle(Us | KING_SIDE))
|
||||
evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1), shelter);
|
||||
|
||||
if (pos.can_castle(Us | QUEEN_SIDE))
|
||||
evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1), shelter);
|
||||
|
||||
return shelter - make_score(VALUE_ZERO, 16 * minPawnDist);
|
||||
return shelter - make_score(0, 16 * minPawnDist);
|
||||
}
|
||||
|
||||
// Explicit template instantiation
|
||||
|
|
11
src/pawns.h
11
src/pawns.h
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -37,8 +37,8 @@ struct Entry {
|
|||
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
|
||||
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
|
||||
Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
|
||||
int weak_unopposed(Color c) const { return weakUnopposed[c]; }
|
||||
int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); }
|
||||
int blocked_count() const { return blockedCount; }
|
||||
|
||||
template<Color Us>
|
||||
Score king_safety(const Position& pos) {
|
||||
|
@ -50,7 +50,7 @@ struct Entry {
|
|||
Score do_king_safety(const Position& pos);
|
||||
|
||||
template<Color Us>
|
||||
void evaluate_shelter(const Position& pos, Square ksq, Score& shelter);
|
||||
Score evaluate_shelter(const Position& pos, Square ksq);
|
||||
|
||||
Key key;
|
||||
Score scores[COLOR_NB];
|
||||
|
@ -59,12 +59,11 @@ struct Entry {
|
|||
Bitboard pawnAttacksSpan[COLOR_NB];
|
||||
Square kingSquares[COLOR_NB];
|
||||
Score kingSafety[COLOR_NB];
|
||||
int weakUnopposed[COLOR_NB];
|
||||
int castlingRights[COLOR_NB];
|
||||
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
|
||||
int blockedCount;
|
||||
};
|
||||
|
||||
typedef HashTable<Entry, 16384> Table;
|
||||
typedef HashTable<Entry, 131072> Table;
|
||||
|
||||
Entry* probe(const Position& pos);
|
||||
|
||||
|
|
265
src/position.cpp
265
src/position.cpp
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -50,41 +50,6 @@ const string PieceToChar(" PNBRQK pnbrqk");
|
|||
|
||||
constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
|
||||
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING };
|
||||
|
||||
// min_attacker() is a helper function used by see_ge() to locate the least
|
||||
// valuable attacker for the side to move, remove the attacker we just found
|
||||
// from the bitboards and scan for new X-ray attacks behind it.
|
||||
|
||||
template<int Pt>
|
||||
PieceType min_attacker(const Bitboard* byTypeBB, Square to, Bitboard stmAttackers,
|
||||
Bitboard& occupied, Bitboard& attackers) {
|
||||
|
||||
Bitboard b = stmAttackers & byTypeBB[Pt];
|
||||
if (!b)
|
||||
return min_attacker<Pt + 1>(byTypeBB, to, stmAttackers, occupied, attackers);
|
||||
|
||||
occupied ^= lsb(b); // Remove the attacker from occupied
|
||||
|
||||
// Add any X-ray attack behind the just removed piece. For instance with
|
||||
// rooks in a8 and a7 attacking a1, after removing a7 we add rook in a8.
|
||||
// Note that new added attackers can be of any color.
|
||||
if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN)
|
||||
attackers |= attacks_bb<BISHOP>(to, occupied) & (byTypeBB[BISHOP] | byTypeBB[QUEEN]);
|
||||
|
||||
if (Pt == ROOK || Pt == QUEEN)
|
||||
attackers |= attacks_bb<ROOK>(to, occupied) & (byTypeBB[ROOK] | byTypeBB[QUEEN]);
|
||||
|
||||
// X-ray may add already processed pieces because byTypeBB[] is constant: in
|
||||
// the rook example, now attackers contains _again_ rook in a7, so remove it.
|
||||
attackers &= occupied;
|
||||
return (PieceType)Pt;
|
||||
}
|
||||
|
||||
template<>
|
||||
PieceType min_attacker<KING>(const Bitboard*, Square, Bitboard, Bitboard&, Bitboard&) {
|
||||
return KING; // No need to update bitboards: it is the last cycle
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
|
@ -174,7 +139,7 @@ void Position::init() {
|
|||
for (Piece pc : Pieces)
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||
for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2)
|
||||
if (PseudoAttacks[type_of(pc)][s1] & s2)
|
||||
if ((type_of(pc) != PAWN) && (attacks_bb(type_of(pc), s1, 0) & s2))
|
||||
{
|
||||
Move move = make_move(s1, s2);
|
||||
Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side;
|
||||
|
@ -352,19 +317,18 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
|
|||
void Position::set_castling_right(Color c, Square rfrom) {
|
||||
|
||||
Square kfrom = square<KING>(c);
|
||||
CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE;
|
||||
CastlingRight cr = (c | cs);
|
||||
CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE: QUEEN_SIDE);
|
||||
|
||||
st->castlingRights |= cr;
|
||||
castlingRightsMask[kfrom] |= cr;
|
||||
castlingRightsMask[rfrom] |= cr;
|
||||
castlingRookSquare[cr] = rfrom;
|
||||
|
||||
Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
|
||||
Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
|
||||
Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1);
|
||||
Square rto = relative_square(c, cr & KING_SIDE ? SQ_F1 : SQ_D1);
|
||||
|
||||
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
|
||||
& ~(square_bb(kfrom) | rfrom);
|
||||
& ~(kfrom | rfrom);
|
||||
}
|
||||
|
||||
|
||||
|
@ -377,10 +341,10 @@ void Position::set_check_info(StateInfo* si) const {
|
|||
|
||||
Square ksq = square<KING>(~sideToMove);
|
||||
|
||||
si->checkSquares[PAWN] = attacks_from<PAWN>(ksq, ~sideToMove);
|
||||
si->checkSquares[KNIGHT] = attacks_from<KNIGHT>(ksq);
|
||||
si->checkSquares[BISHOP] = attacks_from<BISHOP>(ksq);
|
||||
si->checkSquares[ROOK] = attacks_from<ROOK>(ksq);
|
||||
si->checkSquares[PAWN] = pawn_attacks_bb(~sideToMove, ksq);
|
||||
si->checkSquares[KNIGHT] = attacks_bb<KNIGHT>(ksq);
|
||||
si->checkSquares[BISHOP] = attacks_bb<BISHOP>(ksq, pieces());
|
||||
si->checkSquares[ROOK] = attacks_bb<ROOK>(ksq, pieces());
|
||||
si->checkSquares[QUEEN] = si->checkSquares[BISHOP] | si->checkSquares[ROOK];
|
||||
si->checkSquares[KING] = 0;
|
||||
}
|
||||
|
@ -409,7 +373,7 @@ void Position::set_state(StateInfo* si) const {
|
|||
if (type_of(pc) == PAWN)
|
||||
si->pawnKey ^= Zobrist::psq[pc][s];
|
||||
|
||||
else if (type_of(pc) != PAWN && type_of(pc) != KING)
|
||||
else if (type_of(pc) != KING)
|
||||
si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc];
|
||||
}
|
||||
|
||||
|
@ -433,11 +397,13 @@ void Position::set_state(StateInfo* si) const {
|
|||
|
||||
Position& Position::set(const string& code, Color c, StateInfo* si) {
|
||||
|
||||
assert(code.length() > 0 && code.length() < 8);
|
||||
assert(code[0] == 'K');
|
||||
|
||||
string sides[] = { code.substr(code.find('K', 1)), // Weak
|
||||
code.substr(0, code.find('K', 1)) }; // Strong
|
||||
code.substr(0, std::min(code.find('v'), code.find('K', 1))) }; // Strong
|
||||
|
||||
assert(sides[0].length() > 0 && sides[0].length() < 8);
|
||||
assert(sides[1].length() > 0 && sides[1].length() < 8);
|
||||
|
||||
std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
|
||||
|
||||
|
@ -511,9 +477,9 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners
|
|||
pinners = 0;
|
||||
|
||||
// Snipers are sliders that attack 's' when a piece and other snipers are removed
|
||||
Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK))
|
||||
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
|
||||
Bitboard occupancy = pieces() & ~snipers;
|
||||
Bitboard snipers = ( (attacks_bb< ROOK>(s) & pieces(QUEEN, ROOK))
|
||||
| (attacks_bb<BISHOP>(s) & pieces(QUEEN, BISHOP))) & sliders;
|
||||
Bitboard occupancy = pieces() ^ snipers;
|
||||
|
||||
while (snipers)
|
||||
{
|
||||
|
@ -536,12 +502,12 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners
|
|||
|
||||
Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
|
||||
|
||||
return (attacks_from<PAWN>(s, BLACK) & pieces(WHITE, PAWN))
|
||||
| (attacks_from<PAWN>(s, WHITE) & pieces(BLACK, PAWN))
|
||||
| (attacks_from<KNIGHT>(s) & pieces(KNIGHT))
|
||||
return (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN))
|
||||
| (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN))
|
||||
| (attacks_bb<KNIGHT>(s) & pieces(KNIGHT))
|
||||
| (attacks_bb< ROOK>(s, occupied) & pieces( ROOK, QUEEN))
|
||||
| (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
|
||||
| (attacks_from<KING>(s) & pieces(KING));
|
||||
| (attacks_bb<KING>(s) & pieces(KING));
|
||||
}
|
||||
|
||||
|
||||
|
@ -644,15 +610,15 @@ bool Position::pseudo_legal(const Move m) const {
|
|||
if ((Rank8BB | Rank1BB) & to)
|
||||
return false;
|
||||
|
||||
if ( !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture
|
||||
if ( !(pawn_attacks_bb(us, from) & pieces(~us) & to) // Not a capture
|
||||
&& !((from + pawn_push(us) == to) && empty(to)) // Not a single push
|
||||
&& !( (from + 2 * pawn_push(us) == to) // Not a double push
|
||||
&& (rank_of(from) == relative_rank(us, RANK_2))
|
||||
&& (relative_rank(us, from) == RANK_2)
|
||||
&& empty(to)
|
||||
&& empty(to - pawn_push(us))))
|
||||
return false;
|
||||
}
|
||||
else if (!(attacks_from(type_of(pc), from) & to))
|
||||
else if (!(attacks_bb(type_of(pc), from, pieces()) & to))
|
||||
return false;
|
||||
|
||||
// Evasions generator already takes care to avoid some kind of illegal moves
|
||||
|
@ -691,11 +657,11 @@ bool Position::gives_check(Move m) const {
|
|||
Square to = to_sq(m);
|
||||
|
||||
// Is there a direct check?
|
||||
if (st->checkSquares[type_of(piece_on(from))] & to)
|
||||
if (check_squares(type_of(piece_on(from))) & to)
|
||||
return true;
|
||||
|
||||
// Is there a discovered check?
|
||||
if ( (st->blockersForKing[~sideToMove] & from)
|
||||
if ( (blockers_for_king(~sideToMove) & from)
|
||||
&& !aligned(from, to, square<KING>(~sideToMove)))
|
||||
return true;
|
||||
|
||||
|
@ -722,11 +688,11 @@ bool Position::gives_check(Move m) const {
|
|||
case CASTLING:
|
||||
{
|
||||
Square kfrom = from;
|
||||
Square rfrom = to; // Castling is encoded as 'King captures the rook'
|
||||
Square rfrom = to; // Castling is encoded as 'king captures the rook'
|
||||
Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1);
|
||||
Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1);
|
||||
|
||||
return (PseudoAttacks[ROOK][rto] & square<KING>(~sideToMove))
|
||||
return (attacks_bb<ROOK>(rto) & square<KING>(~sideToMove))
|
||||
&& (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
|
||||
}
|
||||
default:
|
||||
|
@ -821,7 +787,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
|||
piece_no1 = piece_no_of(capsq);
|
||||
#endif // defined(EVAL_NNUE)
|
||||
|
||||
board[capsq] = NO_PIECE; // Not done by remove_piece()
|
||||
//board[capsq] = NO_PIECE; // Not done by remove_piece()
|
||||
#if defined(EVAL_NNUE)
|
||||
evalList.piece_no_list_board[capsq] = PIECE_NUMBER_NB;
|
||||
#endif // defined(EVAL_NNUE)
|
||||
|
@ -843,7 +809,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
|||
}
|
||||
|
||||
// Update board and piece lists
|
||||
remove_piece(captured, capsq);
|
||||
remove_piece(capsq);
|
||||
|
||||
if (type_of(m) == ENPASSANT)
|
||||
board[capsq] = NO_PIECE;
|
||||
|
||||
// Update material hash key and prefetch access to materialTable
|
||||
k ^= Zobrist::psq[captured][capsq];
|
||||
|
@ -893,7 +862,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
|||
piece_no0 = piece_no_of(from);
|
||||
#endif // defined(EVAL_NNUE)
|
||||
|
||||
move_piece(pc, from, to);
|
||||
move_piece(from, to);
|
||||
|
||||
#if defined(EVAL_NNUE)
|
||||
dp.pieceNo[0] = piece_no0;
|
||||
|
@ -909,7 +878,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
|||
{
|
||||
// Set en-passant square if the moved pawn can be captured
|
||||
if ( (int(to) ^ int(from)) == 16
|
||||
&& (attacks_from<PAWN>(to - pawn_push(us), us) & pieces(them, PAWN)))
|
||||
&& (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN)))
|
||||
{
|
||||
st->epSquare = to - pawn_push(us);
|
||||
k ^= Zobrist::enpassant[file_of(st->epSquare)];
|
||||
|
@ -922,7 +891,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
|||
assert(relative_rank(us, to) == RANK_8);
|
||||
assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
|
||||
|
||||
remove_piece(pc, to);
|
||||
remove_piece(to);
|
||||
put_piece(promotion, to);
|
||||
|
||||
#if defined(EVAL_NNUE)
|
||||
|
@ -944,7 +913,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
|||
st->nonPawnMaterial[us] += PieceValue[MG][promotion];
|
||||
}
|
||||
|
||||
// Update pawn hash key and prefetch access to pawnsTable
|
||||
// Update pawn hash key
|
||||
st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
|
||||
|
||||
// Reset rule 50 draw counter
|
||||
|
@ -973,7 +942,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
|||
if (end >= 4)
|
||||
{
|
||||
StateInfo* stp = st->previous->previous;
|
||||
for (int i=4; i <= end; i += 2)
|
||||
for (int i = 4; i <= end; i += 2)
|
||||
{
|
||||
stp = stp->previous->previous;
|
||||
if (stp->key == st->key)
|
||||
|
@ -1016,7 +985,7 @@ void Position::undo_move(Move m) {
|
|||
assert(type_of(pc) == promotion_type(m));
|
||||
assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN);
|
||||
|
||||
remove_piece(pc, to);
|
||||
remove_piece(to);
|
||||
pc = make_piece(us, PAWN);
|
||||
put_piece(pc, to);
|
||||
|
||||
|
@ -1034,7 +1003,7 @@ void Position::undo_move(Move m) {
|
|||
else
|
||||
{
|
||||
|
||||
move_piece(pc, to, from); // Put the piece back at the source square
|
||||
move_piece(to, from); // Put the piece back at the source square
|
||||
|
||||
#if defined(EVAL_NNUE)
|
||||
PieceNumber piece_no0 = st->dirtyPiece.pieceNo[0];
|
||||
|
@ -1110,9 +1079,9 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ
|
|||
#endif // defined(EVAL_NNUE)
|
||||
|
||||
// Remove both pieces first since squares could overlap in Chess960
|
||||
remove_piece(make_piece(us, KING), Do ? from : to);
|
||||
remove_piece(make_piece(us, ROOK), Do ? rfrom : rto);
|
||||
board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us
|
||||
remove_piece(Do ? from : to);
|
||||
remove_piece(Do ? rfrom : rto);
|
||||
board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do this for us
|
||||
put_piece(make_piece(us, KING), Do ? to : from);
|
||||
put_piece(make_piece(us, ROOK), Do ? rto : rfrom);
|
||||
|
||||
|
@ -1140,7 +1109,7 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ
|
|||
}
|
||||
|
||||
|
||||
/// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips
|
||||
/// Position::do(undo)_null_move() is used to do(undo) a "null move": it flips
|
||||
/// the side to move without executing any move on the board.
|
||||
|
||||
void Position::do_null_move(StateInfo& newSt) {
|
||||
|
@ -1217,77 +1186,96 @@ bool Position::see_ge(Move m, Value threshold) const {
|
|||
if (type_of(m) != NORMAL)
|
||||
return VALUE_ZERO >= threshold;
|
||||
|
||||
Bitboard stmAttackers;
|
||||
Square from = from_sq(m), to = to_sq(m);
|
||||
PieceType nextVictim = type_of(piece_on(from));
|
||||
Color us = color_of(piece_on(from));
|
||||
Color stm = ~us; // First consider opponent's move
|
||||
Value balance; // Values of the pieces taken by us minus opponent's ones
|
||||
|
||||
// The opponent may be able to recapture so this is the best result
|
||||
// we can hope for.
|
||||
balance = PieceValue[MG][piece_on(to)] - threshold;
|
||||
|
||||
if (balance < VALUE_ZERO)
|
||||
int swap = PieceValue[MG][piece_on(to)] - threshold;
|
||||
if (swap < 0)
|
||||
return false;
|
||||
|
||||
// Now assume the worst possible result: that the opponent can
|
||||
// capture our piece for free.
|
||||
balance -= PieceValue[MG][nextVictim];
|
||||
|
||||
// If it is enough (like in PxQ) then return immediately. Note that
|
||||
// in case nextVictim == KING we always return here, this is ok
|
||||
// if the given move is legal.
|
||||
if (balance >= VALUE_ZERO)
|
||||
swap = PieceValue[MG][piece_on(from)] - swap;
|
||||
if (swap <= 0)
|
||||
return true;
|
||||
|
||||
// Find all attackers to the destination square, with the moving piece
|
||||
// removed, but possibly an X-ray attacker added behind it.
|
||||
Bitboard occupied = pieces() ^ from ^ to;
|
||||
Bitboard attackers = attackers_to(to, occupied) & occupied;
|
||||
Color stm = color_of(piece_on(from));
|
||||
Bitboard attackers = attackers_to(to, occupied);
|
||||
Bitboard stmAttackers, bb;
|
||||
int res = 1;
|
||||
|
||||
while (true)
|
||||
{
|
||||
stmAttackers = attackers & pieces(stm);
|
||||
stm = ~stm;
|
||||
attackers &= occupied;
|
||||
|
||||
// If stm has no more attackers then give up: stm loses
|
||||
if (!(stmAttackers = attackers & pieces(stm)))
|
||||
break;
|
||||
|
||||
// Don't allow pinned pieces to attack (except the king) as long as
|
||||
// any pinners are on their original square.
|
||||
// there are pinners on their original square.
|
||||
if (st->pinners[~stm] & occupied)
|
||||
stmAttackers &= ~st->blockersForKing[stm];
|
||||
|
||||
// If stm has no more attackers then give up: stm loses
|
||||
if (!stmAttackers)
|
||||
break;
|
||||
|
||||
res ^= 1;
|
||||
|
||||
// Locate and remove the next least valuable attacker, and add to
|
||||
// the bitboard 'attackers' the possibly X-ray attackers behind it.
|
||||
nextVictim = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
|
||||
|
||||
stm = ~stm; // Switch side to move
|
||||
|
||||
// Negamax the balance with alpha = balance, beta = balance+1 and
|
||||
// add nextVictim's value.
|
||||
//
|
||||
// (balance, balance+1) -> (-balance-1, -balance)
|
||||
//
|
||||
assert(balance < VALUE_ZERO);
|
||||
|
||||
balance = -balance - 1 - PieceValue[MG][nextVictim];
|
||||
|
||||
// If balance is still non-negative after giving away nextVictim then we
|
||||
// win. The only thing to be careful about it is that we should revert
|
||||
// stm if we captured with the king when the opponent still has attackers.
|
||||
if (balance >= VALUE_ZERO)
|
||||
// the bitboard 'attackers' any X-ray attackers behind it.
|
||||
if ((bb = stmAttackers & pieces(PAWN)))
|
||||
{
|
||||
if (nextVictim == KING && (attackers & pieces(stm)))
|
||||
stm = ~stm;
|
||||
break;
|
||||
}
|
||||
assert(nextVictim != KING);
|
||||
}
|
||||
return us != stm; // We break the above loop when stm loses
|
||||
}
|
||||
if ((swap = PawnValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= lsb(bb);
|
||||
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
|
||||
}
|
||||
|
||||
else if ((bb = stmAttackers & pieces(KNIGHT)))
|
||||
{
|
||||
if ((swap = KnightValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= lsb(bb);
|
||||
}
|
||||
|
||||
else if ((bb = stmAttackers & pieces(BISHOP)))
|
||||
{
|
||||
if ((swap = BishopValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= lsb(bb);
|
||||
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
|
||||
}
|
||||
|
||||
else if ((bb = stmAttackers & pieces(ROOK)))
|
||||
{
|
||||
if ((swap = RookValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= lsb(bb);
|
||||
attackers |= attacks_bb<ROOK>(to, occupied) & pieces(ROOK, QUEEN);
|
||||
}
|
||||
|
||||
else if ((bb = stmAttackers & pieces(QUEEN)))
|
||||
{
|
||||
if ((swap = QueenValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= lsb(bb);
|
||||
attackers |= (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN))
|
||||
| (attacks_bb<ROOK >(to, occupied) & pieces(ROOK , QUEEN));
|
||||
}
|
||||
|
||||
else // KING
|
||||
// If we "capture" with the king but opponent still has attackers,
|
||||
// reverse the result.
|
||||
return (attackers & ~pieces(stm)) ? res ^ 1 : res;
|
||||
}
|
||||
|
||||
return bool(res);
|
||||
}
|
||||
|
||||
/// Position::is_draw() tests whether the position is drawn by 50-move rule
|
||||
/// or by repetition. It does not detect stalemates.
|
||||
|
@ -1299,10 +1287,7 @@ bool Position::is_draw(int ply) const {
|
|||
|
||||
// Return a draw score if a position repeats once earlier but strictly
|
||||
// after the root, or repeats twice before or at the root.
|
||||
if (st->repetition && st->repetition < ply)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return st->repetition && st->repetition < ply;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1356,10 +1341,10 @@ bool Position::has_game_cycle(int ply) const {
|
|||
if (ply > i)
|
||||
return true;
|
||||
|
||||
// For nodes before or at the root, check that the move is a repetition one
|
||||
// rather than a move to the current position.
|
||||
// In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in the same
|
||||
// location, so we have to select which square to check.
|
||||
// For nodes before or at the root, check that the move is a
|
||||
// repetition rather than a move to the current position.
|
||||
// In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in
|
||||
// the same location, so we have to select which square to check.
|
||||
if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move())
|
||||
continue;
|
||||
|
||||
|
@ -1463,15 +1448,15 @@ bool Position::pos_is_ok() const {
|
|||
assert(0 && "pos_is_ok: Index");
|
||||
}
|
||||
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
|
||||
for (Color c : { WHITE, BLACK })
|
||||
for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE})
|
||||
{
|
||||
if (!can_castle(c | s))
|
||||
if (!can_castle(cr))
|
||||
continue;
|
||||
|
||||
if ( piece_on(castlingRookSquare[c | s]) != make_piece(c, ROOK)
|
||||
|| castlingRightsMask[castlingRookSquare[c | s]] != (c | s)
|
||||
|| (castlingRightsMask[square<KING>(c)] & (c | s)) != (c | s))
|
||||
if ( piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK)
|
||||
|| castlingRightsMask[castlingRookSquare[cr]] != cr
|
||||
|| (castlingRightsMask[square<KING>(c)] & cr) != cr)
|
||||
assert(0 && "pos_is_ok: Castling");
|
||||
}
|
||||
|
||||
|
|
104
src/position.h
104
src/position.h
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -51,7 +51,6 @@ struct StateInfo {
|
|||
Square epSquare;
|
||||
|
||||
// Not copied when making a move (will be recomputed anyhow)
|
||||
int repetition;
|
||||
Key key;
|
||||
Bitboard checkersBB;
|
||||
Piece capturedPiece;
|
||||
|
@ -59,6 +58,7 @@ struct StateInfo {
|
|||
Bitboard blockersForKing[COLOR_NB];
|
||||
Bitboard pinners[COLOR_NB];
|
||||
Bitboard checkSquares[PIECE_TYPE_NB];
|
||||
int repetition;
|
||||
|
||||
#if defined(EVAL_NNUE)
|
||||
Eval::NNUE::Accumulator accumulator;
|
||||
|
@ -98,7 +98,6 @@ public:
|
|||
const std::string fen() const;
|
||||
|
||||
// Position representation
|
||||
Bitboard pieces() const;
|
||||
Bitboard pieces(PieceType pt) const;
|
||||
Bitboard pieces(PieceType pt1, PieceType pt2) const;
|
||||
Bitboard pieces(Color c) const;
|
||||
|
@ -114,22 +113,20 @@ public:
|
|||
bool is_on_semiopen_file(Color c, Square s) const;
|
||||
|
||||
// Castling
|
||||
int castling_rights(Color c) const;
|
||||
bool can_castle(CastlingRight cr) const;
|
||||
bool castling_impeded(CastlingRight cr) const;
|
||||
Square castling_rook_square(CastlingRight cr) const;
|
||||
CastlingRights castling_rights(Color c) const;
|
||||
bool can_castle(CastlingRights cr) const;
|
||||
bool castling_impeded(CastlingRights cr) const;
|
||||
Square castling_rook_square(CastlingRights cr) const;
|
||||
|
||||
// Checking
|
||||
Bitboard checkers() const;
|
||||
Bitboard blockers_for_king(Color c) const;
|
||||
Bitboard check_squares(PieceType pt) const;
|
||||
bool is_discovery_check_on_king(Color c, Move m) const;
|
||||
|
||||
// Attacks to/from a given square
|
||||
Bitboard attackers_to(Square s) const;
|
||||
Bitboard attackers_to(Square s, Bitboard occupied) const;
|
||||
Bitboard attacks_from(PieceType pt, Square s) const;
|
||||
template<PieceType> Bitboard attacks_from(Square s) const;
|
||||
template<PieceType> Bitboard attacks_from(Square s, Color c) const;
|
||||
Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const;
|
||||
|
||||
// Properties of moves
|
||||
|
@ -219,8 +216,8 @@ private:
|
|||
|
||||
// Other helpers
|
||||
void put_piece(Piece pc, Square s);
|
||||
void remove_piece(Piece pc, Square s);
|
||||
void move_piece(Piece pc, Square from, Square to);
|
||||
void remove_piece(Square s);
|
||||
void move_piece(Square from, Square to);
|
||||
template<bool Do>
|
||||
void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto);
|
||||
|
||||
|
@ -262,28 +259,25 @@ inline Color Position::side_to_move() const {
|
|||
return sideToMove;
|
||||
}
|
||||
|
||||
inline bool Position::empty(Square s) const {
|
||||
return board[s] == NO_PIECE;
|
||||
}
|
||||
|
||||
inline Piece Position::piece_on(Square s) const {
|
||||
assert(is_ok(s));
|
||||
return board[s];
|
||||
}
|
||||
|
||||
inline bool Position::empty(Square s) const {
|
||||
return piece_on(s) == NO_PIECE;
|
||||
}
|
||||
|
||||
inline Piece Position::moved_piece(Move m) const {
|
||||
return board[from_sq(m)];
|
||||
return piece_on(from_sq(m));
|
||||
}
|
||||
|
||||
inline Bitboard Position::pieces() const {
|
||||
return byTypeBB[ALL_PIECES];
|
||||
}
|
||||
|
||||
inline Bitboard Position::pieces(PieceType pt) const {
|
||||
inline Bitboard Position::pieces(PieceType pt = ALL_PIECES) const {
|
||||
return byTypeBB[pt];
|
||||
}
|
||||
|
||||
inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const {
|
||||
return byTypeBB[pt1] | byTypeBB[pt2];
|
||||
return pieces(pt1) | pieces(pt2);
|
||||
}
|
||||
|
||||
inline Bitboard Position::pieces(Color c) const {
|
||||
|
@ -291,11 +285,11 @@ inline Bitboard Position::pieces(Color c) const {
|
|||
}
|
||||
|
||||
inline Bitboard Position::pieces(Color c, PieceType pt) const {
|
||||
return byColorBB[c] & byTypeBB[pt];
|
||||
return pieces(c) & pieces(pt);
|
||||
}
|
||||
|
||||
inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const {
|
||||
return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]);
|
||||
return pieces(c) & (pieces(pt1) | pieces(pt2));
|
||||
}
|
||||
|
||||
template<PieceType Pt> inline int Position::count(Color c) const {
|
||||
|
@ -303,7 +297,7 @@ template<PieceType Pt> inline int Position::count(Color c) const {
|
|||
}
|
||||
|
||||
template<PieceType Pt> inline int Position::count() const {
|
||||
return pieceCount[make_piece(WHITE, Pt)] + pieceCount[make_piece(BLACK, Pt)];
|
||||
return count<Pt>(WHITE) + count<Pt>(BLACK);
|
||||
}
|
||||
|
||||
template<PieceType Pt> inline const Square* Position::squares(Color c) const {
|
||||
|
@ -312,7 +306,7 @@ template<PieceType Pt> inline const Square* Position::squares(Color c) const {
|
|||
|
||||
template<PieceType Pt> inline Square Position::square(Color c) const {
|
||||
assert(pieceCount[make_piece(c, Pt)] == 1);
|
||||
return pieceList[make_piece(c, Pt)][0];
|
||||
return squares<Pt>(c)[0];
|
||||
}
|
||||
|
||||
inline Square Position::ep_square() const {
|
||||
|
@ -323,41 +317,28 @@ inline bool Position::is_on_semiopen_file(Color c, Square s) const {
|
|||
return !(pieces(c, PAWN) & file_bb(s));
|
||||
}
|
||||
|
||||
inline bool Position::can_castle(CastlingRight cr) const {
|
||||
inline bool Position::can_castle(CastlingRights cr) const {
|
||||
return st->castlingRights & cr;
|
||||
}
|
||||
|
||||
inline int Position::castling_rights(Color c) const {
|
||||
return st->castlingRights & (c == WHITE ? WHITE_CASTLING : BLACK_CASTLING);
|
||||
inline CastlingRights Position::castling_rights(Color c) const {
|
||||
return c & CastlingRights(st->castlingRights);
|
||||
}
|
||||
|
||||
inline bool Position::castling_impeded(CastlingRight cr) const {
|
||||
return byTypeBB[ALL_PIECES] & castlingPath[cr];
|
||||
inline bool Position::castling_impeded(CastlingRights cr) const {
|
||||
assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO);
|
||||
|
||||
return pieces() & castlingPath[cr];
|
||||
}
|
||||
|
||||
inline Square Position::castling_rook_square(CastlingRight cr) const {
|
||||
inline Square Position::castling_rook_square(CastlingRights cr) const {
|
||||
assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO);
|
||||
|
||||
return castlingRookSquare[cr];
|
||||
}
|
||||
|
||||
template<PieceType Pt>
|
||||
inline Bitboard Position::attacks_from(Square s) const {
|
||||
assert(Pt != PAWN);
|
||||
return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, byTypeBB[ALL_PIECES])
|
||||
: Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
|
||||
: PseudoAttacks[Pt][s];
|
||||
}
|
||||
|
||||
template<>
|
||||
inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const {
|
||||
return PawnAttacks[c][s];
|
||||
}
|
||||
|
||||
inline Bitboard Position::attacks_from(PieceType pt, Square s) const {
|
||||
return attacks_bb(pt, s, byTypeBB[ALL_PIECES]);
|
||||
}
|
||||
|
||||
inline Bitboard Position::attackers_to(Square s) const {
|
||||
return attackers_to(s, byTypeBB[ALL_PIECES]);
|
||||
return attackers_to(s, pieces());
|
||||
}
|
||||
|
||||
inline Bitboard Position::checkers() const {
|
||||
|
@ -372,6 +353,10 @@ inline Bitboard Position::check_squares(PieceType pt) const {
|
|||
return st->checkSquares[pt];
|
||||
}
|
||||
|
||||
inline bool Position::is_discovery_check_on_king(Color c, Move m) const {
|
||||
return st->blockersForKing[c] & from_sq(m);
|
||||
}
|
||||
|
||||
inline bool Position::pawn_passed(Color c, Square s) const {
|
||||
return !(pieces(~c, PAWN) & passed_pawn_span(c, s));
|
||||
}
|
||||
|
@ -406,7 +391,7 @@ inline Value Position::non_pawn_material(Color c) const {
|
|||
}
|
||||
|
||||
inline Value Position::non_pawn_material() const {
|
||||
return st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK];
|
||||
return non_pawn_material(WHITE) + non_pawn_material(BLACK);
|
||||
}
|
||||
|
||||
inline int Position::game_ply() const {
|
||||
|
@ -418,8 +403,8 @@ inline int Position::rule50_count() const {
|
|||
}
|
||||
|
||||
inline bool Position::opposite_bishops() const {
|
||||
return pieceCount[W_BISHOP] == 1
|
||||
&& pieceCount[B_BISHOP] == 1
|
||||
return count<BISHOP>(WHITE) == 1
|
||||
&& count<BISHOP>(BLACK) == 1
|
||||
&& opposite_colors(square<BISHOP>(WHITE), square<BISHOP>(BLACK));
|
||||
}
|
||||
|
||||
|
@ -449,8 +434,7 @@ inline Thread* Position::this_thread() const {
|
|||
inline void Position::put_piece(Piece pc, Square s) {
|
||||
|
||||
board[s] = pc;
|
||||
byTypeBB[ALL_PIECES] |= s;
|
||||
byTypeBB[type_of(pc)] |= s;
|
||||
byTypeBB[ALL_PIECES] |= byTypeBB[type_of(pc)] |= s;
|
||||
byColorBB[color_of(pc)] |= s;
|
||||
index[s] = pieceCount[pc]++;
|
||||
pieceList[pc][index[s]] = s;
|
||||
|
@ -458,12 +442,13 @@ inline void Position::put_piece(Piece pc, Square s) {
|
|||
psq += PSQT::psq[pc][s];
|
||||
}
|
||||
|
||||
inline void Position::remove_piece(Piece pc, 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];
|
||||
byTypeBB[ALL_PIECES] ^= s;
|
||||
byTypeBB[type_of(pc)] ^= s;
|
||||
byColorBB[color_of(pc)] ^= s;
|
||||
|
@ -476,11 +461,12 @@ inline void Position::remove_piece(Piece pc, Square s) {
|
|||
psq -= PSQT::psq[pc][s];
|
||||
}
|
||||
|
||||
inline void Position::move_piece(Piece pc, 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.
|
||||
Bitboard fromTo = square_bb(from) | square_bb(to);
|
||||
Piece pc = board[from];
|
||||
Bitboard fromTo = from | to;
|
||||
byTypeBB[ALL_PIECES] ^= fromTo;
|
||||
byTypeBB[type_of(pc)] ^= fromTo;
|
||||
byColorBB[color_of(pc)] ^= fromTo;
|
||||
|
|
91
src/psqt.cpp
91
src/psqt.cpp
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -21,11 +21,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
Value PieceValue[PHASE_NB][PIECE_NB] = {
|
||||
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
|
||||
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg }
|
||||
};
|
||||
#include "bitboard.h"
|
||||
|
||||
namespace PSQT {
|
||||
|
||||
|
@ -39,34 +35,34 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
|
|||
{ },
|
||||
{ },
|
||||
{ // Knight
|
||||
{ S(-169,-105), S(-96,-74), S(-80,-46), S(-79,-18) },
|
||||
{ S( -79, -70), S(-39,-56), S(-24,-15), S( -9, 6) },
|
||||
{ S( -64, -38), S(-20,-33), S( 4, -5), S( 19, 27) },
|
||||
{ S( -28, -36), S( 5, 0), S( 41, 13), S( 47, 34) },
|
||||
{ S( -29, -41), S( 13,-20), S( 42, 4), S( 52, 35) },
|
||||
{ S( -11, -51), S( 28,-38), S( 63,-17), S( 55, 19) },
|
||||
{ S( -67, -64), S(-21,-45), S( 6,-37), S( 37, 16) },
|
||||
{ S(-200, -98), S(-80,-89), S(-53,-53), S(-32,-16) }
|
||||
{ S(-175, -96), S(-92,-65), S(-74,-49), S(-73,-21) },
|
||||
{ S( -77, -67), S(-41,-54), S(-27,-18), S(-15, 8) },
|
||||
{ S( -61, -40), S(-17,-27), S( 6, -8), S( 12, 29) },
|
||||
{ S( -35, -35), S( 8, -2), S( 40, 13), S( 49, 28) },
|
||||
{ S( -34, -45), S( 13,-16), S( 44, 9), S( 51, 39) },
|
||||
{ S( -9, -51), S( 22,-44), S( 58,-16), S( 53, 17) },
|
||||
{ S( -67, -69), S(-27,-50), S( 4,-51), S( 37, 12) },
|
||||
{ S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) }
|
||||
},
|
||||
{ // Bishop
|
||||
{ S(-44,-63), S( -4,-30), S(-11,-35), S(-28, -8) },
|
||||
{ S(-18,-38), S( 7,-13), S( 14,-14), S( 3, 0) },
|
||||
{ S( -8,-18), S( 24, 0), S( -3, -7), S( 15, 13) },
|
||||
{ S( 1,-26), S( 8, -3), S( 26, 1), S( 37, 16) },
|
||||
{ S( -7,-24), S( 30, -6), S( 23,-10), S( 28, 17) },
|
||||
{ S(-17,-26), S( 4, 2), S( -1, 1), S( 8, 16) },
|
||||
{ S(-21,-34), S(-19,-18), S( 10, -7), S( -6, 9) },
|
||||
{ S(-48,-51), S( -3,-40), S(-12,-39), S(-25,-20) }
|
||||
{ S(-53,-57), S( -5,-30), S( -8,-37), S(-23,-12) },
|
||||
{ S(-15,-37), S( 8,-13), S( 19,-17), S( 4, 1) },
|
||||
{ S( -7,-16), S( 21, -1), S( -5, -2), S( 17, 10) },
|
||||
{ S( -5,-20), S( 11, -6), S( 25, 0), S( 39, 17) },
|
||||
{ S(-12,-17), S( 29, -1), S( 22,-14), S( 31, 15) },
|
||||
{ S(-16,-30), S( 6, 6), S( 1, 4), S( 11, 6) },
|
||||
{ S(-17,-31), S(-14,-20), S( 5, -1), S( 0, 1) },
|
||||
{ S(-48,-46), S( 1,-42), S(-14,-37), S(-23,-24) }
|
||||
},
|
||||
{ // Rook
|
||||
{ S(-24, -2), S(-13,-6), S(-7, -3), S( 2,-2) },
|
||||
{ S(-18,-10), S(-10,-7), S(-5, 1), S( 9, 0) },
|
||||
{ S(-21, 10), S( -7,-4), S( 3, 2), S(-1,-2) },
|
||||
{ S(-13, -5), S( -5, 2), S(-4, -8), S(-6, 8) },
|
||||
{ S(-24, -8), S(-12, 5), S(-1, 4), S( 6,-9) },
|
||||
{ S(-24, 3), S( -4,-2), S( 4,-10), S(10, 7) },
|
||||
{ S( -8, 1), S( 6, 2), S(10, 17), S(12,-8) },
|
||||
{ S(-22, 12), S(-24,-6), S(-6, 13), S( 4, 7) }
|
||||
{ S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) },
|
||||
{ S(-21,-12), S(-13, -9), S( -8, -1), S( 6, -2) },
|
||||
{ S(-25, 6), S(-11, -8), S( -1, -2), S( 3, -6) },
|
||||
{ S(-13, -6), S( -5, 1), S( -4, -9), S(-6, 7) },
|
||||
{ S(-27, -5), S(-15, 8), S( -4, 7), S( 3, -6) },
|
||||
{ S(-22, 6), S( -2, 1), S( 6, -7), S(12, 10) },
|
||||
{ S( -2, 4), S( 12, 5), S( 16, 20), S(18, -5) },
|
||||
{ S(-17, 18), S(-19, 0), S( -1, 19), S( 9, 13) }
|
||||
},
|
||||
{ // Queen
|
||||
{ S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) },
|
||||
|
@ -79,26 +75,26 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
|
|||
{ S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) }
|
||||
},
|
||||
{ // King
|
||||
{ S(272, 0), S(325, 41), S(273, 80), S(190, 93) },
|
||||
{ S(277, 57), S(305, 98), S(241,138), S(183,131) },
|
||||
{ S(198, 86), S(253,138), S(168,165), S(120,173) },
|
||||
{ S(169,103), S(191,152), S(136,168), S(108,169) },
|
||||
{ S(145, 98), S(176,166), S(112,197), S( 69,194) },
|
||||
{ S(122, 87), S(159,164), S( 85,174), S( 36,189) },
|
||||
{ S( 87, 40), S(120, 99), S( 64,128), S( 25,141) },
|
||||
{ S( 64, 5), S( 87, 60), S( 49, 75), S( 0, 75) }
|
||||
{ S(271, 1), S(327, 45), S(271, 85), S(198, 76) },
|
||||
{ S(278, 53), S(303,100), S(234,133), S(179,135) },
|
||||
{ S(195, 88), S(258,130), S(169,169), S(120,175) },
|
||||
{ S(164,103), S(190,156), S(138,172), S( 98,172) },
|
||||
{ S(154, 96), S(179,166), S(105,199), S( 70,199) },
|
||||
{ S(123, 92), S(145,172), S( 81,184), S( 31,191) },
|
||||
{ S( 88, 47), S(120,121), S( 65,116), S( 33,131) },
|
||||
{ S( 59, 11), S( 89, 59), S( 45, 73), S( -1, 78) }
|
||||
}
|
||||
};
|
||||
|
||||
constexpr Score PBonus[RANK_NB][FILE_NB] =
|
||||
{ // Pawn (asymmetric distribution)
|
||||
{ },
|
||||
{ S( 0,-10), S( -5,-3), S( 10, 7), S( 13,-1), S( 21, 7), S( 17, 6), S( 6, 1), S( -3,-20) },
|
||||
{ S(-11, -6), S(-10,-6), S( 15,-1), S( 22,-1), S( 26, -1), S( 28, 2), S( 4,-2), S(-24, -5) },
|
||||
{ S( -9, 4), S(-18,-5), S( 8,-4), S( 22,-5), S( 33, -6), S( 25,-13), S( -4,-3), S(-16, -7) },
|
||||
{ S( 6, 18), S( -3, 2), S(-10, 2), S( 1,-9), S( 12,-13), S( 6, -8), S(-12,11), S( 1, 9) },
|
||||
{ S( -6, 25), S( -8,17), S( 5,19), S( 11,29), S(-14, 29), S( 0, 8), S(-12, 4), S(-14, 12) },
|
||||
{ S(-10, -1), S( 6,-6), S( -5,18), S(-11,22), S( -2, 22), S(-14, 17), S( 12, 2), S( -1, 9) }
|
||||
{ S( 3,-10), S( 3, -6), S( 10, 10), S( 19, 0), S( 16, 14), S( 19, 7), S( 7, -5), S( -5,-19) },
|
||||
{ S( -9,-10), S(-15,-10), S( 11,-10), S( 15, 4), S( 32, 4), S( 22, 3), S( 5, -6), S(-22, -4) },
|
||||
{ S( -8, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S(-12, -9) },
|
||||
{ S( 13, 9), S( 0, 4), S(-13, 3), S( 1,-12), S( 11,-12), S( -2, -6), S(-13, 13), S( 5, 8) },
|
||||
{ S( -5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S(-18, 13) },
|
||||
{ S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) }
|
||||
};
|
||||
|
||||
#undef S
|
||||
|
@ -112,17 +108,14 @@ void init() {
|
|||
|
||||
for (Piece pc = W_PAWN; pc <= W_KING; ++pc)
|
||||
{
|
||||
PieceValue[MG][~pc] = PieceValue[MG][pc];
|
||||
PieceValue[EG][~pc] = PieceValue[EG][pc];
|
||||
|
||||
Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
|
||||
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
{
|
||||
File f = std::min(file_of(s), ~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)]
|
||||
: Bonus[pc][rank_of(s)][f]);
|
||||
psq[~pc][~s] = -psq[pc][s];
|
||||
psq[~pc][flip_rank(s)] = -psq[pc][s];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
854
src/search.cpp
854
src/search.cpp
File diff suppressed because it is too large
Load diff
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -49,6 +49,7 @@ struct Stack {
|
|||
Value staticEval;
|
||||
int statScore;
|
||||
int moveCount;
|
||||
bool inCheck;
|
||||
};
|
||||
|
||||
|
||||
|
@ -69,7 +70,8 @@ struct RootMove {
|
|||
Value score = -VALUE_INFINITE;
|
||||
Value previousScore = -VALUE_INFINITE;
|
||||
int selDepth = 0;
|
||||
int tbRank;
|
||||
int tbRank = 0;
|
||||
int bestMoveCount = 0;
|
||||
Value tbScore;
|
||||
std::vector<Move> pv;
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (c) 2013 Ronald de Man
|
||||
Copyright (C) 2016-2019 Marco Costalba, Lucas Braesch
|
||||
Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -27,12 +27,12 @@
|
|||
#include <list>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
#include <mutex>
|
||||
|
||||
#include "../bitboard.h"
|
||||
#include "../movegen.h"
|
||||
#include "../position.h"
|
||||
#include "../search.h"
|
||||
#include "../thread_win32_osx.h"
|
||||
#include "../types.h"
|
||||
#include "../uci.h"
|
||||
|
||||
|
@ -45,7 +45,9 @@
|
|||
#include <sys/stat.h>
|
||||
#else
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#ifndef NOMINMAX
|
||||
# define NOMINMAX // Disable macros min() and max()
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
|
@ -58,13 +60,12 @@ namespace {
|
|||
constexpr int TBPIECES = 7; // Max number of supported pieces
|
||||
|
||||
enum { BigEndian, LittleEndian };
|
||||
enum TBType { KEY, WDL, DTZ }; // Used as template parameter
|
||||
enum TBType { WDL, DTZ }; // Used as template parameter
|
||||
|
||||
// Each table has a set of flags: all of them refer to DTZ tables, the last one to WDL tables
|
||||
enum TBFlag { STM = 1, Mapped = 2, WinPlies = 4, LossPlies = 8, Wide = 16, SingleValue = 128 };
|
||||
|
||||
inline WDLScore operator-(WDLScore d) { return WDLScore(-int(d)); }
|
||||
inline Square operator^=(Square& s, int i) { return s = Square(int(s) ^ i); }
|
||||
inline Square operator^(Square s, int i) { return Square(int(s) ^ i); }
|
||||
|
||||
const std::string PieceToChar = " PNBRQK pnbrqk";
|
||||
|
@ -367,7 +368,7 @@ TBTable<WDL>::TBTable(const std::string& code) : TBTable() {
|
|||
hasPawns = pos.pieces(PAWN);
|
||||
|
||||
hasUniquePieces = false;
|
||||
for (Color c = WHITE; c <= BLACK; ++c)
|
||||
for (Color c : { WHITE, BLACK })
|
||||
for (PieceType pt = PAWN; pt < KING; ++pt)
|
||||
if (popcount(pos.pieces(c, pt)) == 1)
|
||||
hasUniquePieces = true;
|
||||
|
@ -402,7 +403,17 @@ TBTable<DTZ>::TBTable(const TBTable<WDL>& wdl) : TBTable() {
|
|||
// at init time, accessed at probe time.
|
||||
class TBTables {
|
||||
|
||||
typedef std::tuple<Key, TBTable<WDL>*, TBTable<DTZ>*> Entry;
|
||||
struct Entry
|
||||
{
|
||||
Key key;
|
||||
TBTable<WDL>* wdl;
|
||||
TBTable<DTZ>* dtz;
|
||||
|
||||
template <TBType Type>
|
||||
TBTable<Type>* get() const {
|
||||
return (TBTable<Type>*)(Type == WDL ? (void*)wdl : (void*)dtz);
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr int Size = 1 << 12; // 4K table, indexed by key's 12 lsb
|
||||
static constexpr int Overflow = 1; // Number of elements allowed to map to the last bucket
|
||||
|
@ -414,12 +425,12 @@ class TBTables {
|
|||
|
||||
void insert(Key key, TBTable<WDL>* wdl, TBTable<DTZ>* dtz) {
|
||||
uint32_t homeBucket = (uint32_t)key & (Size - 1);
|
||||
Entry entry = std::make_tuple(key, wdl, dtz);
|
||||
Entry entry{ key, wdl, dtz };
|
||||
|
||||
// Ensure last element is empty to avoid overflow when looking up
|
||||
for (uint32_t bucket = homeBucket; bucket < Size + Overflow - 1; ++bucket) {
|
||||
Key otherKey = std::get<KEY>(hashTable[bucket]);
|
||||
if (otherKey == key || !std::get<WDL>(hashTable[bucket])) {
|
||||
Key otherKey = hashTable[bucket].key;
|
||||
if (otherKey == key || !hashTable[bucket].get<WDL>()) {
|
||||
hashTable[bucket] = entry;
|
||||
return;
|
||||
}
|
||||
|
@ -428,7 +439,7 @@ class TBTables {
|
|||
// insert here and search for a new spot for the other element instead.
|
||||
uint32_t otherHomeBucket = (uint32_t)otherKey & (Size - 1);
|
||||
if (otherHomeBucket > homeBucket) {
|
||||
swap(entry, hashTable[bucket]);
|
||||
std::swap(entry, hashTable[bucket]);
|
||||
key = otherKey;
|
||||
homeBucket = otherHomeBucket;
|
||||
}
|
||||
|
@ -441,8 +452,8 @@ public:
|
|||
template<TBType Type>
|
||||
TBTable<Type>* get(Key key) {
|
||||
for (const Entry* entry = &hashTable[(uint32_t)key & (Size - 1)]; ; ++entry) {
|
||||
if (std::get<KEY>(*entry) == key || !std::get<Type>(*entry))
|
||||
return std::get<Type>(*entry);
|
||||
if (entry->key == key || !entry->get<Type>())
|
||||
return entry->get<Type>();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -519,7 +530,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) {
|
|||
// I(k) = k * d->span + d->span / 2 (1)
|
||||
|
||||
// First step is to get the 'k' of the I(k) nearest to our idx, using definition (1)
|
||||
uint32_t k = idx / d->span;
|
||||
uint32_t k = uint32_t(idx / d->span);
|
||||
|
||||
// Then we read the corresponding SparseIndex[] entry
|
||||
uint32_t block = number<uint32_t, LittleEndian>(&d->sparseIndex[k].block);
|
||||
|
@ -565,7 +576,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) {
|
|||
// All the symbols of a given length are consecutive integers (numerical
|
||||
// sequence property), so we can compute the offset of our symbol of
|
||||
// length len, stored at the beginning of buf64.
|
||||
sym = (buf64 - d->base64[len]) >> (64 - len - d->minSymLen);
|
||||
sym = Sym((buf64 - d->base64[len]) >> (64 - len - d->minSymLen));
|
||||
|
||||
// Now add the value of the lowest symbol of length len to get our symbol
|
||||
sym += number<Sym, LittleEndian>(&d->lowestSym[len]);
|
||||
|
@ -681,7 +692,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
|
|||
bool blackStronger = (pos.material_key() != entry->key);
|
||||
|
||||
int flipColor = (symmetricBlackToMove || blackStronger) * 8;
|
||||
int flipSquares = (symmetricBlackToMove || blackStronger) * 070;
|
||||
int flipSquares = (symmetricBlackToMove || blackStronger) * 56;
|
||||
int stm = (symmetricBlackToMove || blackStronger) ^ pos.side_to_move();
|
||||
|
||||
// For pawns, TB files store 4 separate tables according if leading pawn is on
|
||||
|
@ -704,9 +715,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
|
|||
|
||||
std::swap(squares[0], *std::max_element(squares, squares + leadPawnsCnt, pawns_comp));
|
||||
|
||||
tbFile = file_of(squares[0]);
|
||||
if (tbFile > FILE_D)
|
||||
tbFile = file_of(squares[0] ^ 7); // Horizontal flip: SQ_H1 -> SQ_A1
|
||||
tbFile = File(edge_distance(file_of(squares[0])));
|
||||
}
|
||||
|
||||
// DTZ tables are one-sided, i.e. they store positions only for white to
|
||||
|
@ -730,8 +739,8 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
|
|||
|
||||
// Then we reorder the pieces to have the same sequence as the one stored
|
||||
// in pieces[i]: the sequence that ensures the best compression.
|
||||
for (int i = leadPawnsCnt; i < size; ++i)
|
||||
for (int j = i; j < size; ++j)
|
||||
for (int i = leadPawnsCnt; i < size - 1; ++i)
|
||||
for (int j = i + 1; j < size; ++j)
|
||||
if (d->pieces[i] == pieces[j])
|
||||
{
|
||||
std::swap(pieces[i], pieces[j]);
|
||||
|
@ -743,7 +752,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
|
|||
// the triangle A1-D1-D4.
|
||||
if (file_of(squares[0]) > FILE_D)
|
||||
for (int i = 0; i < size; ++i)
|
||||
squares[i] ^= 7; // Horizontal flip: SQ_H1 -> SQ_A1
|
||||
squares[i] = flip_file(squares[i]);
|
||||
|
||||
// Encode leading pawns starting with the one with minimum MapPawns[] and
|
||||
// proceeding in ascending order.
|
||||
|
@ -762,7 +771,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
|
|||
// piece is below RANK_5.
|
||||
if (rank_of(squares[0]) > RANK_4)
|
||||
for (int i = 0; i < size; ++i)
|
||||
squares[i] ^= 070; // Vertical flip: SQ_A8 -> SQ_A1
|
||||
squares[i] = flip_rank(squares[i]);
|
||||
|
||||
// Look for the first piece of the leading group not on the A1-D4 diagonal
|
||||
// and ensure it is mapped below the diagonal.
|
||||
|
@ -770,7 +779,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
|
|||
if (!off_A1H8(squares[i]))
|
||||
continue;
|
||||
|
||||
if (off_A1H8(squares[i]) > 0) // A1-H8 diagonal flip: SQ_A3 -> SQ_C3
|
||||
if (off_A1H8(squares[i]) > 0) // A1-H8 diagonal flip: SQ_A3 -> SQ_C1
|
||||
for (int j = i; j < size; ++j)
|
||||
squares[j] = Square(((squares[j] >> 3) | (squares[j] << 3)) & 63);
|
||||
break;
|
||||
|
@ -975,7 +984,7 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) {
|
|||
|
||||
d->sizeofBlock = 1ULL << *data++;
|
||||
d->span = 1ULL << *data++;
|
||||
d->sparseIndexSize = (tbSize + d->span - 1) / d->span; // Round up
|
||||
d->sparseIndexSize = size_t((tbSize + d->span - 1) / d->span); // Round up
|
||||
auto padding = number<uint8_t, LittleEndian>(data++);
|
||||
d->blocksNum = number<uint32_t, LittleEndian>(data); data += sizeof(uint32_t);
|
||||
d->blockLengthSize = d->blocksNum + padding; // Padded to ensure SparseIndex[]
|
||||
|
@ -1060,8 +1069,8 @@ void set(T& e, uint8_t* data) {
|
|||
|
||||
enum { Split = 1, HasPawns = 2 };
|
||||
|
||||
assert(e.hasPawns == !!(*data & HasPawns));
|
||||
assert((e.key != e.key2) == !!(*data & Split));
|
||||
assert(e.hasPawns == bool(*data & HasPawns));
|
||||
assert((e.key != e.key2) == bool(*data & Split));
|
||||
|
||||
data++; // First byte stores flags
|
||||
|
||||
|
@ -1124,14 +1133,14 @@ void set(T& e, uint8_t* data) {
|
|||
template<TBType Type>
|
||||
void* mapped(TBTable<Type>& e, const Position& pos) {
|
||||
|
||||
static Mutex mutex;
|
||||
static std::mutex mutex;
|
||||
|
||||
// Use 'acquire' to avoid a thread reading 'ready' == true while
|
||||
// another is still working. (compiler reordering may cause this).
|
||||
if (e.ready.load(std::memory_order_acquire))
|
||||
return e.baseAddress; // Could be nullptr if file does not exist
|
||||
|
||||
std::unique_lock<Mutex> lk(mutex);
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
|
||||
if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock
|
||||
return e.baseAddress;
|
||||
|
@ -1344,7 +1353,7 @@ void Tablebases::init(const std::string& paths) {
|
|||
if (leadPawnsCnt == 1)
|
||||
{
|
||||
MapPawns[sq] = availableSquares--;
|
||||
MapPawns[sq ^ 7] = availableSquares--; // Horizontal flip
|
||||
MapPawns[flip_file(sq)] = availableSquares--;
|
||||
}
|
||||
LeadPawnIdx[leadPawnsCnt][sq] = idx;
|
||||
idx += Binomial[leadPawnsCnt - 1][MapPawns[sq]];
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (c) 2013 Ronald de Man
|
||||
Copyright (C) 2016-2019 Marco Costalba, Lucas Braesch
|
||||
Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -52,6 +52,15 @@ Thread::~Thread() {
|
|||
stdThread.join();
|
||||
}
|
||||
|
||||
/// Thread::bestMoveCount(Move move) return best move counter for the given root move
|
||||
|
||||
int Thread::best_move_count(Move move) const {
|
||||
|
||||
auto rm = std::find(rootMoves.begin() + pvIdx,
|
||||
rootMoves.begin() + pvLast, move);
|
||||
|
||||
return rm != rootMoves.begin() + pvLast ? rm->bestMoveCount : 0;
|
||||
}
|
||||
|
||||
/// Thread::clear() reset histories, usually before a new game
|
||||
|
||||
|
@ -59,20 +68,24 @@ void Thread::clear() {
|
|||
|
||||
counterMoves.fill(MOVE_NONE);
|
||||
mainHistory.fill(0);
|
||||
lowPlyHistory.fill(0);
|
||||
captureHistory.fill(0);
|
||||
|
||||
for (auto& to : continuationHistory)
|
||||
for (auto& h : to)
|
||||
h->fill(0);
|
||||
|
||||
continuationHistory[NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1);
|
||||
for (bool inCheck : { false, true })
|
||||
for (StatsType c : { NoCaptures, Captures })
|
||||
{
|
||||
for (auto& to : continuationHistory[inCheck][c])
|
||||
for (auto& h : to)
|
||||
h->fill(0);
|
||||
continuationHistory[inCheck][c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Thread::start_searching() wakes up the thread that will start the search
|
||||
|
||||
void Thread::start_searching() {
|
||||
|
||||
std::lock_guard<Mutex> lk(mutex);
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
searching = true;
|
||||
cv.notify_one(); // Wake up the thread in idle_loop()
|
||||
}
|
||||
|
@ -83,7 +96,7 @@ void Thread::start_searching() {
|
|||
|
||||
void Thread::wait_for_search_finished() {
|
||||
|
||||
std::unique_lock<Mutex> lk(mutex);
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
cv.wait(lk, [&]{ return !searching; });
|
||||
}
|
||||
|
||||
|
@ -103,7 +116,7 @@ void Thread::idle_loop() {
|
|||
|
||||
while (true)
|
||||
{
|
||||
std::unique_lock<Mutex> lk(mutex);
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
searching = false;
|
||||
cv.notify_one(); // Wake up anyone waiting for search finished
|
||||
cv.wait(lk, [&]{ return searching; });
|
||||
|
@ -138,7 +151,10 @@ void ThreadPool::set(size_t requested) {
|
|||
clear();
|
||||
|
||||
// Reallocate the hash with the new threadpool size
|
||||
TT.resize(Options["Hash"]);
|
||||
TT.resize(size_t(Options["Hash"]));
|
||||
|
||||
// Init thread number dependent search params.
|
||||
Search::init();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +166,7 @@ void ThreadPool::clear() {
|
|||
th->clear();
|
||||
|
||||
main()->callsCnt = 0;
|
||||
main()->previousScore = VALUE_INFINITE;
|
||||
main()->bestPreviousScore = VALUE_INFINITE;
|
||||
main()->previousTimeReduction = 1.0;
|
||||
}
|
||||
|
||||
|
@ -163,6 +179,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
|
|||
main()->wait_for_search_finished();
|
||||
|
||||
main()->stopOnPonderhit = stop = false;
|
||||
increaseDepth = true;
|
||||
main()->ponder = ponderMode;
|
||||
Search::Limits = limits;
|
||||
Search::RootMoves rootMoves;
|
||||
|
@ -192,7 +209,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
|
|||
for (Thread* th : *this)
|
||||
{
|
||||
th->nodes = th->tbHits = th->nmpMinPly = 0;
|
||||
th->rootDepth = th->completedDepth = DEPTH_ZERO;
|
||||
th->rootDepth = th->completedDepth = 0;
|
||||
th->rootMoves = rootMoves;
|
||||
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
|
||||
}
|
||||
|
|
16
src/thread.h
16
src/thread.h
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -42,8 +42,8 @@
|
|||
|
||||
class Thread {
|
||||
|
||||
Mutex mutex;
|
||||
ConditionVariable cv;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
size_t idx;
|
||||
bool exit = false, searching = true; // Set before starting std::thread
|
||||
NativeThread stdThread;
|
||||
|
@ -56,10 +56,12 @@ public:
|
|||
void idle_loop();
|
||||
void start_searching();
|
||||
void wait_for_search_finished();
|
||||
int best_move_count(Move move) const;
|
||||
|
||||
Pawns::Table pawnsTable;
|
||||
Material::Table materialTable;
|
||||
size_t pvIdx, pvLast;
|
||||
uint64_t ttHitAverage;
|
||||
int selDepth, nmpMinPly;
|
||||
Color nmpColor;
|
||||
std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
|
||||
|
@ -69,8 +71,9 @@ public:
|
|||
Depth rootDepth, completedDepth;
|
||||
CounterMoveHistory counterMoves;
|
||||
ButterflyHistory mainHistory;
|
||||
LowPlyHistory lowPlyHistory;
|
||||
CapturePieceToHistory captureHistory;
|
||||
ContinuationHistory continuationHistory;
|
||||
ContinuationHistory continuationHistory[2][2];
|
||||
Score contempt;
|
||||
};
|
||||
|
||||
|
@ -85,7 +88,8 @@ struct MainThread : public Thread {
|
|||
void check_time();
|
||||
|
||||
double previousTimeReduction;
|
||||
Value previousScore;
|
||||
Value bestPreviousScore;
|
||||
Value iterValue[4];
|
||||
int callsCnt;
|
||||
bool stopOnPonderhit;
|
||||
std::atomic_bool ponder;
|
||||
|
@ -106,7 +110,7 @@ struct ThreadPool : public std::vector<Thread*> {
|
|||
uint64_t nodes_searched() const { return accumulate(&Thread::nodes); }
|
||||
uint64_t tb_hits() const { return accumulate(&Thread::tbHits); }
|
||||
|
||||
std::atomic_bool stop;
|
||||
std::atomic_bool stop, increaseDepth;
|
||||
|
||||
private:
|
||||
StateListPtr setupStates;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -21,63 +21,19 @@
|
|||
#ifndef THREAD_WIN32_OSX_H_INCLUDED
|
||||
#define THREAD_WIN32_OSX_H_INCLUDED
|
||||
|
||||
/// STL thread library used by mingw and gcc when cross compiling for Windows
|
||||
/// relies on libwinpthread. Currently libwinpthread implements mutexes directly
|
||||
/// on top of Windows semaphores. Semaphores, being kernel objects, require kernel
|
||||
/// mode transition in order to lock or unlock, which is very slow compared to
|
||||
/// interlocked operations (about 30% slower on bench test). To work around this
|
||||
/// issue, we define our wrappers to the low level Win32 calls. We use critical
|
||||
/// sections to support Windows XP and older versions. Unfortunately, cond_wait()
|
||||
/// is racy between unlock() and WaitForSingleObject() but they have the same
|
||||
/// speed performance as the SRW locks.
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#if defined(_WIN32) && !defined(_MSC_VER)
|
||||
|
||||
#ifndef NOMINMAX
|
||||
# define NOMINMAX // Disable macros min() and max()
|
||||
#endif
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#undef NOMINMAX
|
||||
|
||||
/// Mutex and ConditionVariable struct are wrappers of the low level locking
|
||||
/// machinery and are modeled after the corresponding C++11 classes.
|
||||
|
||||
struct Mutex {
|
||||
Mutex() { InitializeCriticalSection(&cs); }
|
||||
~Mutex() { DeleteCriticalSection(&cs); }
|
||||
void lock() { EnterCriticalSection(&cs); }
|
||||
void unlock() { LeaveCriticalSection(&cs); }
|
||||
|
||||
private:
|
||||
CRITICAL_SECTION cs;
|
||||
};
|
||||
|
||||
typedef std::condition_variable_any ConditionVariable;
|
||||
|
||||
#else // Default case: use STL classes
|
||||
|
||||
typedef std::mutex Mutex;
|
||||
typedef std::condition_variable ConditionVariable;
|
||||
|
||||
#endif
|
||||
|
||||
/// On OSX threads other than the main thread are created with a reduced stack
|
||||
/// size of 512KB by default, this is dangerously low for deep searches, so
|
||||
/// adjust it to TH_STACK_SIZE. The implementation calls pthread_create() with
|
||||
/// proper stack size parameter.
|
||||
/// size of 512KB by default, this is too low for deep searches, which require
|
||||
/// somewhat more than 1MB stack, so adjust it to TH_STACK_SIZE.
|
||||
/// The implementation calls pthread_create() with the stack size parameter
|
||||
/// equal to the linux 8MB default, on platforms that support it.
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__)
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
static const size_t TH_STACK_SIZE = 2 * 1024 * 1024;
|
||||
static const size_t TH_STACK_SIZE = 8 * 1024 * 1024;
|
||||
|
||||
template <class T, class P = std::pair<T*, void(T::*)()>>
|
||||
void* start_routine(void* ptr)
|
||||
|
|
118
src/timeman.cpp
118
src/timeman.cpp
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -28,66 +28,21 @@
|
|||
|
||||
TimeManagement Time; // Our global time management object
|
||||
|
||||
namespace {
|
||||
|
||||
enum TimeType { OptimumTime, MaxTime };
|
||||
|
||||
constexpr int MoveHorizon = 50; // Plan time management at most this many moves ahead
|
||||
constexpr double MaxRatio = 7.3; // When in trouble, we can step over reserved time with this ratio
|
||||
constexpr double StealRatio = 0.34; // However we must not steal time from remaining moves over this ratio
|
||||
|
||||
|
||||
// move_importance() is a skew-logistic function based on naive statistical
|
||||
// analysis of "how many games are still undecided after n half-moves". Game
|
||||
// is considered "undecided" as long as neither side has >275cp advantage.
|
||||
// Data was extracted from the CCRL game database with some simple filtering criteria.
|
||||
|
||||
double move_importance(int ply) {
|
||||
|
||||
constexpr double XScale = 6.85;
|
||||
constexpr double XShift = 64.5;
|
||||
constexpr double Skew = 0.171;
|
||||
|
||||
return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero
|
||||
}
|
||||
|
||||
template<TimeType T>
|
||||
TimePoint remaining(TimePoint myTime, int movesToGo, int ply, TimePoint slowMover) {
|
||||
|
||||
constexpr double TMaxRatio = (T == OptimumTime ? 1.0 : MaxRatio);
|
||||
constexpr double TStealRatio = (T == OptimumTime ? 0.0 : StealRatio);
|
||||
|
||||
double moveImportance = (move_importance(ply) * slowMover) / 100.0;
|
||||
double otherMovesImportance = 0.0;
|
||||
|
||||
for (int i = 1; i < movesToGo; ++i)
|
||||
otherMovesImportance += move_importance(ply + 2 * i);
|
||||
|
||||
double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance);
|
||||
double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance);
|
||||
|
||||
return TimePoint(myTime * std::min(ratio1, ratio2)); // Intel C++ asks for an explicit cast
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
/// init() is called at the beginning of the search and calculates the allowed
|
||||
/// thinking time out of the time control and current game ply. We support four
|
||||
/// different kinds of time controls, passed in 'limits':
|
||||
///
|
||||
/// inc == 0 && movestogo == 0 means: x basetime [sudden death!]
|
||||
/// inc == 0 && movestogo != 0 means: x moves in y minutes
|
||||
/// inc > 0 && movestogo == 0 means: x basetime + z increment
|
||||
/// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment
|
||||
/// init() is called at the beginning of the search and calculates the bounds
|
||||
/// of time allowed for the current game ply. We currently support:
|
||||
// 1) x basetime (+z increment)
|
||||
// 2) x moves in y seconds (+z increment)
|
||||
|
||||
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
|
||||
|
||||
TimePoint minThinkingTime = Options["Minimum Thinking Time"];
|
||||
TimePoint moveOverhead = Options["Move Overhead"];
|
||||
TimePoint slowMover = Options["Slow Mover"];
|
||||
TimePoint npmsec = Options["nodestime"];
|
||||
TimePoint hypMyTime;
|
||||
TimePoint minThinkingTime = TimePoint(Options["Minimum Thinking Time"]);
|
||||
TimePoint moveOverhead = TimePoint(Options["Move Overhead"]);
|
||||
TimePoint slowMover = TimePoint(Options["Slow Mover"]);
|
||||
TimePoint npmsec = TimePoint(Options["nodestime"]);
|
||||
|
||||
// opt_scale is a percentage of available time to use for the current move.
|
||||
// max_scale is a multiplier applied to optimumTime.
|
||||
double opt_scale, max_scale;
|
||||
|
||||
// If we have to play in 'nodes as time' mode, then convert from time
|
||||
// to nodes, and use resulting values in time management formulas.
|
||||
|
@ -105,29 +60,40 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
|
|||
}
|
||||
|
||||
startTime = limits.startTime;
|
||||
optimumTime = maximumTime = std::max(limits.time[us], minThinkingTime);
|
||||
|
||||
const int maxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon;
|
||||
//Maximum move horizon of 50 moves
|
||||
int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50;
|
||||
|
||||
// We calculate optimum time usage for different hypothetical "moves to go" values
|
||||
// and choose the minimum of calculated search time values. Usually the greatest
|
||||
// hypMTG gives the minimum values.
|
||||
for (int hypMTG = 1; hypMTG <= maxMTG; ++hypMTG)
|
||||
// Make sure timeLeft is > 0 since we may use it as a divisor
|
||||
TimePoint timeLeft = std::max(TimePoint(1),
|
||||
limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg));
|
||||
|
||||
// A user may scale time usage by setting UCI option "Slow Mover"
|
||||
// Default is 100 and changing this value will probably lose elo.
|
||||
timeLeft = slowMover * timeLeft / 100;
|
||||
|
||||
// x basetime (+ z increment)
|
||||
// If there is a healthy increment, timeLeft can exceed actual available
|
||||
// game time for the current move, so also cap to 20% of available game time.
|
||||
if (limits.movestogo == 0)
|
||||
{
|
||||
// Calculate thinking time for hypothetical "moves to go"-value
|
||||
hypMyTime = limits.time[us]
|
||||
+ limits.inc[us] * (hypMTG - 1)
|
||||
- moveOverhead * (2 + std::min(hypMTG, 40));
|
||||
|
||||
hypMyTime = std::max(hypMyTime, TimePoint(0));
|
||||
|
||||
TimePoint t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, ply, slowMover);
|
||||
TimePoint t2 = minThinkingTime + remaining<MaxTime >(hypMyTime, hypMTG, ply, slowMover);
|
||||
|
||||
optimumTime = std::min(t1, optimumTime);
|
||||
maximumTime = std::min(t2, maximumTime);
|
||||
opt_scale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0,
|
||||
0.2 * limits.time[us] / double(timeLeft));
|
||||
max_scale = 4 + std::min(36, ply) / 12.0;
|
||||
}
|
||||
|
||||
// x moves in y seconds (+ z increment)
|
||||
else
|
||||
{
|
||||
opt_scale = std::min((0.8 + ply / 128.0) / mtg,
|
||||
0.8 * limits.time[us] / double(timeLeft));
|
||||
max_scale = std::min(6.3, 1.5 + 0.11 * mtg);
|
||||
}
|
||||
|
||||
// Never use more than 80% of the available time for this move
|
||||
optimumTime = std::max(minThinkingTime, TimePoint(opt_scale * timeLeft));
|
||||
maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, max_scale * optimumTime));
|
||||
|
||||
if (Options["Ponder"])
|
||||
optimumTime += optimumTime / 4;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
29
src/tt.cpp
29
src/tt.cpp
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -35,23 +35,22 @@ TranspositionTable TT; // Our global transposition table
|
|||
|
||||
void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) {
|
||||
|
||||
assert(d / ONE_PLY * ONE_PLY == d);
|
||||
|
||||
// Preserve any existing move for the same position
|
||||
if (m || (k >> 48) != key16)
|
||||
move16 = (uint16_t)m;
|
||||
|
||||
// Overwrite less valuable entries
|
||||
if ( (k >> 48) != key16
|
||||
|| d / ONE_PLY + 10 > depth8
|
||||
|| d - DEPTH_OFFSET > depth8 - 4
|
||||
|| b == BOUND_EXACT)
|
||||
{
|
||||
assert(d >= DEPTH_OFFSET);
|
||||
|
||||
key16 = (uint16_t)(k >> 48);
|
||||
value16 = (int16_t)v;
|
||||
eval16 = (int16_t)ev;
|
||||
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
|
||||
assert((d - DEPTH_NONE) / ONE_PLY >= 0);
|
||||
depth8 = (uint8_t)((d - DEPTH_NONE) / ONE_PLY);
|
||||
depth8 = (uint8_t)(d - DEPTH_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,11 +63,10 @@ void TranspositionTable::resize(size_t mbSize) {
|
|||
|
||||
Threads.main()->wait_for_search_finished();
|
||||
|
||||
aligned_ttmem_free(mem);
|
||||
|
||||
clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster);
|
||||
|
||||
free(mem);
|
||||
mem = malloc(clusterCount * sizeof(Cluster) + CacheLineSize - 1);
|
||||
|
||||
table = static_cast<Cluster*>(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), mem));
|
||||
if (!mem)
|
||||
{
|
||||
std::cerr << "Failed to allocate " << mbSize
|
||||
|
@ -76,7 +74,6 @@ void TranspositionTable::resize(size_t mbSize) {
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
table = (Cluster*)((uintptr_t(mem) + CacheLineSize - 1) & ~(CacheLineSize - 1));
|
||||
clear();
|
||||
}
|
||||
|
||||
|
@ -97,8 +94,8 @@ void TranspositionTable::clear() {
|
|||
WinProcGroup::bindThisThread(idx);
|
||||
|
||||
// Each thread will zero its part of the hash table
|
||||
const size_t stride = clusterCount / Options["Threads"],
|
||||
start = stride * idx,
|
||||
const size_t stride = size_t(clusterCount / Options["Threads"]),
|
||||
start = size_t(stride * idx),
|
||||
len = idx != Options["Threads"] - 1 ?
|
||||
stride : clusterCount - start;
|
||||
|
||||
|
@ -106,7 +103,7 @@ void TranspositionTable::clear() {
|
|||
});
|
||||
}
|
||||
|
||||
for (std::thread& th: threads)
|
||||
for (std::thread& th : threads)
|
||||
th.join();
|
||||
}
|
||||
|
||||
|
@ -155,9 +152,9 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
|
|||
int TranspositionTable::hashfull() const {
|
||||
|
||||
int cnt = 0;
|
||||
for (int i = 0; i < 1000 / ClusterSize; ++i)
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
for (int j = 0; j < ClusterSize; ++j)
|
||||
cnt += (table[i].entry[j].genBound8 & 0xF8) == generation8;
|
||||
|
||||
return cnt * 1000 / (ClusterSize * (1000 / ClusterSize));
|
||||
return cnt / ClusterSize;
|
||||
}
|
||||
|
|
24
src/tt.h
24
src/tt.h
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -40,8 +40,8 @@ struct TTEntry {
|
|||
Move move() const { return (Move )move16; }
|
||||
Value value() const { return (Value)value16; }
|
||||
Value eval() const { return (Value)eval16; }
|
||||
Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)) + DEPTH_NONE; }
|
||||
bool is_pv() const { return (bool)(genBound8 & 0x4); }
|
||||
Depth depth() const { return (Depth)depth8 + DEPTH_OFFSET; }
|
||||
bool is_pv() const { return (bool)(genBound8 & 0x4); }
|
||||
Bound bound() const { return (Bound)(genBound8 & 0x3); }
|
||||
void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev);
|
||||
|
||||
|
@ -57,27 +57,25 @@ private:
|
|||
};
|
||||
|
||||
|
||||
/// A TranspositionTable consists of a power of 2 number of clusters and each
|
||||
/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry
|
||||
/// contains information of exactly one position. The size of a cluster should
|
||||
/// divide the size of a cache line size, to ensure that clusters never cross
|
||||
/// cache lines. This ensures best cache performance, as the cacheline is
|
||||
/// prefetched, as soon as possible.
|
||||
/// A TranspositionTable is an array of Cluster, of size clusterCount. Each
|
||||
/// cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry
|
||||
/// contains information on exactly one position. The size of a Cluster should
|
||||
/// divide the size of a cache line for best performance,
|
||||
/// as the cacheline is prefetched when possible.
|
||||
|
||||
class TranspositionTable {
|
||||
|
||||
static constexpr int CacheLineSize = 64;
|
||||
static constexpr int ClusterSize = 3;
|
||||
|
||||
struct Cluster {
|
||||
TTEntry entry[ClusterSize];
|
||||
char padding[2]; // Align to a divisor of the cache line size
|
||||
char padding[2]; // Pad to 32 bytes
|
||||
};
|
||||
|
||||
static_assert(CacheLineSize % sizeof(Cluster) == 0, "Cluster size incorrect");
|
||||
static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size");
|
||||
|
||||
public:
|
||||
~TranspositionTable() { free(mem); }
|
||||
~TranspositionTable() { aligned_ttmem_free(mem); }
|
||||
void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound
|
||||
TTEntry* probe(const Key key, bool& found) const;
|
||||
int hashfull() const;
|
||||
|
|
146
src/tune.cpp
Normal file
146
src/tune.cpp
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "types.h"
|
||||
#include "misc.h"
|
||||
#include "uci.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
bool Tune::update_on_last;
|
||||
const UCI::Option* LastOption = nullptr;
|
||||
BoolConditions Conditions;
|
||||
static std::map<std::string, int> TuneResults;
|
||||
|
||||
string Tune::next(string& names, bool pop) {
|
||||
|
||||
string name;
|
||||
|
||||
do {
|
||||
string token = names.substr(0, names.find(','));
|
||||
|
||||
if (pop)
|
||||
names.erase(0, token.size() + 1);
|
||||
|
||||
std::stringstream ws(token);
|
||||
name += (ws >> token, token); // Remove trailing whitespace
|
||||
|
||||
} while ( std::count(name.begin(), name.end(), '(')
|
||||
- std::count(name.begin(), name.end(), ')'));
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
static void on_tune(const UCI::Option& o) {
|
||||
|
||||
if (!Tune::update_on_last || LastOption == &o)
|
||||
Tune::read_options();
|
||||
}
|
||||
|
||||
static void make_option(const string& n, int v, const SetRange& r) {
|
||||
|
||||
// Do not generate option when there is nothing to tune (ie. min = max)
|
||||
if (r(v).first == r(v).second)
|
||||
return;
|
||||
|
||||
if (TuneResults.count(n))
|
||||
v = TuneResults[n];
|
||||
|
||||
Options[n] << UCI::Option(v, r(v).first, r(v).second, on_tune);
|
||||
LastOption = &Options[n];
|
||||
|
||||
// Print formatted parameters, ready to be copy-pasted in fishtest
|
||||
std::cout << n << ","
|
||||
<< v << ","
|
||||
<< r(v).first << "," << r(v).second << ","
|
||||
<< (r(v).second - r(v).first) / 20.0 << ","
|
||||
<< "0.0020"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
template<> void Tune::Entry<int>::init_option() { make_option(name, value, range); }
|
||||
|
||||
template<> void Tune::Entry<int>::read_option() {
|
||||
if (Options.count(name))
|
||||
value = int(Options[name]);
|
||||
}
|
||||
|
||||
template<> void Tune::Entry<Value>::init_option() { make_option(name, value, range); }
|
||||
|
||||
template<> void Tune::Entry<Value>::read_option() {
|
||||
if (Options.count(name))
|
||||
value = Value(int(Options[name]));
|
||||
}
|
||||
|
||||
template<> void Tune::Entry<Score>::init_option() {
|
||||
make_option("m" + name, mg_value(value), range);
|
||||
make_option("e" + name, eg_value(value), range);
|
||||
}
|
||||
|
||||
template<> void Tune::Entry<Score>::read_option() {
|
||||
if (Options.count("m" + name))
|
||||
value = make_score(int(Options["m" + name]), eg_value(value));
|
||||
|
||||
if (Options.count("e" + name))
|
||||
value = make_score(mg_value(value), int(Options["e" + name]));
|
||||
}
|
||||
|
||||
// Instead of a variable here we have a PostUpdate function: just call it
|
||||
template<> void Tune::Entry<Tune::PostUpdate>::init_option() {}
|
||||
template<> void Tune::Entry<Tune::PostUpdate>::read_option() { value(); }
|
||||
|
||||
|
||||
// Set binary conditions according to a probability that depends
|
||||
// on the corresponding parameter value.
|
||||
|
||||
void BoolConditions::set() {
|
||||
|
||||
static PRNG rng(now());
|
||||
static bool startup = true; // To workaround fishtest bench
|
||||
|
||||
for (size_t i = 0; i < binary.size(); i++)
|
||||
binary[i] = !startup && (values[i] + int(rng.rand<unsigned>() % variance) > threshold);
|
||||
|
||||
startup = false;
|
||||
|
||||
for (size_t i = 0; i < binary.size(); i++)
|
||||
sync_cout << binary[i] << sync_endl;
|
||||
}
|
||||
|
||||
|
||||
// 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.
|
||||
// Just copy fishtest tuning results in a result.txt file and extract the
|
||||
// values with:
|
||||
//
|
||||
// cat results.txt | sed 's/^param: \([^,]*\), best: \([^,]*\).*/ TuneResults["\1"] = int(round(\2));/'
|
||||
//
|
||||
// Then paste the output below, as the function body
|
||||
|
||||
#include <cmath>
|
||||
|
||||
void Tune::read_results() {
|
||||
|
||||
/* ...insert your values here... */
|
||||
}
|
195
src/tune.h
Normal file
195
src/tune.h
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2017 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
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 TUNE_H_INCLUDED
|
||||
#define TUNE_H_INCLUDED
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
typedef std::pair<int, int> Range; // Option's min-max values
|
||||
typedef Range (RangeFun) (int);
|
||||
|
||||
// Default Range function, to calculate Option's min-max values
|
||||
inline Range default_range(int v) {
|
||||
return v > 0 ? Range(0, 2 * v) : Range(2 * v, 0);
|
||||
}
|
||||
|
||||
struct SetRange {
|
||||
explicit SetRange(RangeFun f) : fun(f) {}
|
||||
SetRange(int min, int max) : fun(nullptr), range(min, max) {}
|
||||
Range operator()(int v) const { return fun ? fun(v) : range; }
|
||||
|
||||
RangeFun* fun;
|
||||
Range range;
|
||||
};
|
||||
|
||||
#define SetDefaultRange SetRange(default_range)
|
||||
|
||||
|
||||
/// BoolConditions struct is used to tune boolean conditions in the
|
||||
/// code by toggling them on/off according to a probability that
|
||||
/// depends on the value of a tuned integer parameter: for high
|
||||
/// values of the parameter condition is always disabled, for low
|
||||
/// values is always enabled, otherwise it is enabled with a given
|
||||
/// probability that depnends on the parameter under tuning.
|
||||
|
||||
struct BoolConditions {
|
||||
void init(size_t size) { values.resize(size, defaultValue), binary.resize(size, 0); }
|
||||
void set();
|
||||
|
||||
std::vector<int> binary, values;
|
||||
int defaultValue = 465, variance = 40, threshold = 500;
|
||||
SetRange range = SetRange(0, 1000);
|
||||
};
|
||||
|
||||
extern BoolConditions Conditions;
|
||||
|
||||
inline void set_conditions() { Conditions.set(); }
|
||||
|
||||
|
||||
/// Tune class implements the 'magic' code that makes the setup of a fishtest
|
||||
/// tuning session as easy as it can be. Mainly you have just to remove const
|
||||
/// qualifiers from the variables you want to tune and flag them for tuning, so
|
||||
/// if you have:
|
||||
///
|
||||
/// const Score myScore = S(10, 15);
|
||||
/// const Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } };
|
||||
///
|
||||
/// If you have a my_post_update() function to run after values have been updated,
|
||||
/// and a my_range() function to set custom Option's min-max values, then you just
|
||||
/// remove the 'const' qualifiers and write somewhere below in the file:
|
||||
///
|
||||
/// TUNE(SetRange(my_range), myScore, myValue, my_post_update);
|
||||
///
|
||||
/// You can also set the range directly, and restore the default at the end
|
||||
///
|
||||
/// TUNE(SetRange(-100, 100), myScore, SetDefaultRange);
|
||||
///
|
||||
/// In case update function is slow and you have many parameters, you can add:
|
||||
///
|
||||
/// UPDATE_ON_LAST();
|
||||
///
|
||||
/// And the values update, including post update function call, will be done only
|
||||
/// once, after the engine receives the last UCI option, that is the one defined
|
||||
/// and created as the last one, so the GUI should send the options in the same
|
||||
/// order in which have been defined.
|
||||
|
||||
class Tune {
|
||||
|
||||
typedef void (PostUpdate) (); // Post-update function
|
||||
|
||||
Tune() { read_results(); }
|
||||
Tune(const Tune&) = delete;
|
||||
void operator=(const Tune&) = delete;
|
||||
void read_results();
|
||||
|
||||
static Tune& instance() { static Tune t; return t; } // Singleton
|
||||
|
||||
// Use polymorphism to accomodate Entry of different types in the same vector
|
||||
struct EntryBase {
|
||||
virtual ~EntryBase() = default;
|
||||
virtual void init_option() = 0;
|
||||
virtual void read_option() = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Entry : public EntryBase {
|
||||
|
||||
static_assert(!std::is_const<T>::value, "Parameter cannot be const!");
|
||||
|
||||
static_assert( std::is_same<T, int>::value
|
||||
|| std::is_same<T, Value>::value
|
||||
|| std::is_same<T, Score>::value
|
||||
|| std::is_same<T, PostUpdate>::value, "Parameter type not supported!");
|
||||
|
||||
Entry(const std::string& n, T& v, const SetRange& r) : name(n), value(v), range(r) {}
|
||||
void operator=(const Entry&) = delete; // Because 'value' is a reference
|
||||
void init_option() override;
|
||||
void read_option() override;
|
||||
|
||||
std::string name;
|
||||
T& value;
|
||||
SetRange range;
|
||||
};
|
||||
|
||||
// Our facilty to fill the container, each Entry corresponds to a parameter to tune.
|
||||
// We use variadic templates to deal with an unspecified number of entries, each one
|
||||
// of a possible different type.
|
||||
static std::string next(std::string& names, bool pop = true);
|
||||
|
||||
int add(const SetRange&, std::string&&) { return 0; }
|
||||
|
||||
template<typename T, typename... Args>
|
||||
int add(const SetRange& range, std::string&& names, T& value, Args&&... args) {
|
||||
list.push_back(std::unique_ptr<EntryBase>(new Entry<T>(next(names), value, range)));
|
||||
return add(range, std::move(names), args...);
|
||||
}
|
||||
|
||||
// Template specialization for arrays: recursively handle multi-dimensional arrays
|
||||
template<typename T, size_t N, typename... Args>
|
||||
int add(const SetRange& range, std::string&& names, T (&value)[N], Args&&... args) {
|
||||
for (size_t i = 0; i < N; i++)
|
||||
add(range, next(names, i == N - 1) + "[" + std::to_string(i) + "]", value[i]);
|
||||
return add(range, std::move(names), args...);
|
||||
}
|
||||
|
||||
// Template specialization for SetRange
|
||||
template<typename... Args>
|
||||
int add(const SetRange&, std::string&& names, SetRange& value, Args&&... args) {
|
||||
return add(value, (next(names), std::move(names)), args...);
|
||||
}
|
||||
|
||||
// Template specialization for BoolConditions
|
||||
template<typename... Args>
|
||||
int add(const SetRange& range, std::string&& names, BoolConditions& cond, Args&&... args) {
|
||||
for (size_t size = cond.values.size(), i = 0; i < size; i++)
|
||||
add(cond.range, next(names, i == size - 1) + "_" + std::to_string(i), cond.values[i]);
|
||||
return add(range, std::move(names), args...);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<EntryBase>> list;
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
static int add(const std::string& names, Args&&... args) {
|
||||
return instance().add(SetDefaultRange, names.substr(1, names.size() - 2), args...); // Remove trailing parenthesis
|
||||
}
|
||||
static void init() { for (auto& e : instance().list) e->init_option(); read_options(); } // Deferred, due to UCI::Options access
|
||||
static void read_options() { for (auto& e : instance().list) e->read_option(); }
|
||||
static bool update_on_last;
|
||||
};
|
||||
|
||||
// Some macro magic :-) we define a dummy int variable that compiler initializes calling Tune::add()
|
||||
#define STRINGIFY(x) #x
|
||||
#define UNIQUE2(x, y) x ## y
|
||||
#define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__
|
||||
#define TUNE(...) int UNIQUE(p, __LINE__) = Tune::add(STRINGIFY((__VA_ARGS__)), __VA_ARGS__)
|
||||
|
||||
#define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true
|
||||
|
||||
// Some macro to tune toggling of boolean conditions
|
||||
#define CONDITION(x) (Conditions.binary[__COUNTER__] || (x))
|
||||
#define TUNE_CONDITIONS() int UNIQUE(c, __LINE__) = (Conditions.init(__COUNTER__), 0); \
|
||||
TUNE(Conditions, set_conditions)
|
||||
|
||||
#endif // #ifndef TUNE_H_INCLUDED
|
88
src/types.h
88
src/types.h
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -43,6 +43,7 @@
|
|||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Disable some silly and noisy warning from MSVC compiler
|
||||
|
@ -133,19 +134,17 @@ enum Color {
|
|||
|
||||
constexpr Color Colors[2] = { WHITE, BLACK };
|
||||
|
||||
enum CastlingSide {
|
||||
KING_SIDE, QUEEN_SIDE, CASTLING_SIDE_NB = 2
|
||||
};
|
||||
|
||||
enum CastlingRight {
|
||||
enum CastlingRights {
|
||||
NO_CASTLING,
|
||||
WHITE_OO,
|
||||
WHITE_OOO = WHITE_OO << 1,
|
||||
BLACK_OO = WHITE_OO << 2,
|
||||
BLACK_OOO = WHITE_OO << 3,
|
||||
|
||||
WHITE_CASTLING = WHITE_OO | WHITE_OOO,
|
||||
BLACK_CASTLING = BLACK_OO | BLACK_OOO,
|
||||
KING_SIDE = WHITE_OO | BLACK_OO,
|
||||
QUEEN_SIDE = WHITE_OOO | BLACK_OOO,
|
||||
WHITE_CASTLING = WHITE_OO | WHITE_OOO,
|
||||
BLACK_CASTLING = BLACK_OO | BLACK_OOO,
|
||||
ANY_CASTLING = WHITE_CASTLING | BLACK_CASTLING,
|
||||
|
||||
CASTLING_RIGHT_NB = 16
|
||||
|
@ -179,14 +178,17 @@ enum Value : int {
|
|||
VALUE_INFINITE = 32001,
|
||||
VALUE_NONE = 32002,
|
||||
|
||||
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY,
|
||||
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY,
|
||||
VALUE_TB_WIN_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY,
|
||||
VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_TB_WIN_IN_MAX_PLY,
|
||||
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
|
||||
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY,
|
||||
|
||||
PawnValueMg = 128, PawnValueEg = 213,
|
||||
KnightValueMg = 782, KnightValueEg = 865,
|
||||
BishopValueMg = 830, BishopValueEg = 918,
|
||||
RookValueMg = 1289, RookValueEg = 1378,
|
||||
QueenValueMg = 2529, QueenValueEg = 2687,
|
||||
PawnValueMg = 124, PawnValueEg = 206,
|
||||
KnightValueMg = 781, KnightValueEg = 854,
|
||||
BishopValueMg = 825, BishopValueEg = 915,
|
||||
RookValueMg = 1276, RookValueEg = 1380,
|
||||
QueenValueMg = 2538, QueenValueEg = 2682,
|
||||
Tempo = 28,
|
||||
|
||||
MidgameLimit = 15258, EndgameLimit = 3915,
|
||||
|
||||
|
@ -207,22 +209,24 @@ enum Piece {
|
|||
PIECE_NB = 16
|
||||
};
|
||||
|
||||
extern Value PieceValue[PHASE_NB][PIECE_NB];
|
||||
|
||||
enum Depth : int {
|
||||
|
||||
ONE_PLY = 1,
|
||||
|
||||
DEPTH_ZERO = 0 * ONE_PLY,
|
||||
DEPTH_QS_CHECKS = 0 * ONE_PLY,
|
||||
DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
|
||||
DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
|
||||
|
||||
DEPTH_NONE = -6 * ONE_PLY,
|
||||
DEPTH_MAX = MAX_PLY * ONE_PLY
|
||||
constexpr Value PieceValue[PHASE_NB][PIECE_NB] = {
|
||||
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO,
|
||||
VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO },
|
||||
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, VALUE_ZERO, VALUE_ZERO,
|
||||
VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, VALUE_ZERO, VALUE_ZERO }
|
||||
};
|
||||
|
||||
static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2");
|
||||
typedef int Depth;
|
||||
|
||||
enum : int {
|
||||
|
||||
DEPTH_QS_CHECKS = 0,
|
||||
DEPTH_QS_NO_CHECKS = -1,
|
||||
DEPTH_QS_RECAPTURES = -5,
|
||||
|
||||
DEPTH_NONE = -6,
|
||||
DEPTH_OFFSET = DEPTH_NONE
|
||||
};
|
||||
|
||||
enum Square : int {
|
||||
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
|
||||
|
@ -296,7 +300,6 @@ inline T& operator--(T& d) { return d = T(int(d) - 1); }
|
|||
|
||||
#define ENABLE_FULL_OPERATORS_ON(T) \
|
||||
ENABLE_BASE_OPERATORS_ON(T) \
|
||||
ENABLE_INCR_OPERATORS_ON(T) \
|
||||
constexpr T operator*(int i, T d) { return T(i * int(d)); } \
|
||||
constexpr T operator*(T d, int i) { return T(int(d) * i); } \
|
||||
constexpr T operator/(T d, int i) { return T(int(d) / i); } \
|
||||
|
@ -305,12 +308,10 @@ inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } \
|
|||
inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
|
||||
|
||||
ENABLE_FULL_OPERATORS_ON(Value)
|
||||
ENABLE_FULL_OPERATORS_ON(Depth)
|
||||
ENABLE_FULL_OPERATORS_ON(Direction)
|
||||
|
||||
ENABLE_INCR_OPERATORS_ON(PieceType)
|
||||
ENABLE_INCR_OPERATORS_ON(Piece)
|
||||
ENABLE_INCR_OPERATORS_ON(Color)
|
||||
ENABLE_INCR_OPERATORS_ON(Square)
|
||||
ENABLE_INCR_OPERATORS_ON(File)
|
||||
ENABLE_INCR_OPERATORS_ON(Rank)
|
||||
|
@ -354,24 +355,29 @@ inline Score operator*(Score s, int i) {
|
|||
return result;
|
||||
}
|
||||
|
||||
/// Multiplication of a Score by a boolean
|
||||
inline Score operator*(Score s, bool b) {
|
||||
return b ? s : SCORE_ZERO;
|
||||
}
|
||||
|
||||
constexpr Color operator~(Color c) {
|
||||
return Color(c ^ BLACK); // Toggle color
|
||||
}
|
||||
|
||||
constexpr Square operator~(Square s) {
|
||||
return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8
|
||||
constexpr Square flip_rank(Square s) {
|
||||
return Square(s ^ SQ_A8);
|
||||
}
|
||||
|
||||
constexpr File operator~(File f) {
|
||||
return File(f ^ FILE_H); // Horizontal flip FILE_A -> FILE_H
|
||||
constexpr Square flip_file(Square s) {
|
||||
return Square(s ^ SQ_H1);
|
||||
}
|
||||
|
||||
constexpr Piece operator~(Piece pc) {
|
||||
return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT
|
||||
}
|
||||
|
||||
constexpr CastlingRight operator|(Color c, CastlingSide s) {
|
||||
return CastlingRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c));
|
||||
constexpr CastlingRights operator&(Color c, CastlingRights cr) {
|
||||
return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr);
|
||||
}
|
||||
|
||||
constexpr Value mate_in(int ply) {
|
||||
|
@ -451,6 +457,10 @@ constexpr Move make_move(Square from, Square to) {
|
|||
return Move((from << 6) + to);
|
||||
}
|
||||
|
||||
constexpr Move reverse_move(Move m) {
|
||||
return make_move(to_sq(m), from_sq(m));
|
||||
}
|
||||
|
||||
template<MoveType T>
|
||||
constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) {
|
||||
return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);
|
||||
|
@ -499,3 +509,5 @@ constexpr bool is_ok(PieceNumber pn) { return pn < PIECE_NUMBER_NB; }
|
|||
#endif // defined(EVAL_NNUE) || defined(EVAL_LEARN)
|
||||
|
||||
#endif // #ifndef TYPES_H_INCLUDED
|
||||
|
||||
#include "tune.h" // Global visibility to tuning setup
|
||||
|
|
39
src/uci.cpp
39
src/uci.cpp
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -154,7 +154,7 @@ namespace {
|
|||
limits.startTime = now(); // As early as possible!
|
||||
|
||||
while (is >> token)
|
||||
if (token == "searchmoves")
|
||||
if (token == "searchmoves") // Needs to be the last command on the line
|
||||
while (is >> token)
|
||||
limits.searchmoves.push_back(UCI::to_move(pos, token));
|
||||
|
||||
|
@ -185,7 +185,7 @@ namespace {
|
|||
uint64_t num, nodes = 0, cnt = 1;
|
||||
|
||||
vector<string> list = setup_bench(pos, args);
|
||||
num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0; });
|
||||
num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0 || s.find("eval") == 0; });
|
||||
|
||||
TimePoint elapsed = now();
|
||||
|
||||
|
@ -194,16 +194,21 @@ namespace {
|
|||
istringstream is(cmd);
|
||||
is >> skipws >> token;
|
||||
|
||||
if (token == "go")
|
||||
if (token == "go" || token == "eval")
|
||||
{
|
||||
cerr << "\nPosition: " << cnt++ << '/' << num << endl;
|
||||
go(pos, is, states);
|
||||
Threads.main()->wait_for_search_finished();
|
||||
nodes += Threads.nodes_searched();
|
||||
if (token == "go")
|
||||
{
|
||||
go(pos, is, states);
|
||||
Threads.main()->wait_for_search_finished();
|
||||
nodes += Threads.nodes_searched();
|
||||
}
|
||||
else
|
||||
sync_cout << "\n" << Eval::trace(pos) << sync_endl;
|
||||
}
|
||||
else if (token == "setoption") setoption(is);
|
||||
else if (token == "position") position(pos, is, states);
|
||||
else if (token == "ucinewgame") Search::clear();
|
||||
else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } // Search::clear() may take some while
|
||||
}
|
||||
|
||||
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
|
||||
|
@ -337,9 +342,8 @@ void UCI::loop(int argc, char* argv[]) {
|
|||
Position pos;
|
||||
string token, cmd;
|
||||
StateListPtr states(new std::deque<StateInfo>(1));
|
||||
auto uiThread = std::make_shared<Thread>(0);
|
||||
|
||||
pos.set(StartFEN, false, &states->back(), uiThread.get());
|
||||
pos.set(StartFEN, false, &states->back(), Threads.main());
|
||||
|
||||
for (int i = 1; i < argc; ++i)
|
||||
cmd += std::string(argv[i]) + " ";
|
||||
|
@ -375,11 +379,13 @@ void UCI::loop(int argc, char* argv[]) {
|
|||
else if (token == "ucinewgame") Search::clear();
|
||||
else if (token == "isready") is_ready();
|
||||
|
||||
// Additional custom non-UCI commands, mainly for debugging
|
||||
else if (token == "flip") pos.flip();
|
||||
else if (token == "bench") bench(pos, is, states);
|
||||
else if (token == "d") sync_cout << pos << sync_endl;
|
||||
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
|
||||
// Additional custom non-UCI commands, mainly for debugging.
|
||||
// Do not use these commands during a search!
|
||||
else if (token == "flip") pos.flip();
|
||||
else if (token == "bench") bench(pos, is, states);
|
||||
else if (token == "d") sync_cout << pos << sync_endl;
|
||||
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
|
||||
else if (token == "compiler") sync_cout << compiler_info() << sync_endl;
|
||||
#if defined (EVAL_LEARN)
|
||||
else if (token == "gensfen") Learner::gen_sfen(pos, is);
|
||||
else if (token == "learn") Learner::learn(pos, is);
|
||||
|
@ -398,7 +404,6 @@ void UCI::loop(int argc, char* argv[]) {
|
|||
// ƒeƒXƒgƒRƒ}ƒ“ƒh
|
||||
else if (token == "test") test_cmd(pos, is);
|
||||
#endif
|
||||
|
||||
else
|
||||
sync_cout << "Unknown command: " << cmd << sync_endl;
|
||||
|
||||
|
@ -419,7 +424,7 @@ string UCI::value(Value v) {
|
|||
|
||||
stringstream ss;
|
||||
|
||||
if (abs(v) < VALUE_MATE - MAX_PLY)
|
||||
if (abs(v) < VALUE_MATE_IN_MAX_PLY)
|
||||
ss << "cp " << v * 100 / PawnValueEg;
|
||||
else
|
||||
ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
||||
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
||||
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -38,9 +38,9 @@ namespace UCI {
|
|||
|
||||
/// 'On change' actions, triggered by an option's value change
|
||||
void on_clear_hash(const Option&) { Search::clear(); }
|
||||
void on_hash_size(const Option& o) { TT.resize(o); }
|
||||
void on_hash_size(const Option& o) { TT.resize(size_t(o)); }
|
||||
void on_logger(const Option& o) { start_logger(o); }
|
||||
void on_threads(const Option& o) { Threads.set(o); }
|
||||
void on_threads(const Option& o) { Threads.set(size_t(o)); }
|
||||
void on_tb_path(const Option& o) { Tablebases::init(o); }
|
||||
void on_eval_dir(const Option& o) { load_eval_finished = false; }
|
||||
|
||||
|
@ -69,12 +69,14 @@ void init(OptionsMap& o) {
|
|||
o["Ponder"] << Option(false);
|
||||
o["MultiPV"] << Option(1, 1, 500);
|
||||
o["Skill Level"] << Option(20, 0, 20);
|
||||
o["Move Overhead"] << Option(30, 0, 5000);
|
||||
o["Minimum Thinking Time"] << Option(20, 0, 5000);
|
||||
o["Slow Mover"] << Option(84, 10, 1000);
|
||||
o["Move Overhead"] << Option(10, 0, 5000);
|
||||
o["Minimum Thinking Time"] << Option( 0, 0, 5000);
|
||||
o["Slow Mover"] << Option(100, 10, 1000);
|
||||
o["nodestime"] << Option(0, 0, 10000);
|
||||
o["UCI_Chess960"] << Option(false);
|
||||
o["UCI_AnalyseMode"] << Option(false);
|
||||
o["UCI_LimitStrength"] << Option(false);
|
||||
o["UCI_Elo"] << Option(1350, 1350, 2850);
|
||||
o["SyzygyPath"] << Option("<empty>", on_tb_path);
|
||||
o["SyzygyProbeDepth"] << Option(1, 1, 100);
|
||||
o["Syzygy50MoveRule"] << Option(true);
|
||||
|
|
Loading…
Add table
Reference in a new issue