1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-07-12 03:59:15 +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:
nodchip 2020-06-08 23:09:51 +09:00
commit 5c936572e9
44 changed files with 2512 additions and 1705 deletions

View file

@ -1,5 +1,4 @@
language: cpp language: cpp
sudo: required
dist: xenial dist: xenial
matrix: matrix:
@ -51,14 +50,11 @@ script:
- echo "Reference bench:" $benchref - echo "Reference bench:" $benchref
# #
# Verify bench number against various builds # 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-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 optimize=no debug=yes build && ../tests/signature.sh $benchref
- make clean && make -j2 ARCH=x86-32 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 # Check perft and reproducible search
- ../tests/perft.sh - ../tests/perft.sh

68
AUTHORS
View file

@ -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) Tord Romstad (romstad)
Marco Costalba (mcostalba) Marco Costalba (mcostalba)
@ -9,8 +9,9 @@ Aditya (absimaldata)
Adrian Petrescu (apetresc) Adrian Petrescu (apetresc)
Ajith Chandy Jose (ajithcj) Ajith Chandy Jose (ajithcj)
Alain Savard (Rocky640) Alain Savard (Rocky640)
alayan-stk-2 Alayan Feh (Alayan-stk-2)
Alexander Kure Alexander Kure
Alexander Pagel (Lolligerhans)
Ali AlZhrani (Cooffe) Ali AlZhrani (Cooffe)
Andrew Grant (AndyGrant) Andrew Grant (AndyGrant)
Andrey Neporada (nepal) Andrey Neporada (nepal)
@ -21,27 +22,34 @@ Auguste Pop
Balint Pfliegel Balint Pfliegel
Ben Koshy (BKSpurgeon) Ben Koshy (BKSpurgeon)
Bill Henry (VoyagerOne) Bill Henry (VoyagerOne)
Bojun Guo (noobpwnftw, Nooby)
braich braich
Bojun Guo (noobpwnftw) Brian Sheppard (SapphireBrand, briansheppard-toast)
Brian Sheppard (SapphireBrand)
Bryan Cross (crossbr) Bryan Cross (crossbr)
candirufish
Chess13234
Chris Cain (ceebo) Chris Cain (ceebo)
Dan Schmidt Dan Schmidt (dfannius)
Daniel Axtens (daxtens)
Daniel Dugovic (ddugovic) Daniel Dugovic (ddugovic)
Dariusz Orzechowski Dariusz Orzechowski
David Zar David Zar
Daylen Yang (daylen) Daylen Yang (daylen)
DiscanX DiscanX
Eelco de Groot double-beep
Eduardo Cáceres (eduherminio)
Eelco de Groot (KingDefender)
Elvin Liu (solarlight2) Elvin Liu (solarlight2)
erbsenzaehler erbsenzaehler
Ernesto Gatti Ernesto Gatti
Linmiao Xu (linrock)
Fabian Beuke (madnight) Fabian Beuke (madnight)
Fabian Fichter (ianfab) Fabian Fichter (ianfab)
fanon fanon
Fauzi Akram Dabat (FauziAkram) Fauzi Akram Dabat (FauziAkram)
Felix Wittmann Felix Wittmann
gamander gamander
Gary Heckman (gheckman)
gguliash gguliash
Gian-Carlo Pascutto (gcp) Gian-Carlo Pascutto (gcp)
Gontran Lemaire (gonlem) Gontran Lemaire (gonlem)
@ -59,16 +67,18 @@ Jacques B. (Timshel)
Jan Ondruš (hxim) Jan Ondruš (hxim)
Jared Kish (Kurtbusch) Jared Kish (Kurtbusch)
Jarrod Torriero (DU-jdto) Jarrod Torriero (DU-jdto)
Jean Gauthier (OuaisBla)
Jean-Francois Romang (jromang) Jean-Francois Romang (jromang)
Jekaa
Jerry Donald Watson (jerrydonaldwatson) Jerry Donald Watson (jerrydonaldwatson)
Jonathan Calovski (Mysseno) Jonathan Calovski (Mysseno)
Jonathan D. (SFisGOD) Jonathan Dumale (SFisGOD)
Joost VandeVondele (vondele) Joost VandeVondele (vondele)
Jörg Oster (joergoster) Jörg Oster (joergoster)
Joseph Ellis (jhellis3) Joseph Ellis (jhellis3)
Joseph R. Prostko Joseph R. Prostko
jundery jundery
Justin Blanchard Justin Blanchard (UncombedCoconut)
Kelly Wilson Kelly Wilson
Ken Takusagawa Ken Takusagawa
kinderchocolate kinderchocolate
@ -76,36 +86,46 @@ Kiran Panditrao (Krgp)
Kojirion Kojirion
Leonardo Ljubičić (ICCF World Champion) Leonardo Ljubičić (ICCF World Champion)
Leonid Pechenik (lp--) Leonid Pechenik (lp--)
Linus Arver Linus Arver (listx)
loco-loco loco-loco
Lub van den Berg (ElbertoOne) Lub van den Berg (ElbertoOne)
Luca Brivio (lucabrivio) Luca Brivio (lucabrivio)
Lucas Braesch (lucasart) Lucas Braesch (lucasart)
Lyudmil Antonov (lantonov) Lyudmil Antonov (lantonov)
Matthew Lai (matthewlai) Maciej Żenczykowski (zenczykowski)
Matthew Sullivan Malcolm Campbell (xoto10)
Mark Tenzer (31m059) Mark Tenzer (31m059)
marotear
Matthew Lai (matthewlai)
Matthew Sullivan (Matt14916)
Michael An (man)
Michael Byrne (MichaelB7) Michael Byrne (MichaelB7)
Michael Stembera (mstembera)
Michael Chaly (Vizvezdenec) Michael Chaly (Vizvezdenec)
Michael Stembera (mstembera)
Michael Whiteley (protonspring)
Michel Van den Bergh (vdbergh) Michel Van den Bergh (vdbergh)
Miguel Lahoz (miguel-l) Miguel Lahoz (miguel-l)
Mikael Bäckman (mbootsector) Mikael Bäckman (mbootsector)
Michael Whiteley (protonspring) Mira
Miroslav Fontán (Hexik) Miroslav Fontán (Hexik)
Moez Jellouli (MJZ1977) Moez Jellouli (MJZ1977)
Mohammed Li (tthsqe12) Mohammed Li (tthsqe12)
Nathan Rugg (nmrugg) Nathan Rugg (nmrugg)
Nick Pelling (nickpelling)
Nicklas Persson (NicklasPersson) Nicklas Persson (NicklasPersson)
Niklas Fiekas (niklasf) Niklas Fiekas (niklasf)
Nikolay Kostov (NikolayIT)
Ondrej Mosnáček (WOnder93) Ondrej Mosnáček (WOnder93)
Oskar Werkelin Ahlin Oskar Werkelin Ahlin
Pablo Vazquez Pablo Vazquez
Panthee
Pascal Romaret Pascal Romaret
Pasquale Pigazzini (ppigazzini) Pasquale Pigazzini (ppigazzini)
Patrick Jansen (mibere) Patrick Jansen (mibere)
pellanda pellanda
Peter Zsifkovits (CoffeeOne) Peter Zsifkovits (CoffeeOne)
Praveen Kumar Tummala (praveentml)
Rahul Dsilva (silversolver1)
Ralph Stößer (Ralph Stoesser) Ralph Stößer (Ralph Stoesser)
Raminder Singh Raminder Singh
renouve renouve
@ -113,20 +133,32 @@ Reuven Peleg
Richard Lloyd Richard Lloyd
Rodrigo Exterckötter Tjäder Rodrigo Exterckötter Tjäder
Ron Britvich (Britvich) Ron Britvich (Britvich)
Ronald de Man (syzygy1) Ronald de Man (syzygy1, syzygy)
Ryan Schmitt Ryan Schmitt
Ryan Takker Ryan Takker
Sami Kiminki (skiminki)
Sebastian Buchwald (UniQP) Sebastian Buchwald (UniQP)
Sergei Antonov (saproj) Sergei Antonov (saproj)
Sergei Ivanov (svivanov72)
sf-x sf-x
shane31 Shane Booth (shane31)
Steinar Gunderson (sesse)
Stefan Geschwentner (locutus2) Stefan Geschwentner (locutus2)
Stefano Cardanobile (Stefano80) Stefano Cardanobile (Stefano80)
Steinar Gunderson (sesse)
Stéphane Nicolet (snicolet) Stéphane Nicolet (snicolet)
Thanar2 Thanar2
thaspel thaspel
theo77186
Tom Truscott
Tom Vijlbrief (tomtor) Tom Vijlbrief (tomtor)
Torsten Franz (torfranz) Tomasz Sobczyk (Sopel97)
Torsten Franz (torfranz, tfranzer)
Tracey Emery (basepr1me)
Uri Blass (uriblass) 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

View file

@ -1,7 +1,7 @@
## Overview ## Overview
[![Build Status](https://travis-ci.org/official-stockfish/Stockfish.svg?branch=master)](https://travis-ci.org/official-stockfish/Stockfish) [![Build Status](https://travis-ci.org/official-stockfish/Stockfish.svg?branch=master)](https://travis-ci.org/official-stockfish/Stockfish)
[![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish) [![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?branch=master&svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master)
[Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine [Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine
derived from Glaurung 2.1. It is not a complete chess program and requires a derived from Glaurung 2.1. It is not a complete chess program and requires a
@ -42,7 +42,7 @@ Currently, Stockfish has the following UCI options:
this equal to the number of CPU cores available. this equal to the number of CPU cores available.
* #### Hash * #### 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 Hash
Clear the hash table. Clear the hash table.
@ -55,7 +55,16 @@ Currently, Stockfish has the following UCI options:
Leave at 1 for best performance. Leave at 1 for best performance.
* #### Skill Level * #### 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 * #### 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
@ -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 needed for optimal play and in addition being able to take into account
the 50-move rule. 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 ## 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* set/unset some switches in the compiler command line; see file *types.h*
for a quick reference. 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 ## 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 ### Donating hardware
Improving Stockfish requires a massive amount of testing. You can donate 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) 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](http://tests.stockfishchess.org/tests). and view the current tests on [Fishtest](https://tests.stockfishchess.org/tests).
### Improving the code ### 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 * [In this wiki,](https://www.chessprogramming.org) many techniques used in
Stockfish are explained with a lot of background information. Stockfish are explained with a lot of background information.
@ -170,7 +211,7 @@ Nevertheless, a helpful resource.
* The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish). * The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish).
Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking) Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking)
group and engine testing is done on [Fishtest](http://tests.stockfishchess.org/tests). 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) If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test)
first, where the basics of Stockfish development are explained. first, where the basics of Stockfish development are explained.

View file

@ -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! Thank you!
Username CPU Hours Games played Username CPU Hours Games played
noobpwnftw 3730975 292309380 --------------------------------------------------
mibere 535242 43333774 noobpwnftw 9305707 695548021
crunchy 375564 29121434 mlang 780050 61648867
cw 371664 28748719 dew 621626 43921547
fastgm 318178 22283584 mibere 524702 42238645
JojoM 295354 20958931 crunchy 354587 27344275
dew 215476 17079219 cw 354495 27274181
ctoks 214031 17312035 fastgm 332801 22804359
glinscott 204517 13932027 JojoM 295750 20437451
bking_US 187568 12233168 CSU_Dynasty 262015 21828122
velislav 168404 13336219 Fisherman 232181 18939229
CSU_Dynasty 168069 14417712 ctoks 218866 17622052
Thanar 162373 13842179 glinscott 201989 13780820
spams 149531 10940322 tvijlbrief 201204 15337115
Fisherman 141137 12099359 velislav 188630 14348485
drabel 134441 11180178 gvreuls 187164 15149976
leszek 133658 9812120 bking_US 180289 11876016
marrco 133566 10115202 nordlandia 172076 13467830
sqrt2 128420 10022279 leszek 157152 11443978
vdbergh 123230 9200516 Thanar 148021 12365359
tvijlbrief 123007 9498831 spams 141975 10319326
vdv 120381 8555423 drabel 138073 11121749
malala 117291 8126488 vdv 137850 9394330
dsmith 114010 7622414 mgrabiak 133578 10454324
BrunoBanani 104938 7448565 TueRens 132485 10878471
CoffeeOne 100042 4593596 bcross 129683 11557084
Data 94621 8433010 marrco 126078 9356740
mgrabiak 92248 7787406 sqrt2 125830 9724586
bcross 89440 8506568 robal 122873 9593418
brabos 81868 6647613 vdbergh 120766 8926915
BRAVONE 80811 5341681 malala 115926 8002293
psk 77195 6156031 CoffeeOne 114241 5004100
nordlandia 74833 6231930 dsmith 113189 7570238
robal 72818 5969856 BrunoBanani 104644 7436849
TueRens 72523 6383294 Data 92328 8220352
sterni1971 71049 5647590 mhoram 89333 6695109
sunu 65855 5360884 davar 87924 7009424
mhoram 65034 5192880 xoto 81094 6869316
davar 64794 5457564 ElbertoOne 80899 7023771
nssy 64607 5371952 grandphish2 78067 6160199
Pking_cda 64499 5704075 brabos 77212 6186135
biffhero 63557 5480444 psk 75733 5984901
teddybaer 62147 5585620 BRAVONE 73875 5054681
solarlight 61278 5402642 sunu 70771 5597972
ElbertoOne 60156 5504304 sterni1971 70605 5590573
jromang 58854 4704502 MaZePallas 66886 5188978
dv8silencer 57421 3961325 Vizvezdenec 63708 4967313
tinker 56039 4204914 nssy 63462 5259388
Freja 50331 3808121 jromang 61634 4940891
renouve 50318 3544864 teddybaer 61231 5407666
robnjr 47504 4131742 Pking_cda 60099 5293873
grandphish2 47377 4110003 solarlight 57469 5028306
eva42 46857 4075716 dv8silencer 56913 3883992
ttruscott 46802 3811534 tinker 54936 4086118
finfish 46244 3481661 renouve 49732 3501516
rap 46201 3219490 Freja 49543 3733019
ronaldjerum 45641 3964331 robnjr 46972 4053117
xoto 44998 4170431 rap 46563 3219146
gvreuls 44359 3902234 Bobo1239 46036 3817196
bigpen0r 41780 3448224 ttruscott 45304 3649765
Bobo1239 40767 3657490 racerschmacer 44881 3975413
Antihistamine 39218 2792761 finfish 44764 3370515
mhunt 38991 2697512 eva42 41783 3599691
racerschmacer 38929 3756111 biffhero 40263 3111352
VoyagerOne 35896 3378887 bigpen0r 39817 3291647
homyur 35561 3012398 mhunt 38871 2691355
rkl 33217 2978536 ronaldjerum 38820 3240695
pb00067 33034 2803485 Antihistamine 38785 2761312
speedycpu 32043 2531964 pb00067 38038 3086320
SC 31954 2848432 speedycpu 37591 3003273
EthanOConnor 31638 2143255 rkl 37207 3289580
oryx 30962 2899534 VoyagerOne 37050 3441673
gri 30108 2429137 jbwiebe 35320 2805433
csnodgrass 29396 2808611 cuistot 34191 2146279
Garf 28887 2873564 homyur 33927 2850481
Pyafue 28885 1986098 manap 32873 2327384
jkiiski 28014 1923255 gri 32538 2515779
slakovv 27017 2031279 oryx 31267 2899051
Prcuvu 26300 2307154 EthanOConnor 30959 2090311
hyperbolic.tom 26248 2200777 SC 30832 2730764
jbwiebe 25663 2129063 csnodgrass 29505 2688994
anst 25525 2279159 jmdana 29458 2205261
Patrick_G 24222 1835674 strelock 28219 2067805
nabildanial 23524 1586321 jkiiski 27832 1904470
achambord 23495 1942546 Pyafue 27533 1902349
Sharaf_DG 22975 1790697 Garf 27515 2747562
chriswk 22876 1947731 eastorwest 27421 2317535
ncfish1 22689 1830009 slakovv 26903 2021889
cuistot 22201 1383031 Prcuvu 24835 2170122
Zirie 21171 1493227 anst 24714 2190091
Isidor 20634 1736219 hyperbolic.tom 24319 2017394
JanErik 20596 1791991 Patrick_G 23687 1801617
xor12 20535 1819280 Sharaf_DG 22896 1786697
team-oh 20364 1653708 nabildanial 22195 1519409
nesoneg 20264 1493435 chriswk 21931 1868317
dex 20110 1682756 achambord 21665 1767323
rstoesser 19802 1335177 Zirie 20887 1472937
Vizvezdenec 19750 1695579 team-oh 20217 1636708
eastorwest 19531 1841839 Isidor 20096 1680691
sg4032 18913 1720157 ncfish1 19931 1520927
horst.prack 18425 1708197 nesoneg 19875 1463031
cisco2015 18408 1793774 Spprtr 19853 1548165
ianh2105 18133 1668562 JanErik 19849 1703875
MazeOfGalious 18022 1644593 agg177 19478 1395014
ville 17900 1539130 SFTUser 19231 1567999
j3corre 17607 975954 xor12 19017 1680165
eudhan 17502 1424648 sg4032 18431 1641865
jmdana 17351 1287546 rstoesser 18118 1293588
iisiraider 17175 1118788 MazeOfGalious 17917 1629593
jundery 17172 1115855 j3corre 17743 941444
wei 16852 1822582 cisco2015 17725 1690126
SFTUser 16635 1363975 ianh2105 17706 1632562
purplefishies 16621 1106850 dex 17678 1467203
DragonLord 16599 1252348 jundery 17194 1115855
chris 15274 1575333 iisiraider 17019 1101015
IgorLeMasson 15201 1364148 horst.prack 17012 1465656
dju 15074 914278 Adrian.Schmidt123 16563 1281436
Flopzee 14700 1331632 purplefishies 16342 1092533
OssumOpossum 14149 1029265 wei 16274 1745989
enedene 13762 935618 ville 16144 1384026
ako027ako 13442 1250249 eudhan 15712 1283717
AdrianSA 13324 924980 OuaisBla 15581 972000
bpfliegel 13318 886523 DragonLord 15559 1162790
Nikolay.IT 13260 1155612 dju 14716 875569
jpulman 12776 854815 chris 14479 1487385
joster 12438 988413 0xB00B1ES 14079 1001120
fatmurphy 12015 901134 OssumOpossum 13776 1007129
Nesa92 11711 1132245 enedene 13460 905279
Adrian.Schmidt123 11542 898699 bpfliegel 13346 884523
modolief 11228 926456 Ente 13198 1156722
Dark_wizzie 11214 1017910 IgorLeMasson 13087 1147232
mschmidt 10973 818594 jpulman 13000 870599
Andrew Grant 10780 947859 ako027ako 12775 1173203
infinity 10762 746397 Nikolay.IT 12352 1068349
SapphireBrand 10692 1024604 Andrew Grant 12327 895539
Thomas A. Anderson 10553 736094 joster 12008 950160
basepi 10434 935168 AdrianSA 11996 804972
lantonov 10325 972610 Nesa92 11455 1111993
pgontarz 10294 878746 fatmurphy 11345 853210
Spprtr 10189 823246 Dark_wizzie 11108 1007152
crocogoat 10115 1017325 modolief 10869 896470
stocky 10083 718114 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

View file

@ -35,25 +35,29 @@ BINDIR = $(PREFIX)/bin
### Built-in benchmark for pgo-builds ### Built-in benchmark for pgo-builds
PGOBENCH = ./$(EXE) bench PGOBENCH = ./$(EXE) bench
### Object files ### Source and object files
OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \ SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \
material.o misc.o movegen.o movepick.o pawns.o position.o psqt.o \ material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \
search.o thread.o timeman.o tt.o uci.o ucioption.o syzygy/tbprobe.o \ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
eval/evaluate_mir_inv_tools.o \ eval/evaluate_mir_inv_tools.cpp \
eval/nnue/evaluate_nnue.o \ eval/nnue/evaluate_nnue.cpp \
eval/nnue/evaluate_nnue_learner.o \ eval/nnue/evaluate_nnue_learner.cpp \
eval/nnue/features/half_kp.o \ eval/nnue/features/half_kp.cpp \
eval/nnue/features/half_relative_kp.o \ eval/nnue/features/half_relative_kp.cpp \
eval/nnue/features/k.o \ eval/nnue/features/k.cpp \
eval/nnue/features/p.o \ eval/nnue/features/p.cpp \
eval/nnue/features/castling_right.o \ eval/nnue/features/castling_right.cpp \
eval/nnue/features/enpassant.o \ eval/nnue/features/enpassant.cpp \
eval/nnue/nnue_test_command.o \ eval/nnue/nnue_test_command.cpp \
extra/sfen_packer.o \ extra/sfen_packer.cpp \
learn/gensfen2019.o \ learn/gensfen2019.cpp \
learn/learner.o \ learn/learner.cpp \
learn/learning_tools.o \ learn/learning_tools.cpp \
learn/multi_think.o learn/multi_think.cpp
OBJS = $(SRCS:.cpp=.o)
VPATH = syzygy
### Establish the operating system name ### Establish the operating system name
KERNEL = $(shell uname -s) KERNEL = $(shell uname -s)
@ -151,6 +155,8 @@ endif
ifeq ($(ARCH),ppc-64) ifeq ($(ARCH),ppc-64)
arch = ppc64 arch = ppc64
bits = 64 bits = 64
popcnt = yes
prefetch = yes
endif endif
@ -329,7 +335,9 @@ endif
### 3.6 popcnt ### 3.6 popcnt
ifeq ($(popcnt),yes) ifeq ($(popcnt),yes)
ifeq ($(comp),icc) ifeq ($(arch),ppc64)
CXXFLAGS += -DUSE_POPCNT
else ifeq ($(comp),icc)
CXXFLAGS += -msse3 -DUSE_POPCNT CXXFLAGS += -msse3 -DUSE_POPCNT
else else
CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT
@ -391,10 +399,10 @@ help:
@echo "" @echo ""
@echo "Supported archs:" @echo "Supported archs:"
@echo "" @echo ""
@echo "x86-64 > x86 64-bit" @echo "x86-64-bmi2 > x86 64-bit with pext support (also enables SSE4)"
@echo "x86-64-modern > x86 64-bit with popcnt support" @echo "x86-64-modern > x86 64-bit with popcnt support (also enables SSE3)"
@echo "x86-64-bmi2 > x86 64-bit with pext support" @echo "x86-64 > x86 64-bit generic"
@echo "x86-32 > x86 32-bit with SSE support" @echo "x86-32 > x86 32-bit (also enables SSE)"
@echo "x86-32-old > x86 32-bit fall back for old hardware" @echo "x86-32-old > x86 32-bit fall back for old hardware"
@echo "ppc-64 > PPC 64-bit" @echo "ppc-64 > PPC 64-bit"
@echo "ppc-32 > PPC 32-bit" @echo "ppc-32 > PPC 32-bit"
@ -417,11 +425,11 @@ help:
@echo "Advanced examples, for experienced users: " @echo "Advanced examples, for experienced users: "
@echo "" @echo ""
@echo "make build ARCH=x86-64 COMP=clang" @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 "" @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 \ config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \
clang-profile-use clang-profile-make clang-profile-use clang-profile-make
@ -462,7 +470,7 @@ objclean:
# clean auxiliary profiling files # clean auxiliary profiling files
profileclean: profileclean:
@rm -rf profdir @rm -rf profdir
@rm -f bench.txt *.gcda ./syzygy/*.gcda *.gcno ./syzygy/*.gcno @rm -f bench.txt *.gcda *.gcno
@rm -f stockfish.profdata *.profraw @rm -f stockfish.profdata *.profraw
default: default:

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -61,6 +61,11 @@ const vector<string> Defaults = {
"1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1", "1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1",
"6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 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", "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 // 5-man positions
"8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate "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 fenFile = (is >> token) ? token : "default";
string limitType = (is >> token) ? token : "depth"; string limitType = (is >> token) ? token : "depth";
go = "go " + limitType + " " + limit; go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit;
if (fenFile == "default") if (fenFile == "default")
fens = Defaults; fens = Defaults;
@ -139,9 +144,9 @@ vector<string> setup_bench(const Position& current, istream& is) {
file.close(); file.close();
} }
list.emplace_back("ucinewgame");
list.emplace_back("setoption name Threads value " + threads); list.emplace_back("setoption name Threads value " + threads);
list.emplace_back("setoption name Hash value " + ttSize); list.emplace_back("setoption name Hash value " + ttSize);
list.emplace_back("ucinewgame");
for (const string& fen : fens) for (const string& fen : fens)
if (fen.find("setoption") != string::npos) if (fen.find("setoption") != string::npos)

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -19,8 +19,8 @@
*/ */
#include <cassert> #include <cassert>
#include <numeric>
#include <vector> #include <vector>
#include <bitset>
#include "bitboard.h" #include "bitboard.h"
#include "types.h" #include "types.h"
@ -31,8 +31,7 @@ namespace {
// Positions with the pawn on files E to H will be mirrored before probing. // 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 constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608
// Each uint32_t stores results of 32 positions, one per bit std::bitset<MAX_INDEX> KPKBitbase;
uint32_t KPKBitbase[MAX_INDEX / 32];
// A KPK bitbase index is an integer in [0, IndexMax] range // 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 12: side to move (WHITE or BLACK)
// bit 13-14: white pawn file (from FILE_A to FILE_D) // 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) // 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) { unsigned index(Color stm, Square bksq, Square wksq, Square psq) {
return wksq | (bksq << 6) | (us << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15); return int(wksq) | (bksq << 6) | (stm << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15);
} }
enum Result { enum Result {
@ -60,12 +59,9 @@ namespace {
KPKPosition() = default; KPKPosition() = default;
explicit KPKPosition(unsigned idx); explicit KPKPosition(unsigned idx);
operator Result() const { return result; } operator Result() const { return result; }
Result classify(const std::vector<KPKPosition>& db) Result classify(const std::vector<KPKPosition>& db);
{ return us == WHITE ? classify<WHITE>(db) : classify<BLACK>(db); }
template<Color Us> Result classify(const std::vector<KPKPosition>& db); Color stm;
Color us;
Square ksq[COLOR_NB], psq; Square ksq[COLOR_NB], psq;
Result result; Result result;
}; };
@ -73,12 +69,11 @@ namespace {
} // 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); assert(file_of(wpsq) <= FILE_D);
unsigned idx = index(us, bksq, wksq, wpsq); return KPKBitbase[index(stm, bksq, wksq, wpsq)];
return KPKBitbase[idx / 32] & (1 << (idx & 0x1F));
} }
@ -97,10 +92,10 @@ void Bitbases::init() {
for (repeat = idx = 0; idx < MAX_INDEX; ++idx) for (repeat = idx = 0; idx < MAX_INDEX; ++idx)
repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN); 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) for (idx = 0; idx < MAX_INDEX; ++idx)
if (db[idx] == WIN) 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[WHITE] = Square((idx >> 0) & 0x3F);
ksq[BLACK] = Square((idx >> 6) & 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))); 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 // Check if two pieces are on the same square or if a king can be captured
if ( distance(ksq[WHITE], ksq[BLACK]) <= 1 if ( distance(ksq[WHITE], ksq[BLACK]) <= 1
|| ksq[WHITE] == psq || ksq[WHITE] == psq
|| ksq[BLACK] == psq || ksq[BLACK] == psq
|| (us == WHITE && (PawnAttacks[WHITE][psq] & ksq[BLACK]))) || (stm == WHITE && (pawn_attacks_bb(WHITE, psq) & ksq[BLACK])))
result = INVALID; result = INVALID;
// Immediate win if a pawn can be promoted without getting captured // Immediate win if a pawn can be promoted without getting captured
else if ( us == WHITE else if ( stm == WHITE
&& rank_of(psq) == RANK_7 && rank_of(psq) == RANK_7
&& ksq[us] != psq + NORTH && ksq[stm] != psq + NORTH
&& ( distance(ksq[~us], psq + NORTH) > 1 && ( distance(ksq[~stm], psq + NORTH) > 1
|| (PseudoAttacks[KING][ksq[us]] & (psq + NORTH)))) || (attacks_bb<KING>(ksq[stm]) & (psq + NORTH))))
result = WIN; result = WIN;
// Immediate draw if it is a stalemate or a king captures undefended pawn // Immediate draw if it is a stalemate or a king captures undefended pawn
else if ( us == BLACK else if ( stm == BLACK
&& ( !(PseudoAttacks[KING][ksq[us]] & ~(PseudoAttacks[KING][ksq[~us]] | PawnAttacks[~us][psq])) && ( !(attacks_bb<KING>(ksq[stm]) & ~(attacks_bb<KING>(ksq[~stm]) | pawn_attacks_bb(~stm, psq)))
|| (PseudoAttacks[KING][ksq[us]] & psq & ~PseudoAttacks[KING][ksq[~us]]))) || (attacks_bb<KING>(ksq[stm]) & psq & ~attacks_bb<KING>(ksq[~stm]))))
result = DRAW; result = DRAW;
// Position will be classified later // Position will be classified later
@ -139,7 +134,6 @@ namespace {
result = UNKNOWN; result = UNKNOWN;
} }
template<Color Us>
Result KPKPosition::classify(const std::vector<KPKPosition>& db) { Result KPKPosition::classify(const std::vector<KPKPosition>& db) {
// White to move: If one move leads to a position classified as WIN, the result // 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 // 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 // as WIN, the position is classified as WIN, otherwise the current position is
// classified as UNKNOWN. // classified as UNKNOWN.
const Result Good = (stm == WHITE ? WIN : DRAW);
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); const Result Bad = (stm == WHITE ? DRAW : WIN);
constexpr Result Good = (Us == WHITE ? WIN : DRAW);
constexpr Result Bad = (Us == WHITE ? DRAW : WIN);
Result r = INVALID; Result r = INVALID;
Bitboard b = PseudoAttacks[KING][ksq[Us]]; Bitboard b = attacks_bb<KING>(ksq[stm]);
while (b) while (b)
r |= Us == WHITE ? db[index(Them, ksq[Them] , pop_lsb(&b), psq)] r |= stm == WHITE ? db[index(BLACK, ksq[BLACK] , pop_lsb(&b), psq)]
: db[index(Them, pop_lsb(&b), ksq[Them] , psq)]; : db[index(WHITE, pop_lsb(&b), ksq[WHITE], psq)];
if (Us == WHITE) if (stm == WHITE)
{ {
if (rank_of(psq) < RANK_7) // Single push 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 if ( rank_of(psq) == RANK_2 // Double push
&& psq + NORTH != ksq[Us] && psq + NORTH != ksq[WHITE]
&& psq + NORTH != ksq[Them]) && psq + NORTH != ksq[BLACK])
r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH + NORTH)]; r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH + NORTH)];
} }
return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad; return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad;

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -69,32 +69,14 @@ const std::string Bitboards::pretty(Bitboard b) {
void Bitboards::init() { void Bitboards::init() {
for (unsigned i = 0; i < (1 << 16); ++i) 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) for (Square s = SQ_A1; s <= SQ_H8; ++s)
SquareBB[s] = (1ULL << s); SquareBB[s] = (1ULL << s);
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, 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;
}
}
Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST }; Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST };
Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_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) 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[BISHOP][s1] = attacks_bb<BISHOP>(s1, 0);
PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(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 sliding_attack(Direction directions[], Square sq, Bitboard occupied) {
Bitboard attack = 0; Bitboard attacks = 0;
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
for (Square s = sq + directions[i]; {
is_ok(s) && distance(s, s - directions[i]) == 1; Square s = sq;
s += directions[i]) while(safe_destination(s, directions[i]) && !(occupied & s))
{ attacks |= (s += directions[i]);
attack |= s; }
if (occupied & s) return attacks;
break;
}
return attack;
} }

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -106,7 +106,7 @@ extern Magic RookMagics[SQUARE_NB];
extern Magic BishopMagics[SQUARE_NB]; extern Magic BishopMagics[SQUARE_NB];
inline Bitboard square_bb(Square s) { inline Bitboard square_bb(Square s) {
assert(s >= SQ_A1 && s <= SQ_H8); assert(is_ok(s));
return SquareBB[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^=(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) { constexpr bool more_than_one(Bitboard b) {
return b & (b - 1); return b & (b - 1);
} }
inline bool opposite_colors(Square s1, Square s2) { constexpr bool opposite_colors(Square s1, Square s2) {
return bool(DarkSquares & s1) != bool(DarkSquares & 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> template<Direction D>
constexpr Bitboard shift(Bitboard b) { constexpr Bitboard shift(Bitboard b) {
@ -170,6 +176,12 @@ constexpr Bitboard pawn_attacks_bb(Bitboard b) {
: shift<SOUTH_WEST>(b) | shift<SOUTH_EAST>(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 /// pawn_double_attacks_bb() returns the squares doubly attacked by pawns of the
/// given color from the squares in the given bitboard. /// 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_bb() returns a bitboard representing all the squares on the
/// adjacent files of the given one. /// adjacent files of the given one.
inline Bitboard adjacent_files_bb(File f) { inline Bitboard adjacent_files_bb(Square s) {
return shift<EAST>(file_bb(f)) | shift<WEST>(file_bb(f)); 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. /// If the given squares are not on a same file/rank/diagonal, return 0.
inline Bitboard between_bb(Square s1, Square s2) { inline Bitboard between_bb(Square s1, Square s2) {
return LineBB[s1][s2] & ( (AllSquares << (s1 + (s1 < s2))) Bitboard b = LineBB[s1][s2] & ((AllSquares << s1) ^ (AllSquares << s2));
^(AllSquares << (s2 + !(s1 < 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. /// 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) { inline Bitboard forward_ranks_bb(Color c, Square s) {
return c == WHITE ? ~Rank1BB << 8 * (rank_of(s) - RANK_1) return c == WHITE ? ~Rank1BB << 8 * relative_rank(WHITE, s)
: ~Rank8BB >> 8 * (RANK_8 - rank_of(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. /// starting from the given square.
inline Bitboard pawn_attack_span(Color c, Square s) { 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. /// the given color and on the given square is a passed pawn.
inline Bitboard passed_pawn_span(Color c, Square s) { 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<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<> 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) { inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); }
return v < lo ? lo : v > hi ? hi : v; 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 /// attacks_bb(Square) returns the pseudo attacks of the give piece type
/// piece of type Pt (bishop or rook) placed on 's'. /// 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> template<PieceType Pt>
inline Bitboard attacks_bb(Square s, Bitboard occupied) { inline Bitboard attacks_bb(Square s, Bitboard occupied) {
const Magic& m = Pt == ROOK ? RookMagics[s] : BishopMagics[s]; assert((Pt != PAWN) && (is_ok(s)));
return m.attacks[m.index(occupied)];
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) { inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
assert(pt != PAWN); assert((pt != PAWN) && (is_ok(s)));
switch (pt) 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 /// pop_lsb() finds and clears the least significant bit in a non-zero bitboard
inline Square pop_lsb(Bitboard* b) { inline Square pop_lsb(Bitboard* b) {
assert(*b);
const Square s = lsb(*b); const Square s = lsb(*b);
*b &= *b - 1; *b &= *b - 1;
return s; return s;
} }
/// frontmost_sq() and backmost_sq() return the most/least advanced square in /// frontmost_sq() returns the most advanced square for the given color,
/// the given bitboard relative to the given color. /// requires a non-zero bitboard.
inline Square frontmost_sq(Color c, Bitboard b) {
inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); } assert(b);
inline Square backmost_sq(Color c, Bitboard b) { return c == WHITE ? lsb(b) : msb(b); } return c == WHITE ? msb(b) : lsb(b);
}
#endif // #ifndef BITBOARD_H_INCLUDED #endif // #ifndef BITBOARD_H_INCLUDED

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -24,42 +24,23 @@
#include "endgame.h" #include "endgame.h"
#include "movegen.h" #include "movegen.h"
using std::string;
namespace { 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. // in KX vs K and KQ vs KR endgames.
constexpr int PushToEdges[SQUARE_NB] = { inline int push_to_edge(Square s) {
100, 90, 80, 70, 70, 80, 90, 100, int rd = edge_distance(rank_of(s)), fd = edge_distance(file_of(s));
90, 70, 60, 50, 50, 60, 70, 90, return 90 - (7 * fd * fd / 2 + 7 * rd * rd / 2);
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
};
// Table used to drive the king towards a corner square of the // Used to drive the king towards A1H8 corners in KBN vs K endgames.
// right color in KBN vs K endgames. inline int push_to_corner(Square s) {
constexpr int PushToCorners[SQUARE_NB] = { return abs(7 - rank_of(s) - file_of(s));
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
};
// Tables used to drive a piece towards or away from another piece // Drive a piece close to or away from another piece
constexpr int PushClose[8] = { 0, 0, 100, 80, 60, 40, 20, 10 }; inline int push_close(Square s1, Square s2) { return 140 - 20 * distance(s1, s2); }
constexpr int PushAway [8] = { 0, 5, 20, 40, 60, 80, 90, 100 }; inline int push_away(Square s1, Square s2) { return 120 - push_close(s1, s2); }
// Pawn Rank based scaling factors used in KRPPKRP endgame
constexpr int KRPPKRPScaleFactors[RANK_NB] = { 0, 9, 10, 14, 21, 44, 0, 0 };
#ifndef NDEBUG #ifndef NDEBUG
bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) { bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) {
@ -74,9 +55,9 @@ namespace {
assert(pos.count<PAWN>(strongSide) == 1); assert(pos.count<PAWN>(strongSide) == 1);
if (file_of(pos.square<PAWN>(strongSide)) >= FILE_E) 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 } // namespace
@ -88,27 +69,26 @@ namespace Endgames {
void init() { void init() {
add<KPK>("KPK"); add<KPK>("KPK");
add<KNNK>("KNNK"); add<KNNK>("KNNK");
add<KBNK>("KBNK"); add<KBNK>("KBNK");
add<KRKP>("KRKP"); add<KRKP>("KRKP");
add<KRKB>("KRKB"); add<KRKB>("KRKB");
add<KRKN>("KRKN"); add<KRKN>("KRKN");
add<KQKP>("KQKP"); add<KQKP>("KQKP");
add<KQKR>("KQKR"); add<KQKR>("KQKR");
add<KNNKP>("KNNKP"); add<KNNKP>("KNNKP");
add<KNPK>("KNPK"); add<KRPKR>("KRPKR");
add<KNPKB>("KNPKB"); add<KRPKB>("KRPKB");
add<KRPKR>("KRPKR"); add<KBPKB>("KBPKB");
add<KRPKB>("KRPKB"); add<KBPKN>("KBPKN");
add<KBPKB>("KBPKB"); add<KBPPKB>("KBPPKB");
add<KBPKN>("KBPKN"); add<KRPPKRP>("KRPPKRP");
add<KBPPKB>("KBPPKB");
add<KRPPKRP>("KRPPKRP");
} }
} }
/// Mate with KX vs K. This function is used to evaluate positions with /// 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 /// 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 /// 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) Value result = pos.non_pawn_material(strongSide)
+ pos.count<PAWN>(strongSide) * PawnValueEg + pos.count<PAWN>(strongSide) * PawnValueEg
+ PushToEdges[loserKSq] + push_to_edge(loserKSq)
+ PushClose[distance(winnerKSq, loserKSq)]; + push_close(winnerKSq, loserKSq);
if ( pos.count<QUEEN>(strongSide) if ( pos.count<QUEEN>(strongSide)
|| pos.count<ROOK>(strongSide) || pos.count<ROOK>(strongSide)
||(pos.count<BISHOP>(strongSide) && pos.count<KNIGHT>(strongSide)) ||(pos.count<BISHOP>(strongSide) && pos.count<KNIGHT>(strongSide))
|| ( (pos.pieces(strongSide, BISHOP) & ~DarkSquares) || ( (pos.pieces(strongSide, BISHOP) & ~DarkSquares)
&& (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; 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 loserKSq = pos.square<KING>(weakSide);
Square bishopSq = pos.square<BISHOP>(strongSide); 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). // to drive to opposite corners (A8/H1).
Value result = VALUE_KNOWN_WIN Value result = (VALUE_KNOWN_WIN + 3520)
+ PushClose[distance(winnerKSq, loserKSq)] + push_close(winnerKSq, loserKSq)
+ PushToCorners[opposite_colors(bishopSq, SQ_A1) ? ~loserKSq : 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; 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<> template<>
Value Endgame<KPK>::operator()(const Position& pos) const { 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, strongSide, RookValueMg, 0));
assert(verify_material(pos, weakSide, BishopValueMg, 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; 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 bksq = pos.square<KING>(weakSide);
Square bnsq = pos.square<KNIGHT>(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; 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 loserKSq = pos.square<KING>(weakSide);
Square pawnSq = pos.square<PAWN>(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 if ( relative_rank(weakSide, pawnSq) != RANK_7
|| distance(loserKSq, pawnSq) != 1 || distance(loserKSq, pawnSq) != 1
|| !((FileABB | FileCBB | FileFBB | FileHBB) & pawnSq)) || ((FileBBB | FileDBB | FileEBB | FileGBB) & pawnSq))
result += QueenValueEg - PawnValueEg; result += QueenValueEg - PawnValueEg;
return strongSide == pos.side_to_move() ? result : -result; return strongSide == pos.side_to_move() ? result : -result;
@ -302,23 +282,24 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
Value result = QueenValueEg Value result = QueenValueEg
- RookValueEg - RookValueEg
+ PushToEdges[loserKSq] + push_to_edge(loserKSq)
+ PushClose[distance(winnerKSq, loserKSq)]; + push_close(winnerKSq, loserKSq);
return strongSide == pos.side_to_move() ? result : -result; 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<> template<>
Value Endgame<KNNKP>::operator()(const Position& pos) const { Value Endgame<KNNKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0)); assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
Value result = 2 * KnightValueEg Value result = PawnValueEg
- PawnValueEg + 2 * push_to_edge(pos.square<KING>(weakSide))
+ PushToEdges[pos.square<KING>(weakSide)]; - 10 * relative_rank(weakSide, pos.square<PAWN>(weakSide));
return strongSide == pos.side_to_move() ? result : -result; 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 // No assertions about the material of weakSide, because we want draws to
// be detected even when the weaker side has some pawns. // be detected even when the weaker side has some pawns.
Bitboard pawns = pos.pieces(strongSide, PAWN); Bitboard strongPawns = pos.pieces(strongSide, PAWN);
File pawnsFile = file_of(lsb(pawns)); Bitboard allPawns = pos.pieces(PAWN);
// All pawns are on a single rook file? // All strongSide pawns are on a single rook file?
if ( (pawnsFile == FILE_A || pawnsFile == FILE_H) if (!(strongPawns & ~FileABB) || !(strongPawns & ~FileHBB))
&& !(pawns & ~file_bb(pawnsFile)))
{ {
Square bishopSq = pos.square<BISHOP>(strongSide); Square bishopSq = pos.square<BISHOP>(strongSide);
Square queeningSq = relative_square(strongSide, make_square(pawnsFile, RANK_8)); Square queeningSq = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8));
Square kingSq = pos.square<KING>(weakSide); Square weakKingSq = pos.square<KING>(weakSide);
if ( opposite_colors(queeningSq, bishopSq) if ( opposite_colors(queeningSq, bishopSq)
&& distance(queeningSq, kingSq) <= 1) && distance(queeningSq, weakKingSq) <= 1)
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
} }
// If all the pawns are on the same B or G file, then it's potentially a 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) if ((!(allPawns & ~FileBBB) || !(allPawns & ~FileGBB))
&& !(pos.pieces(PAWN) & ~file_bb(pawnsFile))
&& pos.non_pawn_material(weakSide) == 0 && pos.non_pawn_material(weakSide) == 0
&& pos.count<PAWN>(weakSide) >= 1) && pos.count<PAWN>(weakSide) >= 1)
{ {
// Get weakSide pawn that is closest to the home rank // Get the least advanced weakSide pawn
Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN)); Square weakPawnSq = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN));
Square strongKingSq = pos.square<KING>(strongSide); Square strongKingSq = pos.square<KING>(strongSide);
Square weakKingSq = pos.square<KING>(weakSide); 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, // 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 // the bishop cannot attack it or they only have one pawn left
if ( relative_rank(strongSide, weakPawnSq) == RANK_7 if ( relative_rank(strongSide, weakPawnSq) == RANK_7
&& (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide))) && (strongPawns & (weakPawnSq + pawn_push(weakSide)))
&& (opposite_colors(bishopSq, weakPawnSq) || pos.count<PAWN>(strongSide) == 1)) && (opposite_colors(bishopSq, weakPawnSq) || !more_than_one(strongPawns)))
{ {
int strongKingDist = distance(weakPawnSq, strongKingSq); int strongKingDist = distance(weakPawnSq, strongKingSq);
int weakKingDist = distance(weakPawnSq, weakKingSq); 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, pos.square<KING>(strongSide)) >= RANK_4
&& relative_rank(weakSide, rsq) == RANK_3 && relative_rank(weakSide, rsq) == RANK_3
&& ( pos.pieces(weakSide, PAWN) && ( pos.pieces(weakSide, PAWN)
& pos.attacks_from<KING>(kingSq) & attacks_bb<KING>(kingSq)
& pos.attacks_from<PAWN>(rsq, strongSide))) & pawn_attacks_bb(strongSide, rsq)))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@ -556,7 +535,7 @@ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
// the corner // the corner
if ( rk == RANK_6 if ( rk == RANK_6
&& distance(psq + 2 * push, ksq) <= 1 && distance(psq + 2 * push, ksq) <= 1
&& (PseudoAttacks[BISHOP][bsq] & (psq + push)) && (attacks_bb<BISHOP>(bsq) & (psq + push))
&& distance<File>(bsq, psq) >= 2) && distance<File>(bsq, psq) >= 2)
return ScaleFactor(8); return ScaleFactor(8);
} }
@ -587,7 +566,7 @@ ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
&& relative_rank(strongSide, bksq) > r) && relative_rank(strongSide, bksq) > r)
{ {
assert(r > RANK_1 && r < RANK_7); assert(r > RANK_1 && r < RANK_7);
return ScaleFactor(KRPPKRPScaleFactors[r]); return ScaleFactor(7 * r);
} }
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
@ -605,11 +584,9 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
Square ksq = pos.square<KING>(weakSide); Square ksq = pos.square<KING>(weakSide);
Bitboard pawns = pos.pieces(strongSide, PAWN); Bitboard pawns = pos.pieces(strongSide, PAWN);
// If all pawns are ahead of the king, on a single rook file and // If all pawns are ahead of the king on a single rook file, it's a draw.
// the king is within one file of the pawns, it's a draw. if (!((pawns & ~FileABB) || (pawns & ~FileHBB)) &&
if ( !(pawns & ~forward_ranks_bb(weakSide, ksq)) !(pawns & ~passed_pawn_span(weakSide, ksq)))
&& !((pawns & ~FileABB) && (pawns & ~FileHBB))
&& distance<File>(ksq, lsb(pawns)) <= 1)
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@ -632,8 +609,7 @@ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
Square weakKingSq = pos.square<KING>(weakSide); Square weakKingSq = pos.square<KING>(weakSide);
// Case 1: Defending king blocks the pawn, and cannot be driven away // Case 1: Defending king blocks the pawn, and cannot be driven away
if ( file_of(weakKingSq) == file_of(pawnSq) if ( (forward_file_bb(strongSide, pawnSq) & weakKingSq)
&& relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq)
&& ( opposite_colors(weakKingSq, strongBishopSq) && ( opposite_colors(weakKingSq, strongBishopSq)
|| relative_rank(strongSide, weakKingSq) <= RANK_6)) || relative_rank(strongSide, weakKingSq) <= RANK_6))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
@ -694,14 +670,14 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
if ( ksq == blockSq1 if ( ksq == blockSq1
&& opposite_colors(ksq, wbsq) && opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq2 && ( 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)) || distance<Rank>(psq1, psq2) >= 2))
return SCALE_FACTOR_DRAW; return SCALE_FACTOR_DRAW;
else if ( ksq == blockSq2 else if ( ksq == blockSq2
&& opposite_colors(ksq, wbsq) && opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq1 && ( 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; return SCALE_FACTOR_DRAW;
else else
return SCALE_FACTOR_NONE; 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 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 /// 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 /// has at least a draw with the pawn as well. The exception is when the stronger

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -21,10 +21,10 @@
#ifndef ENDGAME_H_INCLUDED #ifndef ENDGAME_H_INCLUDED
#define ENDGAME_H_INCLUDED #define ENDGAME_H_INCLUDED
#include <map>
#include <memory> #include <memory>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <unordered_map>
#include <utility> #include <utility>
#include "position.h" #include "position.h"
@ -57,8 +57,6 @@ enum EndgameCode {
KBPKB, // KBP vs KB KBPKB, // KBP vs KB
KBPPKB, // KBPP vs KB KBPPKB, // KBPP vs KB
KBPKN, // KBP vs KN KBPKN, // KBP vs KN
KNPK, // KNP vs K
KNPKB, // KNP vs KB
KPKP // KP vs KP 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 /// base objects in two std::map. We use polymorphism to invoke the actual
/// endgame function by calling its virtual operator(). /// endgame function by calling its virtual operator().
namespace Endgames { namespace Endgames {
template<typename T> using Ptr = std::unique_ptr<EndgameBase<T>>; 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; extern std::pair<Map<Value>, Map<ScaleFactor>> maps;
void init();
template<typename T> template<typename T>
Map<T>& map() { Map<T>& map() {
return std::get<std::is_same<T, ScaleFactor>::value>(maps); return std::get<std::is_same<T, ScaleFactor>::value>(maps);
@ -117,10 +117,9 @@ namespace Endgames {
template<typename T> template<typename T>
const EndgameBase<T>* probe(Key key) { 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 #endif // #ifndef ENDGAME_H_INCLUDED

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -80,81 +80,81 @@ namespace {
constexpr Value SpaceThreshold = Value(12222); constexpr Value SpaceThreshold = Value(12222);
// KingAttackWeights[PieceType] contains king attack weights by piece type // KingAttackWeights[PieceType] contains king attack weights by piece type
constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 77, 55, 44, 10 }; constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 };
// Penalties for enemy's safe checks // Penalties for enemy's safe checks
constexpr int QueenSafeCheck = 780; constexpr int QueenSafeCheck = 772;
constexpr int RookSafeCheck = 1080; constexpr int RookSafeCheck = 1084;
constexpr int BishopSafeCheck = 635; constexpr int BishopSafeCheck = 645;
constexpr int KnightSafeCheck = 790; constexpr int KnightSafeCheck = 792;
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game, // MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game,
// indexed by piece type and number of attacked squares in the mobility area. // indexed by piece type and number of attacked squares in the mobility area.
constexpr Score MobilityBonus[][32] = { constexpr Score MobilityBonus[][32] = {
{ S(-62,-81), S(-53,-56), S(-12,-30), S( -4,-14), S( 3, 8), S( 13, 15), // Knights { S(-62,-81), S(-53,-56), S(-12,-31), S( -4,-16), S( 3, 5), S( 13, 11), // Knight
S( 22, 23), S( 28, 27), S( 33, 33) }, 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), // Bishops { 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( 55, 54), S( 63, 57), S( 63, 65), S( 68, 73), S( 81, 78), S( 81, 86),
S( 91, 88), S( 98, 97) }, 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(-60,-78), S(-20,-17), S( 2, 23), S( 3, 39), S( 3, 70), S( 11, 99), // Rook
S( 9,112), S( 16,118), S( 30,132), S( 29,142), S( 32,155), S( 38,165), S( 22,103), S( 31,121), S( 40,134), S( 40,139), S( 41,158), S( 48,164),
S( 46,166), S( 48,169), S( 58,171) }, S( 57,168), S( 57,169), S( 62,172) },
{ S(-39,-36), S(-21,-15), S( 3, 8), S( 3, 18), S( 14, 34), S( 22, 54), // Queens { S(-30,-48), S(-12,-30), S( -8, -7), S( -9, 19), S( 20, 40), S( 23, 55), // Queen
S( 28, 61), S( 41, 73), S( 43, 79), S( 48, 92), S( 56, 94), S( 60,104), S( 23, 59), S( 35, 75), S( 38, 78), S( 53, 96), S( 64, 96), S( 65,100),
S( 60,113), S( 66,120), S( 67,123), S( 70,126), S( 71,133), S( 73,136), S( 65,121), S( 66,127), S( 67,131), S( 67,133), S( 72,136), S( 72,141),
S( 79,140), S( 88,143), S( 88,148), S( 99,166), S(102,170), S(102,175), S( 77,147), S( 79,150), S( 93,151), S(108,168), S(108,168), S(108,171),
S(106,184), S(109,191), S(113,206), S(116,212) } S(110,182), S(114,182), S(114,192), S(116,219) }
}; };
// RookOnFile[semiopen/open] contains bonuses for each rook when there is // RookOnFile[semiopen/open] contains bonuses for each rook when there is
// no (friendly) pawn on the rook file. // no (friendly) pawn on the rook file.
constexpr Score RookOnFile[] = { S(18, 7), S(44, 20) }; constexpr Score RookOnFile[] = { S(19, 7), S(48, 29) };
// ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to // ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to
// which piece type attacks which one. Attacks on lesser pieces which are // which piece type attacks which one. Attacks on lesser pieces which are
// pawn-defended are not considered. // pawn-defended are not considered.
constexpr Score ThreatByMinor[PIECE_TYPE_NB] = { constexpr Score ThreatByMinor[PIECE_TYPE_NB] = {
S(0, 0), S(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] = { 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 // PassedRank[Rank] contains a bonus according to the rank of a passed pawn
constexpr Score PassedRank[RANK_NB] = { constexpr Score PassedRank[RANK_NB] = {
S(0, 0), S(5, 18), S(12, 23), S(10, 31), S(57, 62), S(163, 167), S(271, 250) S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
};
// 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)
}; };
// Assorted bonuses and penalties // Assorted bonuses and penalties
constexpr Score BishopPawns = S( 3, 7); constexpr Score BishopPawns = S( 3, 7);
constexpr Score CorneredBishop = S( 50, 50); constexpr Score BishopOnKingRing = S( 24, 0);
constexpr Score FlankAttacks = S( 8, 0); constexpr Score BishopXRayPawns = S( 4, 5);
constexpr Score Hanging = S( 69, 36); constexpr Score CorneredBishop = S( 50, 50);
constexpr Score KingProtector = S( 7, 8); constexpr Score FlankAttacks = S( 8, 0);
constexpr Score KnightOnQueen = S( 16, 12); constexpr Score Hanging = S( 69, 36);
constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score BishopKingProtector = S( 6, 9);
constexpr Score MinorBehindPawn = S( 18, 3); constexpr Score KnightKingProtector = S( 8, 9);
constexpr Score Outpost = S( 9, 3); constexpr Score KnightOnQueen = S( 16, 11);
constexpr Score PawnlessFlank = S( 17, 95); constexpr Score LongDiagonalBishop = S( 45, 0);
constexpr Score RestrictedPiece = S( 7, 7); constexpr Score MinorBehindPawn = S( 18, 3);
constexpr Score RookOnPawn = S( 10, 32); constexpr Score KnightOutpost = S( 56, 36);
constexpr Score SliderOnQueen = S( 59, 18); constexpr Score BishopOutpost = S( 30, 23);
constexpr Score ThreatByKing = S( 24, 89); constexpr Score ReachableOutpost = S( 31, 22);
constexpr Score ThreatByPawnPush = S( 48, 39); constexpr Score PassedFile = S( 11, 8);
constexpr Score ThreatByRank = S( 13, 0); constexpr Score PawnlessFlank = S( 17, 95);
constexpr Score ThreatBySafePawn = S(173, 94); constexpr Score RestrictedPiece = S( 7, 7);
constexpr Score TrappedRook = S( 47, 4); constexpr Score RookOnKingRing = S( 16, 0);
constexpr Score WeakQueen = S( 49, 15); constexpr Score RookOnQueenFile = S( 5, 9);
constexpr Score WeakUnopposedPawn = S( 12, 23); 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 #undef S
@ -176,7 +176,7 @@ namespace {
template<Color Us> Score passed() const; template<Color Us> Score passed() const;
template<Color Us> Score space() const; template<Color Us> Score space() const;
ScaleFactor scale_factor(Value eg) const; ScaleFactor scale_factor(Value eg) const;
Score initiative(Value eg) const; Score initiative(Score score) const;
const Position& pos; const Position& pos;
Material::Entry* me; Material::Entry* me;
@ -193,10 +193,8 @@ namespace {
// color, including x-rays. But diagonal x-rays through pawns are not computed. // color, including x-rays. But diagonal x-rays through pawns are not computed.
Bitboard attackedBy2[COLOR_NB]; Bitboard attackedBy2[COLOR_NB];
// kingRing[color] are the squares adjacent to the king, plus (only for a // kingRing[color] are the squares adjacent to the king plus some other
// king on its first rank) the squares two ranks in front. For instance, // very near squares, depending on king position.
// if black's king is on g8, kingRing[BLACK] is f8, h8, f7, g7, h7, f6, g6
// and h6.
Bitboard kingRing[COLOR_NB]; Bitboard kingRing[COLOR_NB];
// kingAttackersCount[color] is the number of pieces of the given color // kingAttackersCount[color] is the number of pieces of the given color
@ -223,10 +221,10 @@ namespace {
template<Tracing T> template<Color Us> template<Tracing T> template<Color Us>
void Evaluation<T>::initialize() { void Evaluation<T>::initialize() {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = ~Us;
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); constexpr Direction Up = pawn_push(Us);
constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH); constexpr Direction Down = -Up;
constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB: Rank7BB | Rank6BB); constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB : Rank7BB | Rank6BB);
const Square ksq = pos.square<KING>(Us); const Square ksq = pos.square<KING>(Us);
@ -235,26 +233,20 @@ namespace {
// Find our pawns that are blocked or on the first two ranks // Find our pawns that are blocked or on the first two ranks
Bitboard b = pos.pieces(Us, PAWN) & (shift<Down>(pos.pieces()) | LowRanks); Bitboard b = pos.pieces(Us, PAWN) & (shift<Down>(pos.pieces()) | LowRanks);
// Squares occupied by those pawns, by our king or queen or controlled by // Squares occupied by those pawns, by our king or queen, by blockers to attacks on our king
// enemy pawns are excluded from the mobility area. // or controlled by enemy pawns are excluded from the mobility area.
mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them)); mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pos.blockers_for_king(Us) | pe->pawn_attacks(Them));
// Initialize attackedBy[] for king and pawns // 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][PAWN] = pe->pawn_attacks(Us);
attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN]; attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]); attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
// Init our king safety tables // Init our king safety tables
kingRing[Us] = attackedBy[Us][KING]; Square s = make_square(Utility::clamp(file_of(ksq), FILE_B, FILE_G),
if (relative_rank(Us, ksq) == RANK_1) Utility::clamp(rank_of(ksq), RANK_2, RANK_7));
kingRing[Us] |= shift<Up>(kingRing[Us]); kingRing[Us] = attacks_bb<KING>(s) | s;
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]);
kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them)); kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
kingAttacksCount[Them] = kingAttackersWeight[Them] = 0; kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
@ -268,8 +260,8 @@ namespace {
template<Tracing T> template<Color Us, PieceType Pt> template<Tracing T> template<Color Us, PieceType Pt>
Score Evaluation<T>::pieces() { Score Evaluation<T>::pieces() {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = ~Us;
constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH); constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
: Rank5BB | Rank4BB | Rank3BB); : Rank5BB | Rank4BB | Rank3BB);
const Square* pl = pos.squares<Pt>(Us); const Square* pl = pos.squares<Pt>(Us);
@ -284,7 +276,7 @@ namespace {
// Find attacked squares, including x-ray attacks for bishops and rooks // Find attacked squares, including x-ray attacks for bishops and rooks
b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN)) b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN))
: Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK)) : Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK))
: pos.attacks_from<Pt>(s); : attacks_bb<Pt>(s, pos.pieces());
if (pos.blockers_for_king(Us) & s) if (pos.blockers_for_king(Us) & s)
b &= LineBB[pos.square<KING>(Us)][s]; b &= LineBB[pos.square<KING>(Us)][s];
@ -300,6 +292,12 @@ namespace {
kingAttacksCount[Us] += popcount(b & attackedBy[Them][KING]); 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]); int mob = popcount(b & mobilityArea[Us]);
mobility[Us] += MobilityBonus[Pt - 2][mob]; mobility[Us] += MobilityBonus[Pt - 2][mob];
@ -307,60 +305,61 @@ namespace {
if (Pt == BISHOP || Pt == KNIGHT) if (Pt == BISHOP || Pt == KNIGHT)
{ {
// Bonus if piece is on an outpost square or can reach one // Bonus if 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) if (bb & s)
score += Outpost * (Pt == KNIGHT ? 4 : 2) score += (Pt == KNIGHT) ? KnightOutpost : BishopOutpost;
* ((attackedBy[Us][PAWN] & s) ? 2 : 1); else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
score += ReachableOutpost;
else if (bb &= b & ~pos.pieces(Us)) // Bonus for a knight or bishop shielded by pawn
score += Outpost * (Pt == KNIGHT ? 2 : 1)
* ((attackedBy[Us][PAWN] & bb) ? 2 : 1);
// Knight and Bishop bonus for being right behind a pawn
if (shift<Down>(pos.pieces(PAWN)) & s) if (shift<Down>(pos.pieces(PAWN)) & s)
score += MinorBehindPawn; score += MinorBehindPawn;
// Penalty if the piece is far from the king // 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) if (Pt == BISHOP)
{ {
// Penalty according to number of pawns on the same color square as the // Penalty according to the number of our pawns on the same color square as the
// bishop, bigger when the center files are blocked with pawns. // 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()); Bitboard blocked = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces());
score -= BishopPawns * pos.pawns_on_same_color_squares(Us, s) 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 // 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)) if (more_than_one(attacks_bb<BISHOP>(s, pos.pieces(PAWN)) & Center))
score += LongDiagonalBishop; score += LongDiagonalBishop;
}
// An important Chess960 pattern: A cornered bishop blocked by a friendly // An important Chess960 pattern: a cornered bishop blocked by a friendly
// pawn diagonally in front of it is a very serious problem, especially // pawn diagonally in front of it is a very serious problem, especially
// when that pawn is also blocked. // when that pawn is also blocked.
if ( Pt == BISHOP if ( pos.is_chess960()
&& pos.is_chess960() && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)))
&& (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))) {
{ Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST);
Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST); if (pos.piece_on(s + d) == make_piece(Us, PAWN))
if (pos.piece_on(s + d) == make_piece(Us, PAWN)) score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4
score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4 : pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? CorneredBishop * 2
: pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? CorneredBishop * 2 : CorneredBishop;
: CorneredBishop; }
} }
} }
if (Pt == ROOK) if (Pt == ROOK)
{ {
// Bonus for aligning rook with enemy pawns on the same rank/file // Bonus for rook on the same file as a queen
if (relative_rank(Us, s) >= RANK_5) if (file_bb(s) & pos.pieces(QUEEN))
score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]); score += RookOnQueenFile;
// Bonus for rook on an open or semi-open file // Bonus for rook on an open or semi-open file
if (pos.is_on_semiopen_file(Us, s)) if (pos.is_on_semiopen_file(Us, s))
score += RookOnFile[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 // Penalty when trapped by the king, even more if the king cannot castle
else if (mob <= 3) else if (mob <= 3)
@ -390,11 +389,11 @@ namespace {
template<Tracing T> template<Color Us> template<Tracing T> template<Color Us>
Score Evaluation<T>::king() const { 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 constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB
: AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB); : AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB);
Bitboard weak, b1, b2, safe, unsafeChecks = 0; Bitboard weak, b1, b2, b3, safe, unsafeChecks = 0;
Bitboard rookChecks, queenChecks, bishopChecks, knightChecks; Bitboard rookChecks, queenChecks, bishopChecks, knightChecks;
int kingDanger = 0; int kingDanger = 0;
const Square ksq = pos.square<KING>(Us); const Square ksq = pos.square<KING>(Us);
@ -416,9 +415,9 @@ namespace {
// Enemy rooks checks // Enemy rooks checks
rookChecks = b1 & safe & attackedBy[Them][ROOK]; rookChecks = b1 & safe & attackedBy[Them][ROOK];
if (rookChecks) if (rookChecks)
kingDanger += RookSafeCheck; kingDanger += more_than_one(rookChecks) ? RookSafeCheck * 175/100
: RookSafeCheck;
else else
unsafeChecks |= b1 & attackedBy[Them][ROOK]; unsafeChecks |= b1 & attackedBy[Them][ROOK];
@ -429,9 +428,9 @@ namespace {
& safe & safe
& ~attackedBy[Us][QUEEN] & ~attackedBy[Us][QUEEN]
& ~rookChecks; & ~rookChecks;
if (queenChecks) 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 // 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. // which we can't give a queen check, because queen checks are more valuable.
@ -439,42 +438,41 @@ namespace {
& attackedBy[Them][BISHOP] & attackedBy[Them][BISHOP]
& safe & safe
& ~queenChecks; & ~queenChecks;
if (bishopChecks) if (bishopChecks)
kingDanger += BishopSafeCheck; kingDanger += more_than_one(bishopChecks) ? BishopSafeCheck * 3/2
: BishopSafeCheck;
else else
unsafeChecks |= b2 & attackedBy[Them][BISHOP]; unsafeChecks |= b2 & attackedBy[Them][BISHOP];
// Enemy knights checks // Enemy knights checks
knightChecks = pos.attacks_from<KNIGHT>(ksq) & attackedBy[Them][KNIGHT]; knightChecks = attacks_bb<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
if (knightChecks & safe) if (knightChecks & safe)
kingDanger += KnightSafeCheck; kingDanger += more_than_one(knightChecks & safe) ? KnightSafeCheck * 162/100
: KnightSafeCheck;
else else
unsafeChecks |= knightChecks; unsafeChecks |= knightChecks;
// Unsafe or occupied checking squares will also be considered, as long as // Find the squares that opponent attacks in our king flank, the squares
// the square is in the attacker's mobility area. // which they attack twice in that flank, and the squares that we defend.
unsafeChecks &= mobilityArea[Them];
// Find the squares that opponent attacks in our king flank, and the squares
// which are attacked twice in that flank.
b1 = attackedBy[Them][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp; b1 = attackedBy[Them][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
b2 = b1 & attackedBy2[Them]; 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] kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
+ 69 * kingAttacksCount[Them]
+ 185 * popcount(kingRing[Us] & weak) + 185 * popcount(kingRing[Us] & weak)
- 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) + 148 * popcount(unsafeChecks)
- 35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING]) + 98 * popcount(pos.blockers_for_king(Us))
+ 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks) + 69 * kingAttacksCount[Them]
- 873 * !pos.count<QUEEN>(Them) + 3 * kingFlankAttack * kingFlankAttack / 8
- 6 * mg_value(score) / 8
+ mg_value(mobility[Them] - mobility[Us]) + mg_value(mobility[Them] - mobility[Us])
+ 5 * kingFlankAttacks * kingFlankAttacks / 16 - 873 * !pos.count<QUEEN>(Them)
- 7; - 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 // Transform the kingDanger units into a Score, and subtract it from the evaluation
if (kingDanger > 100) if (kingDanger > 100)
@ -485,7 +483,7 @@ namespace {
score -= PawnlessFlank; score -= PawnlessFlank;
// Penalty if king flank is under attack, potentially moving toward the king // Penalty if king flank is under attack, potentially moving toward the king
score -= FlankAttacks * kingFlankAttacks; score -= FlankAttacks * kingFlankAttack;
if (T) if (T)
Trace::add(KING, Us, score); Trace::add(KING, Us, score);
@ -499,8 +497,8 @@ namespace {
template<Tracing T> template<Color Us> template<Tracing T> template<Color Us>
Score Evaluation<T>::threats() const { Score Evaluation<T>::threats() const {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = ~Us;
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); constexpr Direction Up = pawn_push(Us);
constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe; Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe;
@ -520,29 +518,16 @@ namespace {
// Enemies not strongly protected and under our attack // Enemies not strongly protected and under our attack
weak = pos.pieces(Them) & ~stronglyProtected & attackedBy[Us][ALL_PIECES]; 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 // Bonus according to the kind of attacking pieces
if (defended | weak) if (defended | weak)
{ {
b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]); b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]);
while (b) while (b)
{ score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(&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);
}
b = weak & attackedBy[Us][ROOK]; b = weak & attackedBy[Us][ROOK];
while (b) while (b)
{ score += ThreatByRook[type_of(pos.piece_on(pop_lsb(&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);
}
if (weak & attackedBy[Us][KING]) if (weak & attackedBy[Us][KING])
score += ThreatByKing; score += ThreatByKing;
@ -550,18 +535,24 @@ namespace {
b = ~attackedBy[Them][ALL_PIECES] b = ~attackedBy[Them][ALL_PIECES]
| (nonPawnEnemies & attackedBy2[Us]); | (nonPawnEnemies & attackedBy2[Us]);
score += Hanging * popcount(weak & b); 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 // Bonus for restricting their piece moves
b = attackedBy[Them][ALL_PIECES] b = attackedBy[Them][ALL_PIECES]
& ~stronglyProtected & ~stronglyProtected
& attackedBy[Us][ALL_PIECES]; & attackedBy[Us][ALL_PIECES];
score += RestrictedPiece * popcount(b); score += RestrictedPiece * popcount(b);
// Bonus for enemy unopposed weak pawns // Protected or unattacked squares
if (pos.pieces(Us, ROOK, QUEEN)) safe = ~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES];
score += WeakUnopposedPawn * pe->weak_unopposed(Them);
// 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 // Find squares where our pawns can push on the next move
b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces(); b = shift<Up>(pos.pieces(Us, PAWN)) & ~pos.pieces();
@ -571,14 +562,8 @@ namespace {
b &= ~attackedBy[Them][PAWN] & safe; b &= ~attackedBy[Them][PAWN] & safe;
// Bonus for safe pawn threats on the next move // 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; 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 // Bonus for threats on the next moves against enemy queen
if (pos.count<QUEEN>(Them) == 1) if (pos.count<QUEEN>(Them) == 1)
@ -586,12 +571,12 @@ namespace {
Square s = pos.square<QUEEN>(Them); Square s = pos.square<QUEEN>(Them);
safe = mobilityArea[Us] & ~stronglyProtected; 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); score += KnightOnQueen * popcount(b & safe);
b = (attackedBy[Us][BISHOP] & pos.attacks_from<BISHOP>(s)) b = (attackedBy[Us][BISHOP] & attacks_bb<BISHOP>(s, pos.pieces()))
| (attackedBy[Us][ROOK ] & pos.attacks_from<ROOK >(s)); | (attackedBy[Us][ROOK ] & attacks_bb<ROOK >(s, pos.pieces()));
score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]); score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]);
} }
@ -608,18 +593,32 @@ namespace {
template<Tracing T> template<Color Us> template<Tracing T> template<Color Us>
Score Evaluation<T>::passed() const { Score Evaluation<T>::passed() const {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = ~Us;
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); constexpr Direction Up = pawn_push(Us);
constexpr Direction Down = -Up;
auto king_proximity = [&](Color c, Square s) { auto king_proximity = [&](Color c, Square s) {
return std::min(distance(pos.square<KING>(c), s), 5); 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; Score score = SCORE_ZERO;
b = pe->passed_pawns(Us); 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) while (b)
{ {
Square s = pop_lsb(&b); Square s = pop_lsb(&b);
@ -632,12 +631,12 @@ namespace {
if (r > RANK_3) if (r > RANK_3)
{ {
int w = (r-2) * (r-2) + 2; int w = 5 * r - 13;
Square blockSq = s + Up; Square blockSq = s + Up;
// Adjust bonus based on the king's proximity // Adjust bonus based on the king's proximity
bonus += make_score(0, ( king_proximity(Them, blockSq) * 5 bonus += make_score(0, ( (king_proximity(Them, blockSq) * 19) / 4
- king_proximity(Us, blockSq) * 2) * w); - king_proximity(Us, blockSq) * 2) * w);
// If blockSq is not the queening square then consider also a second push // If blockSq is not the queening square then consider also a second push
if (r != RANK_7) if (r != RANK_7)
@ -646,42 +645,31 @@ namespace {
// If the pawn is free to advance, then increase the bonus // If the pawn is free to advance, then increase the bonus
if (pos.empty(blockSq)) if (pos.empty(blockSq))
{ {
// If there is a rook or queen attacking/defending the pawn from behind, squaresToQueen = forward_file_bb(Us, s);
// consider all the squaresToQueen. Otherwise consider only the squares unsafeSquares = passed_pawn_span(Us, s);
// in the pawn's path attacked or occupied by the enemy.
defendedSquares = unsafeSquares = squaresToQueen = forward_file_bb(Us, s);
bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN); 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)) 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 // If there are no enemy attacks on passed pawn span, assign a big bonus.
// assign a smaller bonus if the block square isn't attacked. // Otherwise assign a smaller bonus if the path to queen is not attacked
int k = !unsafeSquares ? 20 : !(unsafeSquares & blockSq) ? 9 : 0; // 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. // Assign a larger bonus if the block square is defended
// Otherwise assign a smaller bonus if the block square is defended. if ((pos.pieces(Us) & bb) || (attackedBy[Us][ALL_PIECES] & blockSq))
if (defendedSquares == squaresToQueen) k += 5;
k += 6;
else if (defendedSquares & blockSq)
k += 4;
bonus += make_score(k * w, k * w); bonus += make_score(k * w, k * w);
} }
} // r > RANK_3 } // r > RANK_3
// Scale down bonus for candidate passers which need more than one score += bonus - PassedFile * edge_distance(file_of(s));
// 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)];
} }
if (T) if (T)
@ -704,8 +692,8 @@ namespace {
if (pos.non_pawn_material() < SpaceThreshold) if (pos.non_pawn_material() < SpaceThreshold)
return SCORE_ZERO; return SCORE_ZERO;
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = ~Us;
constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH); constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard SpaceMask = constexpr Bitboard SpaceMask =
Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB) Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB)
: CenterFiles & (Rank7BB | Rank6BB | Rank5BB); : CenterFiles & (Rank7BB | Rank6BB | Rank5BB);
@ -720,8 +708,8 @@ namespace {
behind |= shift<Down>(behind); behind |= shift<Down>(behind);
behind |= shift<Down+Down>(behind); behind |= shift<Down+Down>(behind);
int bonus = popcount(safe) + popcount(behind & safe); int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]);
int weight = pos.count<ALL_PIECES>(Us) - 1; int weight = pos.count<ALL_PIECES>(Us) - 3 + std::min(pe->blocked_count(), 9);
Score score = make_score(bonus * weight * weight / 16, 0); Score score = make_score(bonus * weight * weight / 16, 0);
if (T) if (T)
@ -736,7 +724,7 @@ namespace {
// known attacking/defending status of the players. // known attacking/defending status of the players.
template<Tracing T> 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)) int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
- distance<Rank>(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) bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide)
&& (pos.pieces(PAWN) & KingSide); && (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 // Compute the initiative bonus for the attacking side
int complexity = 9 * pe->passed_count() int complexity = 9 * pe->passed_count()
+ 11 * pos.count<PAWN>() + 12 * pos.count<PAWN>()
+ 9 * outflanking + 9 * outflanking
+ 18 * pawnsOnBothFlanks + 21 * pawnsOnBothFlanks
+ 49 * !pos.non_pawn_material() + 24 * infiltration
-103 ; + 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 Value mg = mg_value(score);
// the sign of the endgame value, and that we carefully cap the bonus so Value eg = eg_value(score);
// that the endgame score will never change sign after the bonus.
// 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)); int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg));
if (T) 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 scale is not already specific, scale down the endgame via general heuristics
if (sf == SCALE_FACTOR_NORMAL) if (sf == SCALE_FACTOR_NORMAL)
{ {
if ( pos.opposite_bishops() if (pos.opposite_bishops())
&& pos.non_pawn_material() == 2 * BishopValueMg) {
sf = 16 + 4 * pe->passed_count(); 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 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); return ScaleFactor(sf);
@ -815,7 +820,7 @@ namespace {
// Early exit if score is high // Early exit if score is high
Value v = (mg_value(score) + eg_value(score)) / 2; 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; return pos.side_to_move() == WHITE ? v : -v;
// Main evaluation begins here // Main evaluation begins here
@ -823,7 +828,8 @@ namespace {
initialize<WHITE>(); initialize<WHITE>();
initialize<BLACK>(); 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>() score += pieces<WHITE, KNIGHT>() - pieces<BLACK, KNIGHT>()
+ pieces<WHITE, BISHOP>() - pieces<BLACK, BISHOP>() + pieces<WHITE, BISHOP>() - pieces<BLACK, BISHOP>()
+ pieces<WHITE, ROOK >() - pieces<BLACK, ROOK >() + pieces<WHITE, ROOK >() - pieces<BLACK, ROOK >()
@ -831,12 +837,13 @@ namespace {
score += mobility[WHITE] - mobility[BLACK]; score += mobility[WHITE] - mobility[BLACK];
// More complex interactions that require fully populated attack bitboards
score += king< WHITE>() - king< BLACK>() score += king< WHITE>() - king< BLACK>()
+ threats<WHITE>() - threats<BLACK>() + threats<WHITE>() - threats<BLACK>()
+ passed< WHITE>() - passed< BLACK>() + passed< WHITE>() - passed< BLACK>()
+ space< WHITE>() - space< BLACK>(); + space< WHITE>() - space< BLACK>();
score += initiative(eg_value(score)); score += initiative(score);
// Interpolate between a middlegame and a (scaled by 'sf') endgame score // Interpolate between a middlegame and a (scaled by 'sf') endgame score
ScaleFactor sf = scale_factor(eg_value(score)); ScaleFactor sf = scale_factor(eg_value(score));
@ -855,8 +862,8 @@ namespace {
Trace::add(TOTAL, score); Trace::add(TOTAL, score);
} }
return (pos.side_to_move() == WHITE ? v : -v) // Side to move point of view // Side to move point of view
+ Eval::Tempo; return (pos.side_to_move() == WHITE ? v : -v) + Tempo;
} }
} // namespace } // namespace
@ -878,6 +885,9 @@ Value Eval::evaluate(const Position& pos) {
std::string Eval::trace(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)); std::memset(scores, 0, sizeof(scores));
pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -29,8 +29,6 @@ class Position;
namespace Eval { namespace Eval {
constexpr Value Tempo = Value(28); // Must be visible to search
std::string trace(const Position& pos); std::string trace(const Position& pos);
Value evaluate(const Position& pos); Value evaluate(const Position& pos);

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -21,12 +21,12 @@
#include <iostream> #include <iostream>
#include "bitboard.h" #include "bitboard.h"
#include "endgame.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "thread.h" #include "thread.h"
#include "tt.h" #include "tt.h"
#include "uci.h" #include "uci.h"
#include "endgame.h"
#include "syzygy/tbprobe.h" #include "syzygy/tbprobe.h"
namespace PSQT { namespace PSQT {
@ -38,13 +38,13 @@ int main(int argc, char* argv[]) {
std::cout << engine_info() << std::endl; std::cout << engine_info() << std::endl;
UCI::init(Options); UCI::init(Options);
Tune::init();
PSQT::init(); PSQT::init();
Bitboards::init(); Bitboards::init();
Position::init(); Position::init();
Bitbases::init(); Bitbases::init();
Search::init();
Endgames::init(); Endgames::init();
Threads.set(Options["Threads"]); Threads.set(size_t(Options["Threads"]));
Search::clear(); // After threads are up Search::clear(); // After threads are up
UCI::loop(argc, argv); UCI::loop(argc, argv);

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -84,7 +84,7 @@ namespace {
template<Color Us> template<Color Us>
int imbalance(const int pieceCount[][PIECE_TYPE_NB]) { int imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = ~Us;
int bonus = 0; int bonus = 0;
@ -129,7 +129,7 @@ Entry* probe(const Position& pos) {
Value npm_w = pos.non_pawn_material(WHITE); Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK); Value npm_b = pos.non_pawn_material(BLACK);
Value npm = clamp(npm_w + npm_b, EndgameLimit, MidgameLimit); Value npm = Utility::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
// Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME] // Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME]
e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit)); e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
@ -140,7 +140,7 @@ Entry* probe(const Position& pos) {
if ((e->evaluationFunction = Endgames::probe<Value>(key)) != nullptr) if ((e->evaluationFunction = Endgames::probe<Value>(key)) != nullptr)
return e; return e;
for (Color c = WHITE; c <= BLACK; ++c) for (Color c : { WHITE, BLACK })
if (is_KXK(pos, c)) if (is_KXK(pos, c))
{ {
e->evaluationFunction = &EvaluateKXK[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 // 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 // ones that refer to more than one material distribution. Note that in this
// case we don't return after setting the function. // 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)) if (is_KBPsK(pos, c))
e->scalingFunction[c] = &ScaleKBPsK[c]; e->scalingFunction[c] = &ScaleKBPsK[c];

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -48,6 +48,11 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#if defined(__linux__) && !defined(__ANDROID__)
#include <stdlib.h>
#include <sys/mman.h>
#endif
#include "misc.h" #include "misc.h"
#include "thread.h" #include "thread.h"
@ -103,6 +108,13 @@ public:
if (!fname.empty() && !l.file.is_open()) if (!fname.empty() && !l.file.is_open())
{ {
l.file.open(fname, ifstream::out); 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); cin.rdbuf(&l.in);
cout.rdbuf(&l.out); 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 /// 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 b) { ++hits[0]; if (b) ++hits[1]; }
void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); } 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) { std::ostream& operator<<(std::ostream& os, SyncCout sc) {
static Mutex m; static std::mutex m;
if (sc == IO_LOCK) if (sc == IO_LOCK)
m.lock(); m.lock();
@ -211,6 +294,130 @@ void prefetch(void* addr) {
#endif #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 { namespace WinProcGroup {
#ifndef _WIN32 #ifndef _WIN32

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -25,6 +25,7 @@
#include <cassert> #include <cassert>
#include <chrono> #include <chrono>
#include <functional> #include <functional>
#include <mutex>
#include <ostream> #include <ostream>
#include <string> #include <string>
#include <vector> #include <vector>
@ -33,8 +34,11 @@
#include "thread_win32_osx.h" #include "thread_win32_osx.h"
const std::string engine_info(bool to_uci = false); const std::string engine_info(bool to_uci = false);
const std::string compiler_info();
void prefetch(void* addr); void prefetch(void* addr);
void start_logger(const std::string& fname); void start_logger(const std::string& fname);
void* 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 b);
void dbg_hit_on(bool c, 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_cout std::cout << IO_LOCK
#define sync_endl std::endl << IO_UNLOCK #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 /// xorshift64star Pseudo-Random Number Generator
/// This class is based on original code written and dedicated /// This class is based on original code written and dedicated
@ -161,13 +173,13 @@ struct AsyncPRNG
AsyncPRNG(uint64_t seed) : prng(seed) { assert(seed); } AsyncPRNG(uint64_t seed) : prng(seed) { assert(seed); }
// [ASYNC] 乱数を一つ取り出す。 // [ASYNC] 乱数を一つ取り出す。
template<typename T> T rand() { template<typename T> T rand() {
std::unique_lock<Mutex> lk(mutex); std::unique_lock<std::mutex> lk(mutex);
return prng.rand<T>(); return prng.rand<T>();
} }
// [ASYNC] 0からn-1までの乱数を返す。(一様分布ではないが現実的にはこれで十分) // [ASYNC] 0からn-1までの乱数を返す。(一様分布ではないが現実的にはこれで十分)
uint64_t rand(uint64_t n) { uint64_t rand(uint64_t n) {
std::unique_lock<Mutex> lk(mutex); std::unique_lock<std::mutex> lk(mutex);
return prng.rand(n); return prng.rand(n);
} }
@ -175,7 +187,7 @@ struct AsyncPRNG
uint64_t get_seed() const { return prng.get_seed(); } uint64_t get_seed() const { return prng.get_seed(); }
protected: protected:
Mutex mutex; std::mutex mutex;
PRNG prng; PRNG prng;
}; };

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -40,7 +40,7 @@ namespace {
// Knight promotion is the only promotion that can give a direct check // Knight promotion is the only promotion that can give a direct check
// that's not already included in the queen promotion. // 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); *moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
else else
(void)ksq; // Silence a warning under MSVC (void)ksq; // Silence a warning under MSVC
@ -52,14 +52,14 @@ namespace {
template<Color Us, GenType Type> template<Color Us, GenType Type>
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) { 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;
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); 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 UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
const Square ksq = pos.square<KING>(Them);
Bitboard emptySquares; Bitboard emptySquares;
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
@ -84,10 +84,8 @@ namespace {
if (Type == QUIET_CHECKS) if (Type == QUIET_CHECKS)
{ {
Square ksq = pos.square<KING>(Them); b1 &= pawn_attacks_bb(Them, ksq);
b2 &= pawn_attacks_bb(Them, ksq);
b1 &= pos.attacks_from<PAWN>(ksq, Them);
b2 &= pos.attacks_from<PAWN>(ksq, Them);
// Add pawn pushes which give discovered check. This is possible only // Add pawn pushes which give discovered check. This is possible only
// if the pawn is not on the same file as the enemy king, because we // if the pawn is not on the same file as the enemy king, because we
@ -130,8 +128,6 @@ namespace {
Bitboard b2 = shift<UpLeft >(pawnsOn7) & enemies; Bitboard b2 = shift<UpLeft >(pawnsOn7) & enemies;
Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares; Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares;
Square ksq = pos.square<KING>(Them);
while (b1) while (b1)
moveList = make_promotions<Type, UpRight>(moveList, pop_lsb(&b1), ksq); moveList = make_promotions<Type, UpRight>(moveList, pop_lsb(&b1), ksq);
@ -170,7 +166,7 @@ namespace {
if (Type == EVASIONS && !(target & (pos.ep_square() - Up))) if (Type == EVASIONS && !(target & (pos.ep_square() - Up)))
return moveList; return moveList;
b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them); b1 = pawnsNotOn7 & pawn_attacks_bb(Them, pos.ep_square());
assert(b1); assert(b1);
@ -183,27 +179,26 @@ namespace {
} }
template<PieceType Pt, bool Checks> template<Color Us, PieceType Pt, bool Checks>
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us, ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
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) for (Square from = *pl; from != SQ_NONE; from = *++pl)
{ {
if (Checks) if (Checks)
{ {
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) 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; continue;
if (pos.blockers_for_king(~us) & from) if (pos.blockers_for_king(~Us) & from)
continue; continue;
} }
Bitboard b = pos.attacks_from<Pt>(from) & target; Bitboard b = attacks_bb<Pt>(from, pos.pieces()) & target;
if (Checks) if (Checks)
b &= pos.check_squares(Pt); b &= pos.check_squares(Pt);
@ -217,33 +212,49 @@ namespace {
template<Color Us, GenType Type> template<Color Us, GenType Type>
ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) { ExtMove* generate_all(const Position& pos, ExtMove* moveList) {
constexpr CastlingRight OO = Us | KING_SIDE;
constexpr CastlingRight OOO = Us | QUEEN_SIDE;
constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations 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_pawn_moves<Us, Type>(pos, moveList, target);
moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target); moveList = generate_moves<Us, KNIGHT, Checks>(pos, moveList, target);
moveList = generate_moves<BISHOP, Checks>(pos, moveList, Us, target); moveList = generate_moves<Us, BISHOP, Checks>(pos, moveList, target);
moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target); moveList = generate_moves<Us, ROOK, Checks>(pos, moveList, target);
moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target); moveList = generate_moves<Us, QUEEN, Checks>(pos, moveList, target);
if (Type != QUIET_CHECKS && Type != EVASIONS) if (Type != QUIET_CHECKS && Type != EVASIONS)
{ {
Square ksq = pos.square<KING>(Us); Square ksq = pos.square<KING>(Us);
Bitboard b = pos.attacks_from<KING>(ksq) & target; Bitboard b = attacks_bb<KING>(ksq) & target;
while (b) while (b)
*moveList++ = make_move(ksq, pop_lsb(&b)); *moveList++ = make_move(ksq, pop_lsb(&b));
if (Type != CAPTURES && pos.can_castle(CastlingRight(OO | OOO))) if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING))
{ for(CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } )
if (!pos.castling_impeded(OO) && pos.can_castle(OO)) if (!pos.castling_impeded(cr) && pos.can_castle(cr))
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OO)); *moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(cr));
if (!pos.castling_impeded(OOO) && pos.can_castle(OOO))
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OOO));
}
} }
return moveList; return moveList;
@ -261,17 +272,13 @@ namespace {
template<GenType Type> template<GenType Type>
ExtMove* generate(const Position& pos, ExtMove* moveList) { 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()); assert(!pos.checkers());
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Bitboard target = Type == CAPTURES ? pos.pieces(~us) return us == WHITE ? generate_all<WHITE, Type>(pos, moveList)
: Type == QUIETS ? ~pos.pieces() : generate_all<BLACK, Type>(pos, moveList);
: Type == NON_EVASIONS ? ~pos.pieces(us) : 0;
return us == WHITE ? generate_all<WHITE, Type>(pos, moveList, target)
: generate_all<BLACK, Type>(pos, moveList, target);
} }
// Explicit template instantiations // Explicit template instantiations
@ -288,27 +295,24 @@ ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
assert(!pos.checkers()); assert(!pos.checkers());
Color us = pos.side_to_move(); 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) while (dc)
{ {
Square from = pop_lsb(&dc); Square from = pop_lsb(&dc);
PieceType pt = type_of(pos.piece_on(from)); PieceType pt = type_of(pos.piece_on(from));
if (pt == PAWN) Bitboard b = attacks_bb(pt, from, pos.pieces()) & ~pos.pieces();
continue; // Will be generated together with direct checks
Bitboard b = pos.attacks_from(pt, from) & ~pos.pieces();
if (pt == KING) if (pt == KING)
b &= ~PseudoAttacks[QUEEN][pos.square<KING>(~us)]; b &= ~attacks_bb<QUEEN>(pos.square<KING>(~us));
while (b) while (b)
*moveList++ = make_move(from, pop_lsb(&b)); *moveList++ = make_move(from, pop_lsb(&b));
} }
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList, ~pos.pieces()) return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList)
: generate_all<BLACK, QUIET_CHECKS>(pos, moveList, ~pos.pieces()); : 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 // the king evasions in order to skip known illegal moves, which avoids any
// useless legality checks later on. // useless legality checks later on.
while (sliders) while (sliders)
{ sliderAttacks |= LineBB[ksq][pop_lsb(&sliders)] & ~pos.checkers();
Square checksq = pop_lsb(&sliders);
sliderAttacks |= LineBB[checksq][ksq] ^ checksq;
}
// Generate evasions for king, capture and non capture moves // 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) while (b)
*moveList++ = make_move(ksq, pop_lsb(&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 return moveList; // Double check, only a king move can save the day
// Generate blocking evasions or captures of the checking piece // Generate blocking evasions or captures of the checking piece
Square checksq = lsb(pos.checkers()); return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList)
Bitboard target = between_bb(checksq, ksq) | checksq; : generate_all<BLACK, EVASIONS>(pos, moveList);
return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList, target)
: generate_all<BLACK, EVASIONS>(pos, moveList, target);
} }

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -56,45 +56,39 @@ namespace {
/// ordering is at the current node. /// ordering is at the current node.
/// MovePicker constructor for the main search /// MovePicker constructor for the main search
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, 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) const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, Move* killers, int pl)
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), : pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch),
refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) { 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; stage = (pos.checkers() ? EVASION_TT : MAIN_TT) +
ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; !(ttm && pos.pseudo_legal(ttm));
stage += (ttMove == MOVE_NONE);
} }
/// MovePicker constructor for quiescence search /// MovePicker constructor for quiescence search
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs) 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; stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) +
ttMove = ttm !(ttm && (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare)
&& (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) && pos.pseudo_legal(ttm));
&& pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
stage += (ttMove == MOVE_NONE);
} }
/// MovePicker constructor for ProbCut: we generate captures with SEE greater /// MovePicker constructor for ProbCut: we generate captures with SEE greater
/// than or equal to the given threshold. /// than or equal to the given threshold.
MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) 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()); assert(!pos.checkers());
stage = PROBCUT_TT; stage = PROBCUT_TT + !(ttm && pos.capture(ttm)
ttMove = ttm && pos.pseudo_legal(ttm)
&& pos.capture(ttm) && pos.see_ge(ttm, threshold));
&& pos.pseudo_legal(ttm)
&& pos.see_ge(ttm, threshold) ? ttm : MOVE_NONE;
stage += (ttMove == MOVE_NONE);
} }
/// MovePicker::score() assigns a numerical value to each move in a list, used /// MovePicker::score() assigns a numerical value to each move in a list, used
@ -107,15 +101,16 @@ void MovePicker::score() {
for (auto& m : *this) for (auto& m : *this)
if (Type == CAPTURES) if (Type == CAPTURES)
m.value = PieceValue[MG][pos.piece_on(to_sq(m))] 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)))] / 8; + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))];
else if (Type == QUIETS) else if (Type == QUIETS)
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
+ (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
+ (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)] + 2 * (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
+ (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)] + 2 * (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]
+ (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)] / 2; + (*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 else // Type == EVASIONS
{ {
@ -174,7 +169,7 @@ top:
case GOOD_CAPTURE: case GOOD_CAPTURE:
if (select<Best>([&](){ 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 // Move losing capture to endBadCaptures to be tried later
true : (*endBadCaptures++ = *cur, false); })) true : (*endBadCaptures++ = *cur, false); }))
return *(cur - 1); return *(cur - 1);
@ -200,11 +195,15 @@ top:
/* fallthrough */ /* fallthrough */
case QUIET_INIT: case QUIET_INIT:
cur = endBadCaptures; if (!skipQuiets)
endMoves = generate<QUIETS>(pos, cur); {
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; ++stage;
/* fallthrough */ /* fallthrough */

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -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 /// In stats table, D=0 means that the template parameter is not used
enum StatsParams { NOT_USED = 0 }; enum StatsParams { NOT_USED = 0 };
enum StatsType { NoCaptures, Captures };
/// ButterflyHistory records how often quiet moves have been successful or /// ButterflyHistory records how often quiet moves have been successful or
/// unsuccessful during the current search, and is used for reduction and move /// 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 /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
typedef Stats<int16_t, 10692, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory; typedef Stats<int16_t, 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 /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
/// move, see www.chessprogramming.org/Countermove_Heuristic /// move, see www.chessprogramming.org/Countermove_Heuristic
typedef Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB> CounterMoveHistory; typedef Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB> CounterMoveHistory;
@ -123,10 +129,12 @@ public:
const PieceToHistory**, const PieceToHistory**,
Square); Square);
MovePicker(const Position&, Move, Depth, const ButterflyHistory*, MovePicker(const Position&, Move, Depth, const ButterflyHistory*,
const LowPlyHistory*,
const CapturePieceToHistory*, const CapturePieceToHistory*,
const PieceToHistory**, const PieceToHistory**,
Move, Move,
Move*); Move*,
int);
Move next_move(bool skipQuiets = false); Move next_move(bool skipQuiets = false);
private: private:
@ -137,6 +145,7 @@ private:
const Position& pos; const Position& pos;
const ButterflyHistory* mainHistory; const ButterflyHistory* mainHistory;
const LowPlyHistory* lowPlyHistory;
const CapturePieceToHistory* captureHistory; const CapturePieceToHistory* captureHistory;
const PieceToHistory** continuationHistory; const PieceToHistory** continuationHistory;
Move ttMove; Move ttMove;
@ -145,6 +154,7 @@ private:
Square recaptureSquare; Square recaptureSquare;
Value threshold; Value threshold;
Depth depth; Depth depth;
int ply;
ExtMove moves[MAX_MOVES]; ExtMove moves[MAX_MOVES];
}; };

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -32,9 +32,13 @@ namespace {
#define S(mg, eg) make_score(mg, eg) #define S(mg, eg) make_score(mg, eg)
// Pawn penalties // Pawn penalties
constexpr Score Backward = S( 9, 24); constexpr Score Backward = S( 9, 24);
constexpr Score Doubled = S(11, 56); constexpr Score Doubled = S(11, 56);
constexpr Score Isolated = S( 5, 15); 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 // Connected pawn bonus
constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; 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]. // 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 // 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] = { constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = {
{ V( 89), V(107), V(123), V(93), V(57), V( 45), V( 51) }, { V( 85), V(-289), V(-166), V(97), V(50), V( 45), V( 50) },
{ V( 44), V(-18), V(123), V(46), V(39), V( -7), V( 23) }, { V( 46), V( -25), V( 122), V(45), V(37), V(-10), V( 20) },
{ V( 4), V( 52), V(162), V(37), V( 7), V(-14), V( -2) }, { V( -6), V( 51), V( 168), V(34), V(-2), V(-22), V(-14) },
{ V(-10), V(-14), V( 90), V(15), V( 2), V( -7), V(-16) } { V(-15), V( -11), V( 101), V( 4), V(11), V(-15), V(-29) }
}; };
#undef S #undef S
@ -64,81 +69,98 @@ namespace {
template<Color Us> template<Color Us>
Score evaluate(const Position& pos, Pawns::Entry* e) { Score evaluate(const Position& pos, Pawns::Entry* e) {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Color Them = ~Us;
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); constexpr Direction Up = pawn_push(Us);
Bitboard b, neighbours, stoppers, doubled, support, phalanx; Bitboard neighbours, stoppers, support, phalanx, opposed;
Bitboard lever, leverPush; Bitboard lever, leverPush, blocked;
Square s; Square s;
bool opposed, backward; bool backward, passed, doubled;
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
const Square* pl = pos.squares<PAWN>(Us); const Square* pl = pos.squares<PAWN>(Us);
Bitboard ourPawns = pos.pieces( Us, PAWN); Bitboard ourPawns = pos.pieces( Us, PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN);
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = e->weakUnopposed[Us] = 0; Bitboard doubleAttackThem = pawn_double_attacks_bb<Them>(theirPawns);
e->kingSquares[Us] = SQ_NONE;
e->pawnAttacks[Us] = pawn_attacks_bb<Us>(ourPawns); 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 // Loop through all pawns of the current color and score each pawn
while ((s = *pl++) != SQ_NONE) while ((s = *pl++) != SQ_NONE)
{ {
assert(pos.piece_on(s) == make_piece(Us, PAWN)); assert(pos.piece_on(s) == make_piece(Us, PAWN));
File f = file_of(s);
Rank r = relative_rank(Us, s); Rank r = relative_rank(Us, s);
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
// Flag the pawn // Flag the pawn
opposed = theirPawns & forward_file_bb(Us, s); opposed = theirPawns & forward_file_bb(Us, s);
blocked = theirPawns & (s + Up);
stoppers = theirPawns & passed_pawn_span(Us, s); stoppers = theirPawns & passed_pawn_span(Us, s);
lever = theirPawns & PawnAttacks[Us][s]; lever = theirPawns & pawn_attacks_bb(Us, s);
leverPush = theirPawns & PawnAttacks[Us][s + Up]; leverPush = theirPawns & pawn_attacks_bb(Us, s + Up);
doubled = ourPawns & (s - Up); doubled = ourPawns & (s - Up);
neighbours = ourPawns & adjacent_files_bb(f); neighbours = ourPawns & adjacent_files_bb(s);
phalanx = neighbours & rank_bb(s); phalanx = neighbours & rank_bb(s);
support = neighbours & rank_bb(s - Up); support = neighbours & rank_bb(s - Up);
// A pawn is backward when it is behind all pawns of the same color // A pawn is backward when it is behind all pawns of the same color on
// on the adjacent files and cannot be safely advanced. // the adjacent files and cannot safely advance.
backward = !(ourPawns & pawn_attack_span(Them, s + Up)) backward = !(neighbours & forward_ranks_bb(Them, s + Up))
&& (stoppers & (leverPush | (s + Up))); && (leverPush | blocked);
// Passed pawns will be properly scored in evaluation because we need // Compute additional span if pawn is not backward nor blocked
// full attack info to evaluate them. Include also not passed pawns if (!backward && !blocked)
// which could become passed after one or two pawn pushes when are e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
// not attacked more times than defended.
if ( !(stoppers ^ lever ^ leverPush) // A pawn is passed if one of the three following conditions is true:
&& (support || !more_than_one(lever)) // (a) there is no stoppers except some levers
&& popcount(phalanx) >= popcount(leverPush)) // (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; 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 // Score this pawn
if (support | phalanx) if (support | phalanx)
{ {
int v = Connected[r] * (phalanx ? 3 : 2) / (opposed ? 2 : 1) int v = Connected[r] * (4 + 2 * bool(phalanx) - 2 * bool(opposed) - bool(blocked)) / 2
+ 17 * popcount(support); + 21 * popcount(support);
score += make_score(v, v * (r - 2) / 4); score += make_score(v, v * (r - 2) / 4);
} }
else if (!neighbours) 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) else if (backward)
score -= Backward, e->weakUnopposed[Us] += !opposed; score -= Backward
+ WeakUnopposed * !opposed;
if (doubled && !support) if (!support)
score -= Doubled; score -= Doubled * doubled
+ WeakLever * more_than_one(lever);
} }
return score; return score;
@ -162,6 +184,7 @@ Entry* probe(const Position& pos) {
return e; return e;
e->key = key; e->key = key;
e->blockedCount = 0;
e->scores[WHITE] = evaluate<WHITE>(pos, e); e->scores[WHITE] = evaluate<WHITE>(pos, e);
e->scores[BLACK] = evaluate<BLACK>(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. /// penalty for a king, looking at the king file and the two closest files.
template<Color Us> 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 Color Them = ~Us;
constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
constexpr Bitboard BlockSquares = (Rank1BB | Rank2BB | Rank7BB | Rank8BB)
& (FileABB | FileHBB);
Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq); 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); Bitboard theirPawns = b & pos.pieces(Them);
Value bonus[] = { (shift<Down>(theirPawns) & BlockSquares & ksq) ? Value(374) : Value(5), Score bonus = make_score(5, 5);
VALUE_ZERO };
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) for (File f = File(center - 1); f <= File(center + 1); ++f)
{ {
b = ourPawns & file_bb(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); 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); int d = edge_distance(f);
bonus[MG] += ShelterStrength[d][ourRank]; bonus += make_score(ShelterStrength[d][ourRank], 0);
if (ourRank && (ourRank == theirRank - 1)) if (ourRank && (ourRank == theirRank - 1))
bonus[MG] -= 82 * (theirRank == RANK_3), bonus[EG] -= 82 * (theirRank == RANK_3); bonus -= BlockedStorm[theirRank];
else else
bonus[MG] -= UnblockedStorm[d][theirRank]; bonus -= make_score(UnblockedStorm[d][theirRank], 0);
} }
if (bonus[MG] > mg_value(shelter)) return bonus;
shelter = make_score(bonus[MG], bonus[EG]);
} }
@ -219,27 +237,28 @@ Score Entry::do_king_safety(const Position& pos) {
Square ksq = pos.square<KING>(Us); Square ksq = pos.square<KING>(Us);
kingSquares[Us] = ksq; kingSquares[Us] = ksq;
castlingRights[Us] = pos.castling_rights(Us); 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); 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; minPawnDist = 1;
else while (pawns) else while (pawns)
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns))); minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns)));
Score shelter = make_score(-VALUE_INFINITE, VALUE_ZERO); return shelter - make_score(0, 16 * minPawnDist);
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);
} }
// Explicit template instantiation // Explicit template instantiation

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -37,8 +37,8 @@ struct Entry {
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[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 passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); }
int blocked_count() const { return blockedCount; }
template<Color Us> template<Color Us>
Score king_safety(const Position& pos) { Score king_safety(const Position& pos) {
@ -50,7 +50,7 @@ struct Entry {
Score do_king_safety(const Position& pos); Score do_king_safety(const Position& pos);
template<Color Us> template<Color Us>
void evaluate_shelter(const Position& pos, Square ksq, Score& shelter); Score evaluate_shelter(const Position& pos, Square ksq);
Key key; Key key;
Score scores[COLOR_NB]; Score scores[COLOR_NB];
@ -59,12 +59,11 @@ struct Entry {
Bitboard pawnAttacksSpan[COLOR_NB]; Bitboard pawnAttacksSpan[COLOR_NB];
Square kingSquares[COLOR_NB]; Square kingSquares[COLOR_NB];
Score kingSafety[COLOR_NB]; Score kingSafety[COLOR_NB];
int weakUnopposed[COLOR_NB];
int castlingRights[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); Entry* probe(const Position& pos);

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -50,41 +50,6 @@ const string PieceToChar(" PNBRQK pnbrqk");
constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, 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 }; 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 } // namespace
@ -174,7 +139,7 @@ void Position::init() {
for (Piece pc : Pieces) for (Piece pc : Pieces)
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2) 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); Move move = make_move(s1, s2);
Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side; 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) { void Position::set_castling_right(Color c, Square rfrom) {
Square kfrom = square<KING>(c); Square kfrom = square<KING>(c);
CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE; CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE: QUEEN_SIDE);
CastlingRight cr = (c | cs);
st->castlingRights |= cr; st->castlingRights |= cr;
castlingRightsMask[kfrom] |= cr; castlingRightsMask[kfrom] |= cr;
castlingRightsMask[rfrom] |= cr; castlingRightsMask[rfrom] |= cr;
castlingRookSquare[cr] = rfrom; castlingRookSquare[cr] = rfrom;
Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1); Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1); Square rto = relative_square(c, cr & KING_SIDE ? SQ_F1 : SQ_D1);
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto) 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); Square ksq = square<KING>(~sideToMove);
si->checkSquares[PAWN] = attacks_from<PAWN>(ksq, ~sideToMove); si->checkSquares[PAWN] = pawn_attacks_bb(~sideToMove, ksq);
si->checkSquares[KNIGHT] = attacks_from<KNIGHT>(ksq); si->checkSquares[KNIGHT] = attacks_bb<KNIGHT>(ksq);
si->checkSquares[BISHOP] = attacks_from<BISHOP>(ksq); si->checkSquares[BISHOP] = attacks_bb<BISHOP>(ksq, pieces());
si->checkSquares[ROOK] = attacks_from<ROOK>(ksq); si->checkSquares[ROOK] = attacks_bb<ROOK>(ksq, pieces());
si->checkSquares[QUEEN] = si->checkSquares[BISHOP] | si->checkSquares[ROOK]; si->checkSquares[QUEEN] = si->checkSquares[BISHOP] | si->checkSquares[ROOK];
si->checkSquares[KING] = 0; si->checkSquares[KING] = 0;
} }
@ -409,7 +373,7 @@ void Position::set_state(StateInfo* si) const {
if (type_of(pc) == PAWN) if (type_of(pc) == PAWN)
si->pawnKey ^= Zobrist::psq[pc][s]; 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]; 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) { Position& Position::set(const string& code, Color c, StateInfo* si) {
assert(code.length() > 0 && code.length() < 8);
assert(code[0] == 'K'); assert(code[0] == 'K');
string sides[] = { code.substr(code.find('K', 1)), // Weak 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); 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; pinners = 0;
// Snipers are sliders that attack 's' when a piece and other snipers are removed // Snipers are sliders that attack 's' when a piece and other snipers are removed
Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK)) Bitboard snipers = ( (attacks_bb< ROOK>(s) & pieces(QUEEN, ROOK))
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders; | (attacks_bb<BISHOP>(s) & pieces(QUEEN, BISHOP))) & sliders;
Bitboard occupancy = pieces() & ~snipers; Bitboard occupancy = pieces() ^ snipers;
while (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 { Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
return (attacks_from<PAWN>(s, BLACK) & pieces(WHITE, PAWN)) return (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN))
| (attacks_from<PAWN>(s, WHITE) & pieces(BLACK, PAWN)) | (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN))
| (attacks_from<KNIGHT>(s) & pieces(KNIGHT)) | (attacks_bb<KNIGHT>(s) & pieces(KNIGHT))
| (attacks_bb< ROOK>(s, occupied) & pieces( ROOK, QUEEN)) | (attacks_bb< ROOK>(s, occupied) & pieces( ROOK, QUEEN))
| (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, 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) if ((Rank8BB | Rank1BB) & to)
return false; 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 + pawn_push(us) == to) && empty(to)) // Not a single push
&& !( (from + 2 * pawn_push(us) == to) // Not a double 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)
&& empty(to - pawn_push(us)))) && empty(to - pawn_push(us))))
return false; return false;
} }
else if (!(attacks_from(type_of(pc), from) & to)) else if (!(attacks_bb(type_of(pc), from, pieces()) & to))
return false; return false;
// Evasions generator already takes care to avoid some kind of illegal moves // 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); Square to = to_sq(m);
// Is there a direct check? // 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; return true;
// Is there a discovered check? // Is there a discovered check?
if ( (st->blockersForKing[~sideToMove] & from) if ( (blockers_for_king(~sideToMove) & from)
&& !aligned(from, to, square<KING>(~sideToMove))) && !aligned(from, to, square<KING>(~sideToMove)))
return true; return true;
@ -722,11 +688,11 @@ bool Position::gives_check(Move m) const {
case CASTLING: case CASTLING:
{ {
Square kfrom = from; 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 kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1);
Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1); 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)); && (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
} }
default: default:
@ -821,7 +787,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
piece_no1 = piece_no_of(capsq); piece_no1 = piece_no_of(capsq);
#endif // defined(EVAL_NNUE) #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) #if defined(EVAL_NNUE)
evalList.piece_no_list_board[capsq] = PIECE_NUMBER_NB; evalList.piece_no_list_board[capsq] = PIECE_NUMBER_NB;
#endif // defined(EVAL_NNUE) #endif // defined(EVAL_NNUE)
@ -843,7 +809,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
} }
// Update board and piece lists // 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 // Update material hash key and prefetch access to materialTable
k ^= Zobrist::psq[captured][capsq]; 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); piece_no0 = piece_no_of(from);
#endif // defined(EVAL_NNUE) #endif // defined(EVAL_NNUE)
move_piece(pc, from, to); move_piece(from, to);
#if defined(EVAL_NNUE) #if defined(EVAL_NNUE)
dp.pieceNo[0] = piece_no0; 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 // Set en-passant square if the moved pawn can be captured
if ( (int(to) ^ int(from)) == 16 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); st->epSquare = to - pawn_push(us);
k ^= Zobrist::enpassant[file_of(st->epSquare)]; 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(relative_rank(us, to) == RANK_8);
assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN); assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
remove_piece(pc, to); remove_piece(to);
put_piece(promotion, to); put_piece(promotion, to);
#if defined(EVAL_NNUE) #if defined(EVAL_NNUE)
@ -944,7 +913,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
st->nonPawnMaterial[us] += PieceValue[MG][promotion]; 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]; st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
// Reset rule 50 draw counter // Reset rule 50 draw counter
@ -973,7 +942,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
if (end >= 4) if (end >= 4)
{ {
StateInfo* stp = st->previous->previous; 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; stp = stp->previous->previous;
if (stp->key == st->key) 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) == promotion_type(m));
assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN); assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN);
remove_piece(pc, to); remove_piece(to);
pc = make_piece(us, PAWN); pc = make_piece(us, PAWN);
put_piece(pc, to); put_piece(pc, to);
@ -1034,7 +1003,7 @@ void Position::undo_move(Move m) {
else 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) #if defined(EVAL_NNUE)
PieceNumber piece_no0 = st->dirtyPiece.pieceNo[0]; 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) #endif // defined(EVAL_NNUE)
// Remove both pieces first since squares could overlap in Chess960 // Remove both pieces first since squares could overlap in Chess960
remove_piece(make_piece(us, KING), Do ? from : to); remove_piece(Do ? from : to);
remove_piece(make_piece(us, ROOK), Do ? rfrom : rto); remove_piece(Do ? rfrom : rto);
board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us 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, KING), Do ? to : from);
put_piece(make_piece(us, ROOK), Do ? rto : rfrom); 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. /// the side to move without executing any move on the board.
void Position::do_null_move(StateInfo& newSt) { 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) if (type_of(m) != NORMAL)
return VALUE_ZERO >= threshold; return VALUE_ZERO >= threshold;
Bitboard stmAttackers;
Square from = from_sq(m), to = to_sq(m); 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 int swap = PieceValue[MG][piece_on(to)] - threshold;
// we can hope for. if (swap < 0)
balance = PieceValue[MG][piece_on(to)] - threshold;
if (balance < VALUE_ZERO)
return false; return false;
// Now assume the worst possible result: that the opponent can swap = PieceValue[MG][piece_on(from)] - swap;
// capture our piece for free. if (swap <= 0)
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)
return true; 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 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) 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 // 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) if (st->pinners[~stm] & occupied)
stmAttackers &= ~st->blockersForKing[stm]; stmAttackers &= ~st->blockersForKing[stm];
// If stm has no more attackers then give up: stm loses
if (!stmAttackers) if (!stmAttackers)
break; break;
res ^= 1;
// Locate and remove the next least valuable attacker, and add to // Locate and remove the next least valuable attacker, and add to
// the bitboard 'attackers' the possibly X-ray attackers behind it. // the bitboard 'attackers' any X-ray attackers behind it.
nextVictim = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers); if ((bb = stmAttackers & pieces(PAWN)))
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)
{ {
if (nextVictim == KING && (attackers & pieces(stm))) if ((swap = PawnValueMg - swap) < res)
stm = ~stm; break;
break;
}
assert(nextVictim != KING);
}
return us != stm; // We break the above loop when stm loses
}
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 /// Position::is_draw() tests whether the position is drawn by 50-move rule
/// or by repetition. It does not detect stalemates. /// 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 // Return a draw score if a position repeats once earlier but strictly
// after the root, or repeats twice before or at the root. // after the root, or repeats twice before or at the root.
if (st->repetition && st->repetition < ply) return st->repetition && st->repetition < ply;
return true;
return false;
} }
@ -1356,10 +1341,10 @@ bool Position::has_game_cycle(int ply) const {
if (ply > i) if (ply > i)
return true; return true;
// For nodes before or at the root, check that the move is a repetition one // For nodes before or at the root, check that the move is a
// rather than a move to the current position. // repetition rather than a move to the current position.
// In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in the same // In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in
// location, so we have to select which square to check. // the same location, so we have to select which square to check.
if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move()) if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move())
continue; continue;
@ -1463,15 +1448,15 @@ bool Position::pos_is_ok() const {
assert(0 && "pos_is_ok: Index"); assert(0 && "pos_is_ok: Index");
} }
for (Color c = WHITE; c <= BLACK; ++c) for (Color c : { WHITE, BLACK })
for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1)) for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE})
{ {
if (!can_castle(c | s)) if (!can_castle(cr))
continue; continue;
if ( piece_on(castlingRookSquare[c | s]) != make_piece(c, ROOK) if ( piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK)
|| castlingRightsMask[castlingRookSquare[c | s]] != (c | s) || castlingRightsMask[castlingRookSquare[cr]] != cr
|| (castlingRightsMask[square<KING>(c)] & (c | s)) != (c | s)) || (castlingRightsMask[square<KING>(c)] & cr) != cr)
assert(0 && "pos_is_ok: Castling"); assert(0 && "pos_is_ok: Castling");
} }

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -51,7 +51,6 @@ struct StateInfo {
Square epSquare; Square epSquare;
// Not copied when making a move (will be recomputed anyhow) // Not copied when making a move (will be recomputed anyhow)
int repetition;
Key key; Key key;
Bitboard checkersBB; Bitboard checkersBB;
Piece capturedPiece; Piece capturedPiece;
@ -59,6 +58,7 @@ struct StateInfo {
Bitboard blockersForKing[COLOR_NB]; Bitboard blockersForKing[COLOR_NB];
Bitboard pinners[COLOR_NB]; Bitboard pinners[COLOR_NB];
Bitboard checkSquares[PIECE_TYPE_NB]; Bitboard checkSquares[PIECE_TYPE_NB];
int repetition;
#if defined(EVAL_NNUE) #if defined(EVAL_NNUE)
Eval::NNUE::Accumulator accumulator; Eval::NNUE::Accumulator accumulator;
@ -98,7 +98,6 @@ public:
const std::string fen() const; const std::string fen() const;
// Position representation // Position representation
Bitboard pieces() const;
Bitboard pieces(PieceType pt) const; Bitboard pieces(PieceType pt) const;
Bitboard pieces(PieceType pt1, PieceType pt2) const; Bitboard pieces(PieceType pt1, PieceType pt2) const;
Bitboard pieces(Color c) const; Bitboard pieces(Color c) const;
@ -114,22 +113,20 @@ public:
bool is_on_semiopen_file(Color c, Square s) const; bool is_on_semiopen_file(Color c, Square s) const;
// Castling // Castling
int castling_rights(Color c) const; CastlingRights castling_rights(Color c) const;
bool can_castle(CastlingRight cr) const; bool can_castle(CastlingRights cr) const;
bool castling_impeded(CastlingRight cr) const; bool castling_impeded(CastlingRights cr) const;
Square castling_rook_square(CastlingRight cr) const; Square castling_rook_square(CastlingRights cr) const;
// Checking // Checking
Bitboard checkers() const; Bitboard checkers() const;
Bitboard blockers_for_king(Color c) const; Bitboard blockers_for_king(Color c) const;
Bitboard check_squares(PieceType pt) const; Bitboard check_squares(PieceType pt) const;
bool is_discovery_check_on_king(Color c, Move m) const;
// Attacks to/from a given square // Attacks to/from a given square
Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s) const;
Bitboard attackers_to(Square s, Bitboard occupied) 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; Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const;
// Properties of moves // Properties of moves
@ -219,8 +216,8 @@ private:
// Other helpers // Other helpers
void put_piece(Piece pc, Square s); void put_piece(Piece pc, Square s);
void remove_piece(Piece pc, Square s); void remove_piece(Square s);
void move_piece(Piece pc, Square from, Square to); void move_piece(Square from, Square to);
template<bool Do> template<bool Do>
void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto);
@ -262,28 +259,25 @@ inline Color Position::side_to_move() const {
return sideToMove; return sideToMove;
} }
inline bool Position::empty(Square s) const {
return board[s] == NO_PIECE;
}
inline Piece Position::piece_on(Square s) const { inline Piece Position::piece_on(Square s) const {
assert(is_ok(s));
return board[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 { inline Piece Position::moved_piece(Move m) const {
return board[from_sq(m)]; return piece_on(from_sq(m));
} }
inline Bitboard Position::pieces() const { inline Bitboard Position::pieces(PieceType pt = ALL_PIECES) const {
return byTypeBB[ALL_PIECES];
}
inline Bitboard Position::pieces(PieceType pt) const {
return byTypeBB[pt]; return byTypeBB[pt];
} }
inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const { 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 { 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 { 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 { 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 { 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 { 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 { 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 { template<PieceType Pt> inline Square Position::square(Color c) const {
assert(pieceCount[make_piece(c, Pt)] == 1); 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 { 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)); 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; return st->castlingRights & cr;
} }
inline int Position::castling_rights(Color c) const { inline CastlingRights Position::castling_rights(Color c) const {
return st->castlingRights & (c == WHITE ? WHITE_CASTLING : BLACK_CASTLING); return c & CastlingRights(st->castlingRights);
} }
inline bool Position::castling_impeded(CastlingRight cr) const { inline bool Position::castling_impeded(CastlingRights cr) const {
return byTypeBB[ALL_PIECES] & castlingPath[cr]; 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]; 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 { 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 { inline Bitboard Position::checkers() const {
@ -372,6 +353,10 @@ inline Bitboard Position::check_squares(PieceType pt) const {
return st->checkSquares[pt]; return st->checkSquares[pt];
} }
inline bool Position::is_discovery_check_on_king(Color c, Move m) const {
return st->blockersForKing[c] & from_sq(m);
}
inline bool Position::pawn_passed(Color c, Square s) const { inline bool Position::pawn_passed(Color c, Square s) const {
return !(pieces(~c, PAWN) & passed_pawn_span(c, s)); 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 { 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 { inline int Position::game_ply() const {
@ -418,8 +403,8 @@ inline int Position::rule50_count() const {
} }
inline bool Position::opposite_bishops() const { inline bool Position::opposite_bishops() const {
return pieceCount[W_BISHOP] == 1 return count<BISHOP>(WHITE) == 1
&& pieceCount[B_BISHOP] == 1 && count<BISHOP>(BLACK) == 1
&& opposite_colors(square<BISHOP>(WHITE), square<BISHOP>(BLACK)); && 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) { inline void Position::put_piece(Piece pc, Square s) {
board[s] = pc; board[s] = pc;
byTypeBB[ALL_PIECES] |= s; byTypeBB[ALL_PIECES] |= byTypeBB[type_of(pc)] |= s;
byTypeBB[type_of(pc)] |= s;
byColorBB[color_of(pc)] |= s; byColorBB[color_of(pc)] |= s;
index[s] = pieceCount[pc]++; index[s] = pieceCount[pc]++;
pieceList[pc][index[s]] = s; pieceList[pc][index[s]] = s;
@ -458,12 +442,13 @@ inline void Position::put_piece(Piece pc, Square s) {
psq += PSQT::psq[pc][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 // 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 // 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[] // the list and not in its original place, it means index[] and pieceList[]
// are not invariant to a do_move() + undo_move() sequence. // are not invariant to a do_move() + undo_move() sequence.
Piece pc = board[s];
byTypeBB[ALL_PIECES] ^= s; byTypeBB[ALL_PIECES] ^= s;
byTypeBB[type_of(pc)] ^= s; byTypeBB[type_of(pc)] ^= s;
byColorBB[color_of(pc)] ^= s; byColorBB[color_of(pc)] ^= s;
@ -476,11 +461,12 @@ inline void Position::remove_piece(Piece pc, Square s) {
psq -= PSQT::psq[pc][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[] // index[from] is not updated and becomes stale. This works as long as index[]
// is accessed just by known occupied squares. // 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[ALL_PIECES] ^= fromTo;
byTypeBB[type_of(pc)] ^= fromTo; byTypeBB[type_of(pc)] ^= fromTo;
byColorBB[color_of(pc)] ^= fromTo; byColorBB[color_of(pc)] ^= fromTo;

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -21,11 +21,7 @@
#include <algorithm> #include <algorithm>
#include "types.h" #include "types.h"
#include "bitboard.h"
Value PieceValue[PHASE_NB][PIECE_NB] = {
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg }
};
namespace PSQT { namespace PSQT {
@ -39,34 +35,34 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
{ }, { },
{ }, { },
{ // Knight { // Knight
{ S(-169,-105), S(-96,-74), S(-80,-46), S(-79,-18) }, { S(-175, -96), S(-92,-65), S(-74,-49), S(-73,-21) },
{ S( -79, -70), S(-39,-56), S(-24,-15), S( -9, 6) }, { S( -77, -67), S(-41,-54), S(-27,-18), S(-15, 8) },
{ S( -64, -38), S(-20,-33), S( 4, -5), S( 19, 27) }, { S( -61, -40), S(-17,-27), S( 6, -8), S( 12, 29) },
{ S( -28, -36), S( 5, 0), S( 41, 13), S( 47, 34) }, { S( -35, -35), S( 8, -2), S( 40, 13), S( 49, 28) },
{ S( -29, -41), S( 13,-20), S( 42, 4), S( 52, 35) }, { S( -34, -45), S( 13,-16), S( 44, 9), S( 51, 39) },
{ S( -11, -51), S( 28,-38), S( 63,-17), S( 55, 19) }, { S( -9, -51), S( 22,-44), S( 58,-16), S( 53, 17) },
{ S( -67, -64), S(-21,-45), S( 6,-37), S( 37, 16) }, { S( -67, -69), S(-27,-50), S( 4,-51), S( 37, 12) },
{ S(-200, -98), S(-80,-89), S(-53,-53), S(-32,-16) } { S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) }
}, },
{ // Bishop { // Bishop
{ S(-44,-63), S( -4,-30), S(-11,-35), S(-28, -8) }, { S(-53,-57), S( -5,-30), S( -8,-37), S(-23,-12) },
{ S(-18,-38), S( 7,-13), S( 14,-14), S( 3, 0) }, { S(-15,-37), S( 8,-13), S( 19,-17), S( 4, 1) },
{ S( -8,-18), S( 24, 0), S( -3, -7), S( 15, 13) }, { S( -7,-16), S( 21, -1), S( -5, -2), S( 17, 10) },
{ S( 1,-26), S( 8, -3), S( 26, 1), S( 37, 16) }, { S( -5,-20), S( 11, -6), S( 25, 0), S( 39, 17) },
{ S( -7,-24), S( 30, -6), S( 23,-10), S( 28, 17) }, { S(-12,-17), S( 29, -1), S( 22,-14), S( 31, 15) },
{ S(-17,-26), S( 4, 2), S( -1, 1), S( 8, 16) }, { S(-16,-30), S( 6, 6), S( 1, 4), S( 11, 6) },
{ S(-21,-34), S(-19,-18), S( 10, -7), S( -6, 9) }, { S(-17,-31), S(-14,-20), S( 5, -1), S( 0, 1) },
{ S(-48,-51), S( -3,-40), S(-12,-39), S(-25,-20) } { S(-48,-46), S( 1,-42), S(-14,-37), S(-23,-24) }
}, },
{ // Rook { // Rook
{ S(-24, -2), S(-13,-6), S(-7, -3), S( 2,-2) }, { S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) },
{ S(-18,-10), S(-10,-7), S(-5, 1), S( 9, 0) }, { S(-21,-12), S(-13, -9), S( -8, -1), S( 6, -2) },
{ S(-21, 10), S( -7,-4), S( 3, 2), S(-1,-2) }, { S(-25, 6), S(-11, -8), S( -1, -2), S( 3, -6) },
{ S(-13, -5), S( -5, 2), S(-4, -8), S(-6, 8) }, { S(-13, -6), S( -5, 1), S( -4, -9), S(-6, 7) },
{ S(-24, -8), S(-12, 5), S(-1, 4), S( 6,-9) }, { S(-27, -5), S(-15, 8), S( -4, 7), S( 3, -6) },
{ S(-24, 3), S( -4,-2), S( 4,-10), S(10, 7) }, { S(-22, 6), S( -2, 1), S( 6, -7), S(12, 10) },
{ S( -8, 1), S( 6, 2), S(10, 17), S(12,-8) }, { S( -2, 4), S( 12, 5), S( 16, 20), S(18, -5) },
{ S(-22, 12), S(-24,-6), S(-6, 13), S( 4, 7) } { S(-17, 18), S(-19, 0), S( -1, 19), S( 9, 13) }
}, },
{ // Queen { // Queen
{ S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) }, { S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) },
@ -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) } { S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) }
}, },
{ // King { // King
{ S(272, 0), S(325, 41), S(273, 80), S(190, 93) }, { S(271, 1), S(327, 45), S(271, 85), S(198, 76) },
{ S(277, 57), S(305, 98), S(241,138), S(183,131) }, { S(278, 53), S(303,100), S(234,133), S(179,135) },
{ S(198, 86), S(253,138), S(168,165), S(120,173) }, { S(195, 88), S(258,130), S(169,169), S(120,175) },
{ S(169,103), S(191,152), S(136,168), S(108,169) }, { S(164,103), S(190,156), S(138,172), S( 98,172) },
{ S(145, 98), S(176,166), S(112,197), S( 69,194) }, { S(154, 96), S(179,166), S(105,199), S( 70,199) },
{ S(122, 87), S(159,164), S( 85,174), S( 36,189) }, { S(123, 92), S(145,172), S( 81,184), S( 31,191) },
{ S( 87, 40), S(120, 99), S( 64,128), S( 25,141) }, { S( 88, 47), S(120,121), S( 65,116), S( 33,131) },
{ S( 64, 5), S( 87, 60), S( 49, 75), S( 0, 75) } { S( 59, 11), S( 89, 59), S( 45, 73), S( -1, 78) }
} }
}; };
constexpr Score PBonus[RANK_NB][FILE_NB] = constexpr Score PBonus[RANK_NB][FILE_NB] =
{ // Pawn (asymmetric distribution) { // 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( 3,-10), S( 3, -6), S( 10, 10), S( 19, 0), S( 16, 14), S( 19, 7), S( 7, -5), S( -5,-19) },
{ 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,-10), S(-15,-10), S( 11,-10), S( 15, 4), S( 32, 4), S( 22, 3), S( 5, -6), S(-22, -4) },
{ 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( -8, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S(-12, -9) },
{ 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( 13, 9), S( 0, 4), S(-13, 3), S( 1,-12), S( 11,-12), S( -2, -6), S(-13, 13), S( 5, 8) },
{ 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( -5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S(-18, 13) },
{ 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( -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 #undef S
@ -112,17 +108,14 @@ void init() {
for (Piece pc = W_PAWN; pc <= W_KING; ++pc) 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]); Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
for (Square s = SQ_A1; s <= SQ_H8; ++s) for (Square s = SQ_A1; s <= SQ_H8; ++s)
{ {
File f = 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)] psq[ pc][ s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)]
: Bonus[pc][rank_of(s)][f]); : Bonus[pc][rank_of(s)][f]);
psq[~pc][~s] = -psq[pc][s]; psq[~pc][flip_rank(s)] = -psq[pc][s];
} }
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -49,6 +49,7 @@ struct Stack {
Value staticEval; Value staticEval;
int statScore; int statScore;
int moveCount; int moveCount;
bool inCheck;
}; };
@ -69,7 +70,8 @@ struct RootMove {
Value score = -VALUE_INFINITE; Value score = -VALUE_INFINITE;
Value previousScore = -VALUE_INFINITE; Value previousScore = -VALUE_INFINITE;
int selDepth = 0; int selDepth = 0;
int tbRank; int tbRank = 0;
int bestMoveCount = 0;
Value tbScore; Value tbScore;
std::vector<Move> pv; std::vector<Move> pv;
}; };

View file

@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (c) 2013 Ronald de Man 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -27,12 +27,12 @@
#include <list> #include <list>
#include <sstream> #include <sstream>
#include <type_traits> #include <type_traits>
#include <mutex>
#include "../bitboard.h" #include "../bitboard.h"
#include "../movegen.h" #include "../movegen.h"
#include "../position.h" #include "../position.h"
#include "../search.h" #include "../search.h"
#include "../thread_win32_osx.h"
#include "../types.h" #include "../types.h"
#include "../uci.h" #include "../uci.h"
@ -45,7 +45,9 @@
#include <sys/stat.h> #include <sys/stat.h>
#else #else
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#define NOMINMAX #ifndef NOMINMAX
# define NOMINMAX // Disable macros min() and max()
#endif
#include <windows.h> #include <windows.h>
#endif #endif
@ -58,13 +60,12 @@ namespace {
constexpr int TBPIECES = 7; // Max number of supported pieces constexpr int TBPIECES = 7; // Max number of supported pieces
enum { BigEndian, LittleEndian }; 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 // 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 }; enum TBFlag { STM = 1, Mapped = 2, WinPlies = 4, LossPlies = 8, Wide = 16, SingleValue = 128 };
inline WDLScore operator-(WDLScore d) { return WDLScore(-int(d)); } 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); } inline Square operator^(Square s, int i) { return Square(int(s) ^ i); }
const std::string PieceToChar = " PNBRQK pnbrqk"; const std::string PieceToChar = " PNBRQK pnbrqk";
@ -367,7 +368,7 @@ TBTable<WDL>::TBTable(const std::string& code) : TBTable() {
hasPawns = pos.pieces(PAWN); hasPawns = pos.pieces(PAWN);
hasUniquePieces = false; hasUniquePieces = false;
for (Color c = WHITE; c <= BLACK; ++c) for (Color c : { WHITE, BLACK })
for (PieceType pt = PAWN; pt < KING; ++pt) for (PieceType pt = PAWN; pt < KING; ++pt)
if (popcount(pos.pieces(c, pt)) == 1) if (popcount(pos.pieces(c, pt)) == 1)
hasUniquePieces = true; hasUniquePieces = true;
@ -402,7 +403,17 @@ TBTable<DTZ>::TBTable(const TBTable<WDL>& wdl) : TBTable() {
// at init time, accessed at probe time. // at init time, accessed at probe time.
class TBTables { 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 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 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) { void insert(Key key, TBTable<WDL>* wdl, TBTable<DTZ>* dtz) {
uint32_t homeBucket = (uint32_t)key & (Size - 1); 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 // Ensure last element is empty to avoid overflow when looking up
for (uint32_t bucket = homeBucket; bucket < Size + Overflow - 1; ++bucket) { for (uint32_t bucket = homeBucket; bucket < Size + Overflow - 1; ++bucket) {
Key otherKey = std::get<KEY>(hashTable[bucket]); Key otherKey = hashTable[bucket].key;
if (otherKey == key || !std::get<WDL>(hashTable[bucket])) { if (otherKey == key || !hashTable[bucket].get<WDL>()) {
hashTable[bucket] = entry; hashTable[bucket] = entry;
return; return;
} }
@ -428,7 +439,7 @@ class TBTables {
// insert here and search for a new spot for the other element instead. // insert here and search for a new spot for the other element instead.
uint32_t otherHomeBucket = (uint32_t)otherKey & (Size - 1); uint32_t otherHomeBucket = (uint32_t)otherKey & (Size - 1);
if (otherHomeBucket > homeBucket) { if (otherHomeBucket > homeBucket) {
swap(entry, hashTable[bucket]); std::swap(entry, hashTable[bucket]);
key = otherKey; key = otherKey;
homeBucket = otherHomeBucket; homeBucket = otherHomeBucket;
} }
@ -441,8 +452,8 @@ public:
template<TBType Type> template<TBType Type>
TBTable<Type>* get(Key key) { TBTable<Type>* get(Key key) {
for (const Entry* entry = &hashTable[(uint32_t)key & (Size - 1)]; ; ++entry) { for (const Entry* entry = &hashTable[(uint32_t)key & (Size - 1)]; ; ++entry) {
if (std::get<KEY>(*entry) == key || !std::get<Type>(*entry)) if (entry->key == key || !entry->get<Type>())
return std::get<Type>(*entry); 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) // 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) // 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 // Then we read the corresponding SparseIndex[] entry
uint32_t block = number<uint32_t, LittleEndian>(&d->sparseIndex[k].block); 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 // All the symbols of a given length are consecutive integers (numerical
// sequence property), so we can compute the offset of our symbol of // sequence property), so we can compute the offset of our symbol of
// length len, stored at the beginning of buf64. // 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 // Now add the value of the lowest symbol of length len to get our symbol
sym += number<Sym, LittleEndian>(&d->lowestSym[len]); 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); bool blackStronger = (pos.material_key() != entry->key);
int flipColor = (symmetricBlackToMove || blackStronger) * 8; int flipColor = (symmetricBlackToMove || blackStronger) * 8;
int flipSquares = (symmetricBlackToMove || blackStronger) * 070; int flipSquares = (symmetricBlackToMove || blackStronger) * 56;
int stm = (symmetricBlackToMove || blackStronger) ^ pos.side_to_move(); int stm = (symmetricBlackToMove || blackStronger) ^ pos.side_to_move();
// For pawns, TB files store 4 separate tables according if leading pawn is on // 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)); std::swap(squares[0], *std::max_element(squares, squares + leadPawnsCnt, pawns_comp));
tbFile = file_of(squares[0]); tbFile = File(edge_distance(file_of(squares[0])));
if (tbFile > FILE_D)
tbFile = file_of(squares[0] ^ 7); // Horizontal flip: SQ_H1 -> SQ_A1
} }
// DTZ tables are one-sided, i.e. they store positions only for white to // 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 // Then we reorder the pieces to have the same sequence as the one stored
// in pieces[i]: the sequence that ensures the best compression. // in pieces[i]: the sequence that ensures the best compression.
for (int i = leadPawnsCnt; i < size; ++i) for (int i = leadPawnsCnt; i < size - 1; ++i)
for (int j = i; j < size; ++j) for (int j = i + 1; j < size; ++j)
if (d->pieces[i] == pieces[j]) if (d->pieces[i] == pieces[j])
{ {
std::swap(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. // the triangle A1-D1-D4.
if (file_of(squares[0]) > FILE_D) if (file_of(squares[0]) > FILE_D)
for (int i = 0; i < size; ++i) 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 // Encode leading pawns starting with the one with minimum MapPawns[] and
// proceeding in ascending order. // 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. // piece is below RANK_5.
if (rank_of(squares[0]) > RANK_4) if (rank_of(squares[0]) > RANK_4)
for (int i = 0; i < size; ++i) 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 // Look for the first piece of the leading group not on the A1-D4 diagonal
// and ensure it is mapped below the 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])) if (!off_A1H8(squares[i]))
continue; 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) for (int j = i; j < size; ++j)
squares[j] = Square(((squares[j] >> 3) | (squares[j] << 3)) & 63); squares[j] = Square(((squares[j] >> 3) | (squares[j] << 3)) & 63);
break; break;
@ -975,7 +984,7 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) {
d->sizeofBlock = 1ULL << *data++; d->sizeofBlock = 1ULL << *data++;
d->span = 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++); auto padding = number<uint8_t, LittleEndian>(data++);
d->blocksNum = number<uint32_t, LittleEndian>(data); data += sizeof(uint32_t); d->blocksNum = number<uint32_t, LittleEndian>(data); data += sizeof(uint32_t);
d->blockLengthSize = d->blocksNum + padding; // Padded to ensure SparseIndex[] 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 }; enum { Split = 1, HasPawns = 2 };
assert(e.hasPawns == !!(*data & HasPawns)); assert(e.hasPawns == bool(*data & HasPawns));
assert((e.key != e.key2) == !!(*data & Split)); assert((e.key != e.key2) == bool(*data & Split));
data++; // First byte stores flags data++; // First byte stores flags
@ -1124,14 +1133,14 @@ void set(T& e, uint8_t* data) {
template<TBType Type> template<TBType Type>
void* mapped(TBTable<Type>& e, const Position& pos) { 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 // Use 'acquire' to avoid a thread reading 'ready' == true while
// another is still working. (compiler reordering may cause this). // another is still working. (compiler reordering may cause this).
if (e.ready.load(std::memory_order_acquire)) if (e.ready.load(std::memory_order_acquire))
return e.baseAddress; // Could be nullptr if file does not exist return e.baseAddress; // Could be nullptr if file does not exist
std::unique_lock<Mutex> lk(mutex); std::unique_lock<std::mutex> lk(mutex);
if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock
return e.baseAddress; return e.baseAddress;
@ -1344,7 +1353,7 @@ void Tablebases::init(const std::string& paths) {
if (leadPawnsCnt == 1) if (leadPawnsCnt == 1)
{ {
MapPawns[sq] = availableSquares--; MapPawns[sq] = availableSquares--;
MapPawns[sq ^ 7] = availableSquares--; // Horizontal flip MapPawns[flip_file(sq)] = availableSquares--;
} }
LeadPawnIdx[leadPawnsCnt][sq] = idx; LeadPawnIdx[leadPawnsCnt][sq] = idx;
idx += Binomial[leadPawnsCnt - 1][MapPawns[sq]]; idx += Binomial[leadPawnsCnt - 1][MapPawns[sq]];

View file

@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (c) 2013 Ronald de Man 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -52,6 +52,15 @@ Thread::~Thread() {
stdThread.join(); 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 /// Thread::clear() reset histories, usually before a new game
@ -59,20 +68,24 @@ void Thread::clear() {
counterMoves.fill(MOVE_NONE); counterMoves.fill(MOVE_NONE);
mainHistory.fill(0); mainHistory.fill(0);
lowPlyHistory.fill(0);
captureHistory.fill(0); captureHistory.fill(0);
for (auto& to : continuationHistory) for (bool inCheck : { false, true })
for (auto& h : to) for (StatsType c : { NoCaptures, Captures })
h->fill(0); {
for (auto& to : continuationHistory[inCheck][c])
continuationHistory[NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1); 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 /// Thread::start_searching() wakes up the thread that will start the search
void Thread::start_searching() { void Thread::start_searching() {
std::lock_guard<Mutex> lk(mutex); std::lock_guard<std::mutex> lk(mutex);
searching = true; searching = true;
cv.notify_one(); // Wake up the thread in idle_loop() cv.notify_one(); // Wake up the thread in idle_loop()
} }
@ -83,7 +96,7 @@ void Thread::start_searching() {
void Thread::wait_for_search_finished() { void Thread::wait_for_search_finished() {
std::unique_lock<Mutex> lk(mutex); std::unique_lock<std::mutex> lk(mutex);
cv.wait(lk, [&]{ return !searching; }); cv.wait(lk, [&]{ return !searching; });
} }
@ -103,7 +116,7 @@ void Thread::idle_loop() {
while (true) while (true)
{ {
std::unique_lock<Mutex> lk(mutex); std::unique_lock<std::mutex> lk(mutex);
searching = false; searching = false;
cv.notify_one(); // Wake up anyone waiting for search finished cv.notify_one(); // Wake up anyone waiting for search finished
cv.wait(lk, [&]{ return searching; }); cv.wait(lk, [&]{ return searching; });
@ -138,7 +151,10 @@ void ThreadPool::set(size_t requested) {
clear(); clear();
// Reallocate the hash with the new threadpool size // 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(); th->clear();
main()->callsCnt = 0; main()->callsCnt = 0;
main()->previousScore = VALUE_INFINITE; main()->bestPreviousScore = VALUE_INFINITE;
main()->previousTimeReduction = 1.0; main()->previousTimeReduction = 1.0;
} }
@ -163,6 +179,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
main()->wait_for_search_finished(); main()->wait_for_search_finished();
main()->stopOnPonderhit = stop = false; main()->stopOnPonderhit = stop = false;
increaseDepth = true;
main()->ponder = ponderMode; main()->ponder = ponderMode;
Search::Limits = limits; Search::Limits = limits;
Search::RootMoves rootMoves; Search::RootMoves rootMoves;
@ -192,7 +209,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
for (Thread* th : *this) for (Thread* th : *this)
{ {
th->nodes = th->tbHits = th->nmpMinPly = 0; th->nodes = th->tbHits = th->nmpMinPly = 0;
th->rootDepth = th->completedDepth = DEPTH_ZERO; th->rootDepth = th->completedDepth = 0;
th->rootMoves = rootMoves; th->rootMoves = rootMoves;
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
} }

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -42,8 +42,8 @@
class Thread { class Thread {
Mutex mutex; std::mutex mutex;
ConditionVariable cv; std::condition_variable cv;
size_t idx; size_t idx;
bool exit = false, searching = true; // Set before starting std::thread bool exit = false, searching = true; // Set before starting std::thread
NativeThread stdThread; NativeThread stdThread;
@ -56,10 +56,12 @@ public:
void idle_loop(); void idle_loop();
void start_searching(); void start_searching();
void wait_for_search_finished(); void wait_for_search_finished();
int best_move_count(Move move) const;
Pawns::Table pawnsTable; Pawns::Table pawnsTable;
Material::Table materialTable; Material::Table materialTable;
size_t pvIdx, pvLast; size_t pvIdx, pvLast;
uint64_t ttHitAverage;
int selDepth, nmpMinPly; int selDepth, nmpMinPly;
Color nmpColor; Color nmpColor;
std::atomic<uint64_t> nodes, tbHits, bestMoveChanges; std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
@ -69,8 +71,9 @@ public:
Depth rootDepth, completedDepth; Depth rootDepth, completedDepth;
CounterMoveHistory counterMoves; CounterMoveHistory counterMoves;
ButterflyHistory mainHistory; ButterflyHistory mainHistory;
LowPlyHistory lowPlyHistory;
CapturePieceToHistory captureHistory; CapturePieceToHistory captureHistory;
ContinuationHistory continuationHistory; ContinuationHistory continuationHistory[2][2];
Score contempt; Score contempt;
}; };
@ -85,7 +88,8 @@ struct MainThread : public Thread {
void check_time(); void check_time();
double previousTimeReduction; double previousTimeReduction;
Value previousScore; Value bestPreviousScore;
Value iterValue[4];
int callsCnt; int callsCnt;
bool stopOnPonderhit; bool stopOnPonderhit;
std::atomic_bool ponder; 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 nodes_searched() const { return accumulate(&Thread::nodes); }
uint64_t tb_hits() const { return accumulate(&Thread::tbHits); } uint64_t tb_hits() const { return accumulate(&Thread::tbHits); }
std::atomic_bool stop; std::atomic_bool stop, increaseDepth;
private: private:
StateListPtr setupStates; StateListPtr setupStates;

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -21,63 +21,19 @@
#ifndef THREAD_WIN32_OSX_H_INCLUDED #ifndef THREAD_WIN32_OSX_H_INCLUDED
#define 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> #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 /// 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 /// size of 512KB by default, this is too low for deep searches, which require
/// adjust it to TH_STACK_SIZE. The implementation calls pthread_create() with /// somewhat more than 1MB stack, so adjust it to TH_STACK_SIZE.
/// proper stack size parameter. /// 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> #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::*)()>> template <class T, class P = std::pair<T*, void(T::*)()>>
void* start_routine(void* ptr) void* start_routine(void* ptr)

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -28,66 +28,21 @@
TimeManagement Time; // Our global time management object TimeManagement Time; // Our global time management object
namespace { /// init() is called at the beginning of the search and calculates the bounds
/// of time allowed for the current game ply. We currently support:
enum TimeType { OptimumTime, MaxTime }; // 1) x basetime (+z increment)
// 2) x moves in y seconds (+z increment)
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
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
TimePoint minThinkingTime = Options["Minimum Thinking Time"]; TimePoint minThinkingTime = TimePoint(Options["Minimum Thinking Time"]);
TimePoint moveOverhead = Options["Move Overhead"]; TimePoint moveOverhead = TimePoint(Options["Move Overhead"]);
TimePoint slowMover = Options["Slow Mover"]; TimePoint slowMover = TimePoint(Options["Slow Mover"]);
TimePoint npmsec = Options["nodestime"]; TimePoint npmsec = TimePoint(Options["nodestime"]);
TimePoint hypMyTime;
// 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 // If we have to play in 'nodes as time' mode, then convert from time
// to nodes, and use resulting values in time management formulas. // to nodes, and use resulting values in time management formulas.
@ -105,29 +60,40 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
} }
startTime = limits.startTime; 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 // Make sure timeLeft is > 0 since we may use it as a divisor
// and choose the minimum of calculated search time values. Usually the greatest TimePoint timeLeft = std::max(TimePoint(1),
// hypMTG gives the minimum values. limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg));
for (int hypMTG = 1; hypMTG <= maxMTG; ++hypMTG)
// 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 opt_scale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0,
hypMyTime = limits.time[us] 0.2 * limits.time[us] / double(timeLeft));
+ limits.inc[us] * (hypMTG - 1) max_scale = 4 + std::min(36, ply) / 12.0;
- 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);
} }
// 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"]) if (Options["Ponder"])
optimumTime += optimumTime / 4; optimumTime += optimumTime / 4;
} }

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -35,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) { 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 // Preserve any existing move for the same position
if (m || (k >> 48) != key16) if (m || (k >> 48) != key16)
move16 = (uint16_t)m; move16 = (uint16_t)m;
// Overwrite less valuable entries // Overwrite less valuable entries
if ( (k >> 48) != key16 if ( (k >> 48) != key16
|| d / ONE_PLY + 10 > depth8 || d - DEPTH_OFFSET > depth8 - 4
|| b == BOUND_EXACT) || b == BOUND_EXACT)
{ {
assert(d >= DEPTH_OFFSET);
key16 = (uint16_t)(k >> 48); key16 = (uint16_t)(k >> 48);
value16 = (int16_t)v; value16 = (int16_t)v;
eval16 = (int16_t)ev; eval16 = (int16_t)ev;
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
assert((d - DEPTH_NONE) / ONE_PLY >= 0); depth8 = (uint8_t)(d - DEPTH_OFFSET);
depth8 = (uint8_t)((d - DEPTH_NONE) / ONE_PLY);
} }
} }
@ -64,11 +63,10 @@ void TranspositionTable::resize(size_t mbSize) {
Threads.main()->wait_for_search_finished(); Threads.main()->wait_for_search_finished();
aligned_ttmem_free(mem);
clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster);
table = static_cast<Cluster*>(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), mem));
free(mem);
mem = malloc(clusterCount * sizeof(Cluster) + CacheLineSize - 1);
if (!mem) if (!mem)
{ {
std::cerr << "Failed to allocate " << mbSize std::cerr << "Failed to allocate " << mbSize
@ -76,7 +74,6 @@ void TranspositionTable::resize(size_t mbSize) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
table = (Cluster*)((uintptr_t(mem) + CacheLineSize - 1) & ~(CacheLineSize - 1));
clear(); clear();
} }
@ -97,8 +94,8 @@ void TranspositionTable::clear() {
WinProcGroup::bindThisThread(idx); WinProcGroup::bindThisThread(idx);
// Each thread will zero its part of the hash table // Each thread will zero its part of the hash table
const size_t stride = clusterCount / Options["Threads"], const size_t stride = size_t(clusterCount / Options["Threads"]),
start = stride * idx, start = size_t(stride * idx),
len = idx != Options["Threads"] - 1 ? len = idx != Options["Threads"] - 1 ?
stride : clusterCount - start; stride : clusterCount - start;
@ -106,7 +103,7 @@ void TranspositionTable::clear() {
}); });
} }
for (std::thread& th: threads) for (std::thread& th : threads)
th.join(); th.join();
} }
@ -155,9 +152,9 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
int TranspositionTable::hashfull() const { int TranspositionTable::hashfull() const {
int cnt = 0; 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) for (int j = 0; j < ClusterSize; ++j)
cnt += (table[i].entry[j].genBound8 & 0xF8) == generation8; cnt += (table[i].entry[j].genBound8 & 0xF8) == generation8;
return cnt * 1000 / (ClusterSize * (1000 / ClusterSize)); return cnt / ClusterSize;
} }

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -40,8 +40,8 @@ struct TTEntry {
Move move() const { return (Move )move16; } Move move() const { return (Move )move16; }
Value value() const { return (Value)value16; } Value value() const { return (Value)value16; }
Value eval() const { return (Value)eval16; } Value eval() const { return (Value)eval16; }
Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)) + DEPTH_NONE; } Depth depth() const { return (Depth)depth8 + DEPTH_OFFSET; }
bool is_pv() const { return (bool)(genBound8 & 0x4); } bool is_pv() const { return (bool)(genBound8 & 0x4); }
Bound bound() const { return (Bound)(genBound8 & 0x3); } Bound bound() const { return (Bound)(genBound8 & 0x3); }
void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev); 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 /// A TranspositionTable is an array of Cluster, of size clusterCount. Each
/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry /// cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry
/// contains information of exactly one position. The size of a cluster should /// contains information on exactly one position. The size of a Cluster should
/// divide the size of a cache line size, to ensure that clusters never cross /// divide the size of a cache line for best performance,
/// cache lines. This ensures best cache performance, as the cacheline is /// as the cacheline is prefetched when possible.
/// prefetched, as soon as possible.
class TranspositionTable { class TranspositionTable {
static constexpr int CacheLineSize = 64;
static constexpr int ClusterSize = 3; static constexpr int ClusterSize = 3;
struct Cluster { struct Cluster {
TTEntry entry[ClusterSize]; 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: public:
~TranspositionTable() { free(mem); } ~TranspositionTable() { aligned_ttmem_free(mem); }
void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound
TTEntry* probe(const Key key, bool& found) const; TTEntry* probe(const Key key, bool& found) const;
int hashfull() const; int hashfull() const;

146
src/tune.cpp Normal file
View 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
View 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

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -43,6 +43,7 @@
#include <climits> #include <climits>
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <algorithm>
#if defined(_MSC_VER) #if defined(_MSC_VER)
// Disable some silly and noisy warning from MSVC compiler // Disable some silly and noisy warning from MSVC compiler
@ -133,19 +134,17 @@ enum Color {
constexpr Color Colors[2] = { WHITE, BLACK }; constexpr Color Colors[2] = { WHITE, BLACK };
enum CastlingSide { enum CastlingRights {
KING_SIDE, QUEEN_SIDE, CASTLING_SIDE_NB = 2
};
enum CastlingRight {
NO_CASTLING, NO_CASTLING,
WHITE_OO, WHITE_OO,
WHITE_OOO = WHITE_OO << 1, WHITE_OOO = WHITE_OO << 1,
BLACK_OO = WHITE_OO << 2, BLACK_OO = WHITE_OO << 2,
BLACK_OOO = WHITE_OO << 3, BLACK_OOO = WHITE_OO << 3,
WHITE_CASTLING = WHITE_OO | WHITE_OOO, KING_SIDE = WHITE_OO | BLACK_OO,
BLACK_CASTLING = BLACK_OO | BLACK_OOO, QUEEN_SIDE = WHITE_OOO | BLACK_OOO,
WHITE_CASTLING = WHITE_OO | WHITE_OOO,
BLACK_CASTLING = BLACK_OO | BLACK_OOO,
ANY_CASTLING = WHITE_CASTLING | BLACK_CASTLING, ANY_CASTLING = WHITE_CASTLING | BLACK_CASTLING,
CASTLING_RIGHT_NB = 16 CASTLING_RIGHT_NB = 16
@ -179,14 +178,17 @@ enum Value : int {
VALUE_INFINITE = 32001, VALUE_INFINITE = 32001,
VALUE_NONE = 32002, VALUE_NONE = 32002,
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, VALUE_TB_WIN_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY,
VALUE_MATED_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, PawnValueMg = 124, PawnValueEg = 206,
KnightValueMg = 782, KnightValueEg = 865, KnightValueMg = 781, KnightValueEg = 854,
BishopValueMg = 830, BishopValueEg = 918, BishopValueMg = 825, BishopValueEg = 915,
RookValueMg = 1289, RookValueEg = 1378, RookValueMg = 1276, RookValueEg = 1380,
QueenValueMg = 2529, QueenValueEg = 2687, QueenValueMg = 2538, QueenValueEg = 2682,
Tempo = 28,
MidgameLimit = 15258, EndgameLimit = 3915, MidgameLimit = 15258, EndgameLimit = 3915,
@ -207,22 +209,24 @@ enum Piece {
PIECE_NB = 16 PIECE_NB = 16
}; };
extern Value PieceValue[PHASE_NB][PIECE_NB]; constexpr Value PieceValue[PHASE_NB][PIECE_NB] = {
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO,
enum Depth : int { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO },
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, VALUE_ZERO, VALUE_ZERO,
ONE_PLY = 1, VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, VALUE_ZERO, VALUE_ZERO }
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
}; };
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 { enum Square : int {
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1, 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) \ #define ENABLE_FULL_OPERATORS_ON(T) \
ENABLE_BASE_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*(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); } \
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); } inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
ENABLE_FULL_OPERATORS_ON(Value) ENABLE_FULL_OPERATORS_ON(Value)
ENABLE_FULL_OPERATORS_ON(Depth)
ENABLE_FULL_OPERATORS_ON(Direction) ENABLE_FULL_OPERATORS_ON(Direction)
ENABLE_INCR_OPERATORS_ON(PieceType) ENABLE_INCR_OPERATORS_ON(PieceType)
ENABLE_INCR_OPERATORS_ON(Piece) ENABLE_INCR_OPERATORS_ON(Piece)
ENABLE_INCR_OPERATORS_ON(Color)
ENABLE_INCR_OPERATORS_ON(Square) ENABLE_INCR_OPERATORS_ON(Square)
ENABLE_INCR_OPERATORS_ON(File) ENABLE_INCR_OPERATORS_ON(File)
ENABLE_INCR_OPERATORS_ON(Rank) ENABLE_INCR_OPERATORS_ON(Rank)
@ -354,24 +355,29 @@ inline Score operator*(Score s, int i) {
return result; 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) { constexpr Color operator~(Color c) {
return Color(c ^ BLACK); // Toggle color return Color(c ^ BLACK); // Toggle color
} }
constexpr Square operator~(Square s) { constexpr Square flip_rank(Square s) {
return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8 return Square(s ^ SQ_A8);
} }
constexpr File operator~(File f) { constexpr Square flip_file(Square s) {
return File(f ^ FILE_H); // Horizontal flip FILE_A -> FILE_H return Square(s ^ SQ_H1);
} }
constexpr Piece operator~(Piece pc) { constexpr Piece operator~(Piece pc) {
return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT
} }
constexpr CastlingRight operator|(Color c, CastlingSide s) { constexpr CastlingRights operator&(Color c, CastlingRights cr) {
return CastlingRight(WHITE_OO << ((s == QUEEN_SIDE) + 2 * c)); return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr);
} }
constexpr Value mate_in(int ply) { constexpr Value mate_in(int ply) {
@ -451,6 +457,10 @@ constexpr Move make_move(Square from, Square to) {
return Move((from << 6) + to); return Move((from << 6) + to);
} }
constexpr Move reverse_move(Move m) {
return make_move(to_sq(m), from_sq(m));
}
template<MoveType T> template<MoveType T>
constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) { constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) {
return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); 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 // defined(EVAL_NNUE) || defined(EVAL_LEARN)
#endif // #ifndef TYPES_H_INCLUDED #endif // #ifndef TYPES_H_INCLUDED
#include "tune.h" // Global visibility to tuning setup

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -154,7 +154,7 @@ namespace {
limits.startTime = now(); // As early as possible! limits.startTime = now(); // As early as possible!
while (is >> token) while (is >> token)
if (token == "searchmoves") if (token == "searchmoves") // Needs to be the last command on the line
while (is >> token) while (is >> token)
limits.searchmoves.push_back(UCI::to_move(pos, token)); limits.searchmoves.push_back(UCI::to_move(pos, token));
@ -185,7 +185,7 @@ namespace {
uint64_t num, nodes = 0, cnt = 1; uint64_t num, nodes = 0, cnt = 1;
vector<string> list = setup_bench(pos, args); 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(); TimePoint elapsed = now();
@ -194,16 +194,21 @@ namespace {
istringstream is(cmd); istringstream is(cmd);
is >> skipws >> token; is >> skipws >> token;
if (token == "go") if (token == "go" || token == "eval")
{ {
cerr << "\nPosition: " << cnt++ << '/' << num << endl; cerr << "\nPosition: " << cnt++ << '/' << num << endl;
go(pos, is, states); if (token == "go")
Threads.main()->wait_for_search_finished(); {
nodes += Threads.nodes_searched(); 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 == "setoption") setoption(is);
else if (token == "position") position(pos, is, states); 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' 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; Position pos;
string token, cmd; string token, cmd;
StateListPtr states(new std::deque<StateInfo>(1)); 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) for (int i = 1; i < argc; ++i)
cmd += std::string(argv[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 == "ucinewgame") Search::clear();
else if (token == "isready") is_ready(); else if (token == "isready") is_ready();
// Additional custom non-UCI commands, mainly for debugging // Additional custom non-UCI commands, mainly for debugging.
else if (token == "flip") pos.flip(); // Do not use these commands during a search!
else if (token == "bench") bench(pos, is, states); else if (token == "flip") pos.flip();
else if (token == "d") sync_cout << pos << sync_endl; else if (token == "bench") bench(pos, is, states);
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl; 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) #if defined (EVAL_LEARN)
else if (token == "gensfen") Learner::gen_sfen(pos, is); else if (token == "gensfen") Learner::gen_sfen(pos, is);
else if (token == "learn") Learner::learn(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 // ƒeƒXƒgƒRƒ}ƒ“ƒh
else if (token == "test") test_cmd(pos, is); else if (token == "test") test_cmd(pos, is);
#endif #endif
else else
sync_cout << "Unknown command: " << cmd << sync_endl; sync_cout << "Unknown command: " << cmd << sync_endl;
@ -419,7 +424,7 @@ string UCI::value(Value v) {
stringstream ss; stringstream ss;
if (abs(v) < VALUE_MATE - MAX_PLY) if (abs(v) < VALUE_MATE_IN_MAX_PLY)
ss << "cp " << v * 100 / PawnValueEg; ss << "cp " << v * 100 / PawnValueEg;
else else
ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad 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 Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -38,9 +38,9 @@ namespace UCI {
/// 'On change' actions, triggered by an option's value change /// 'On change' actions, triggered by an option's value change
void on_clear_hash(const Option&) { Search::clear(); } 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_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_tb_path(const Option& o) { Tablebases::init(o); }
void on_eval_dir(const Option& o) { load_eval_finished = false; } void on_eval_dir(const Option& o) { load_eval_finished = false; }
@ -69,12 +69,14 @@ void init(OptionsMap& o) {
o["Ponder"] << Option(false); o["Ponder"] << Option(false);
o["MultiPV"] << Option(1, 1, 500); o["MultiPV"] << Option(1, 1, 500);
o["Skill Level"] << Option(20, 0, 20); o["Skill Level"] << Option(20, 0, 20);
o["Move Overhead"] << Option(30, 0, 5000); o["Move Overhead"] << Option(10, 0, 5000);
o["Minimum Thinking Time"] << Option(20, 0, 5000); o["Minimum Thinking Time"] << Option( 0, 0, 5000);
o["Slow Mover"] << Option(84, 10, 1000); o["Slow Mover"] << Option(100, 10, 1000);
o["nodestime"] << Option(0, 0, 10000); o["nodestime"] << Option(0, 0, 10000);
o["UCI_Chess960"] << Option(false); o["UCI_Chess960"] << Option(false);
o["UCI_AnalyseMode"] << 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["SyzygyPath"] << Option("<empty>", on_tb_path);
o["SyzygyProbeDepth"] << Option(1, 1, 100); o["SyzygyProbeDepth"] << Option(1, 1, 100);
o["Syzygy50MoveRule"] << Option(true); o["Syzygy50MoveRule"] << Option(true);