1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-04-30 08:43:09 +00:00

Merge branch 'master' of github.com:official-stockfish/Stockfish

# Conflicts:
#	src/Makefile
#	src/position.cpp
#	src/position.h
#	src/search.cpp
#	src/types.h
#	src/uci.cpp
This commit is contained in:
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
sudo: required
dist: xenial
matrix:
@ -51,14 +50,11 @@ script:
- echo "Reference bench:" $benchref
#
# Verify bench number against various builds
- export CXXFLAGS=-Werror
- export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG"
- make clean && make -j2 ARCH=x86-64 optimize=no debug=yes build && ../tests/signature.sh $benchref
- make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref
- make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref
# Verify bench number is ONE_PLY independent by doubling its value
- sed -i'.bak' 's/.*\(ONE_PLY = [0-9]*\),.*/\1 * 2,/g' types.h
- make clean && make -j2 ARCH=x86-64 build && ../tests/signature.sh $benchref
#
# Check perft and reproducible search
- ../tests/perft.sh

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

View file

@ -1,7 +1,7 @@
## Overview
[![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
derived from Glaurung 2.1. It is not a complete chess program and requires a
@ -34,15 +34,15 @@ Currently, Stockfish has the following UCI options:
A positive value for contempt favors middle game positions and avoids draws.
* #### Analysis Contempt
By default, contempt is set to prefer the side to move. Set this option to "White"
By default, contempt is set to prefer the side to move. Set this option to "White"
or "Black" to analyse with contempt for that side, or "Off" to disable contempt.
* #### Threads
The number of CPU threads used for searching a position. For best performance, set
The number of CPU threads used for searching a position. For best performance, set
this equal to the number of CPU cores available.
* #### Hash
The size of the hash table in MB.
The size of the hash table in MB. It is recommended to set Hash after setting Threads.
* #### Clear Hash
Clear the hash table.
@ -55,21 +55,30 @@ Currently, Stockfish has the following UCI options:
Leave at 1 for best performance.
* #### Skill Level
Lower the Skill Level in order to make Stockfish play weaker.
Lower the Skill Level in order to make Stockfish play weaker (see also UCI_LimitStrength).
Internally, MultiPV is enabled, and with a certain probability depending on the Skill Level a
weaker move will be played.
* #### UCI_LimitStrength
Enable weaker play aiming for an Elo rating as set by UCI_Elo. This option overrides Skill Level.
* #### UCI_Elo
If enabled by UCI_LimitStrength, aim for an engine strength of the given Elo.
This Elo rating has been calibrated at a time control of 60s+0.6s and anchored to CCRL 40/4.
* #### Move Overhead
Assume a time delay of x ms due to network and GUI overheads. This is useful to
Assume a time delay of x ms due to network and GUI overheads. This is useful to
avoid losses on time in those cases.
* #### Minimum Thinking Time
Search for at least x ms per move.
Search for at least x ms per move.
* #### Slow Mover
Lower values will make Stockfish take less time in games, higher values will
Lower values will make Stockfish take less time in games, higher values will
make it think longer.
* #### nodestime
Tells the engine to use nodes searched instead of wall time to account for
Tells the engine to use nodes searched instead of wall time to account for
elapsed time. Useful for engine testing.
* #### UCI_Chess960
@ -79,13 +88,13 @@ Currently, Stockfish has the following UCI options:
An option handled by your GUI.
* #### SyzygyPath
Path to the folders/directories storing the Syzygy tablebase files. Multiple
directories are to be separated by ";" on Windows and by ":" on Unix-based
Path to the folders/directories storing the Syzygy tablebase files. Multiple
directories are to be separated by ";" on Windows and by ":" on Unix-based
operating systems. Do not use spaces around the ";" or ":".
Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6`
It is recommended to store .rtbw files on an SSD. There is no loss in storing
It is recommended to store .rtbw files on an SSD. There is no loss in storing
the .rtbz files on a regular HD. It is recommended to verify all md5 checksums
of the downloaded tablebase files (`md5sum -c checksum.md5`) as corruption will
lead to engine crashes.
@ -129,6 +138,30 @@ more compact than Nalimov tablebases, while still storing all information
needed for optimal play and in addition being able to take into account
the 50-move rule.
## Large Pages
Stockfish supports large pages on Linux and Windows. Large pages make
the hash access more efficient, improving the engine speed, especially
on large hash sizes. Typical increases are 5..10% in terms of nps, but
speed increases up to 30% have been measured. The support is
automatic. Stockfish attempts to use large pages when available and
will fall back to regular memory allocation when this is not the case.
### Support on Linux
Large page support on Linux is obtained by the Linux kernel
transparent huge pages functionality. Typically, transparent huge pages
are already enabled and no configuration is needed.
### Support on Windows
The use of large pages requires "Lock Pages in Memory" privilege. See
[Enable the Lock Pages in Memory Option (Windows)](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows)
on how to enable this privilege. Logout/login may be needed
afterwards. Due to memory fragmentation, it may not always be
possible to allocate large pages even when enabled. A reboot
might alleviate this problem. To determine whether large pages
are in use, see the engine log.
## Compiling Stockfish yourself from the sources
@ -144,6 +177,14 @@ compile (for instance with Microsoft MSVC) you need to manually
set/unset some switches in the compiler command line; see file *types.h*
for a quick reference.
When reporting an issue or a bug, please tell us which version and
compiler you used to create your executable. These informations can
be found by typing the following commands in a console:
```
./stockfish
compiler
```
## Understanding the code base and participating in the project
@ -153,12 +194,12 @@ community effort. There are a few ways to help contribute to its growth.
### Donating hardware
Improving Stockfish requires a massive amount of testing. You can donate
your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker)
and view the current tests on [Fishtest](http://tests.stockfishchess.org/tests).
your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker:-overview)
and view the current tests on [Fishtest](https://tests.stockfishchess.org/tests).
### Improving the code
If you want to help improve the code, there are several valuable ressources:
If you want to help improve the code, there are several valuable resources:
* [In this wiki,](https://www.chessprogramming.org) many techniques used in
Stockfish are explained with a lot of background information.
@ -169,8 +210,8 @@ generic rather than being focused on Stockfish's precise implementation.
Nevertheless, a helpful resource.
* The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish).
Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking)
group and engine testing is done on [Fishtest](http://tests.stockfishchess.org/tests).
Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking)
group and engine testing is done on [Fishtest](https://tests.stockfishchess.org/tests).
If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test)
first, where the basics of Stockfish development are explained.

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

View file

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

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -61,6 +61,11 @@ const vector<string> Defaults = {
"1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1",
"6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1",
"8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1",
"5rk1/q6p/2p3bR/1pPp1rP1/1P1Pp3/P3B1Q1/1K3P2/R7 w - - 93 90",
"4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21",
"r3k2r/3nnpbp/q2pp1p1/p7/Pp1PPPP1/4BNN1/1P5P/R2Q1RK1 w kq - 0 16",
"3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40",
"4k3/3q1r2/1N2r1b1/3ppN2/2nPP3/1B1R2n1/2R1Q3/3K4 w - - 5 1",
// 5-man positions
"8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate
@ -113,7 +118,7 @@ vector<string> setup_bench(const Position& current, istream& is) {
string fenFile = (is >> token) ? token : "default";
string limitType = (is >> token) ? token : "depth";
go = "go " + limitType + " " + limit;
go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit;
if (fenFile == "default")
fens = Defaults;
@ -139,9 +144,9 @@ vector<string> setup_bench(const Position& current, istream& is) {
file.close();
}
list.emplace_back("ucinewgame");
list.emplace_back("setoption name Threads value " + threads);
list.emplace_back("setoption name Hash value " + ttSize);
list.emplace_back("ucinewgame");
for (const string& fen : fens)
if (fen.find("setoption") != string::npos)

View file

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

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -69,32 +69,14 @@ const std::string Bitboards::pretty(Bitboard b) {
void Bitboards::init() {
for (unsigned i = 0; i < (1 << 16); ++i)
PopCnt16[i] = std::bitset<16>(i).count();
PopCnt16[i] = uint8_t(std::bitset<16>(i).count());
for (Square s = SQ_A1; s <= SQ_H8; ++s)
SquareBB[s] = (1ULL << s);
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
int steps[][5] = { {}, { 7, 9 }, { 6, 10, 15, 17 }, {}, {}, {}, { 1, 7, 8, 9 } };
for (Color c = WHITE; c <= BLACK; ++c)
for (PieceType pt : { PAWN, KNIGHT, KING })
for (Square s = SQ_A1; s <= SQ_H8; ++s)
for (int i = 0; steps[pt][i]; ++i)
{
Square to = s + Direction(c == WHITE ? steps[pt][i] : -steps[pt][i]);
if (is_ok(to) && distance(s, to) < 3)
{
if (pt == PAWN)
PawnAttacks[c][s] |= to;
else
PseudoAttacks[pt][s] |= to;
}
}
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST };
Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
@ -104,6 +86,15 @@ void Bitboards::init() {
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
{
PawnAttacks[WHITE][s1] = pawn_attacks_bb<WHITE>(square_bb(s1));
PawnAttacks[BLACK][s1] = pawn_attacks_bb<BLACK>(square_bb(s1));
for (int step : {-9, -8, -7, -1, 1, 7, 8, 9} )
PseudoAttacks[KING][s1] |= safe_destination(s1, step);
for (int step : {-17, -15, -10, -6, 6, 10, 15, 17} )
PseudoAttacks[KNIGHT][s1] |= safe_destination(s1, step);
PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb<BISHOP>(s1, 0);
PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0);
@ -119,20 +110,16 @@ namespace {
Bitboard sliding_attack(Direction directions[], Square sq, Bitboard occupied) {
Bitboard attack = 0;
Bitboard attacks = 0;
for (int i = 0; i < 4; ++i)
for (Square s = sq + directions[i];
is_ok(s) && distance(s, s - directions[i]) == 1;
s += directions[i])
{
attack |= s;
{
Square s = sq;
while(safe_destination(s, directions[i]) && !(occupied & s))
attacks |= (s += directions[i]);
}
if (occupied & s)
break;
}
return attack;
return attacks;
}

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -106,7 +106,7 @@ extern Magic RookMagics[SQUARE_NB];
extern Magic BishopMagics[SQUARE_NB];
inline Bitboard square_bb(Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
assert(is_ok(s));
return SquareBB[s];
}
@ -119,12 +119,18 @@ inline Bitboard operator^( Bitboard b, Square s) { return b ^ square_bb(s); }
inline Bitboard& operator|=(Bitboard& b, Square s) { return b |= square_bb(s); }
inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); }
inline Bitboard operator&(Square s, Bitboard b) { return b & s; }
inline Bitboard operator|(Square s, Bitboard b) { return b | s; }
inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; }
inline Bitboard operator|(Square s, Square s2) { return square_bb(s) | s2; }
constexpr bool more_than_one(Bitboard b) {
return b & (b - 1);
}
inline bool opposite_colors(Square s1, Square s2) {
return bool(DarkSquares & s1) != bool(DarkSquares & s2);
constexpr bool opposite_colors(Square s1, Square s2) {
return (s1 + rank_of(s1) + s2 + rank_of(s2)) & 1;
}
@ -148,7 +154,7 @@ inline Bitboard file_bb(Square s) {
}
/// shift() moves a bitboard one step along direction D
/// shift() moves a bitboard one or two steps as specified by the direction D
template<Direction D>
constexpr Bitboard shift(Bitboard b) {
@ -170,6 +176,12 @@ constexpr Bitboard pawn_attacks_bb(Bitboard b) {
: shift<SOUTH_WEST>(b) | shift<SOUTH_EAST>(b);
}
inline Bitboard pawn_attacks_bb(Color c, Square s) {
assert(is_ok(s));
return PawnAttacks[c][s];
}
/// pawn_double_attacks_bb() returns the squares doubly attacked by pawns of the
/// given color from the squares in the given bitboard.
@ -184,8 +196,8 @@ constexpr Bitboard pawn_double_attacks_bb(Bitboard b) {
/// adjacent_files_bb() returns a bitboard representing all the squares on the
/// adjacent files of the given one.
inline Bitboard adjacent_files_bb(File f) {
return shift<EAST>(file_bb(f)) | shift<WEST>(file_bb(f));
inline Bitboard adjacent_files_bb(Square s) {
return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s));
}
@ -193,8 +205,8 @@ inline Bitboard adjacent_files_bb(File f) {
/// If the given squares are not on a same file/rank/diagonal, return 0.
inline Bitboard between_bb(Square s1, Square s2) {
return LineBB[s1][s2] & ( (AllSquares << (s1 + (s1 < s2)))
^(AllSquares << (s2 + !(s1 < s2))));
Bitboard b = LineBB[s1][s2] & ((AllSquares << s1) ^ (AllSquares << s2));
return b & (b - 1); //exclude lsb
}
@ -203,8 +215,8 @@ inline Bitboard between_bb(Square s1, Square s2) {
/// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2.
inline Bitboard forward_ranks_bb(Color c, Square s) {
return c == WHITE ? ~Rank1BB << 8 * (rank_of(s) - RANK_1)
: ~Rank8BB >> 8 * (RANK_8 - rank_of(s));
return c == WHITE ? ~Rank1BB << 8 * relative_rank(WHITE, s)
: ~Rank8BB >> 8 * relative_rank(BLACK, s);
}
@ -221,7 +233,7 @@ inline Bitboard forward_file_bb(Color c, Square s) {
/// starting from the given square.
inline Bitboard pawn_attack_span(Color c, Square s) {
return forward_ranks_bb(c, s) & adjacent_files_bb(file_of(s));
return forward_ranks_bb(c, s) & adjacent_files_bb(s);
}
@ -229,7 +241,7 @@ inline Bitboard pawn_attack_span(Color c, Square s) {
/// the given color and on the given square is a passed pawn.
inline Bitboard passed_pawn_span(Color c, Square s) {
return forward_ranks_bb(c, s) & (adjacent_files_bb(file_of(s)) | file_bb(s));
return forward_ranks_bb(c, s) & (adjacent_files_bb(s) | file_bb(s));
}
@ -249,23 +261,49 @@ template<> inline int distance<File>(Square x, Square y) { return std::abs(file_
template<> inline int distance<Rank>(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); }
template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; }
template<class T> constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
return v < lo ? lo : v > hi ? hi : v;
inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); }
inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); }
/// Return the target square bitboard if we do not step off the board, empty otherwise
inline Bitboard safe_destination(Square s, int step)
{
Square to = Square(s + step);
return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
}
/// attacks_bb() returns a bitboard representing all the squares attacked by a
/// piece of type Pt (bishop or rook) placed on 's'.
/// attacks_bb(Square) returns the pseudo attacks of the give piece type
/// assuming an empty board.
template<PieceType Pt>
inline Bitboard attacks_bb(Square s) {
assert((Pt != PAWN) && (is_ok(s)));
return PseudoAttacks[Pt][s];
}
/// attacks_bb(Square, Bitboard) returns the attacks by the given piece
/// assuming the board is occupied according to the passed Bitboard.
/// Sliding piece attacks do not continue passed an occupied square.
template<PieceType Pt>
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
const Magic& m = Pt == ROOK ? RookMagics[s] : BishopMagics[s];
return m.attacks[m.index(occupied)];
assert((Pt != PAWN) && (is_ok(s)));
switch (Pt)
{
case BISHOP: return BishopMagics[s].attacks[BishopMagics[s].index(occupied)];
case ROOK : return RookMagics[s].attacks[ RookMagics[s].index(occupied)];
case QUEEN : return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
default : return PseudoAttacks[Pt][s];
}
}
inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
assert(pt != PAWN);
assert((pt != PAWN) && (is_ok(s)));
switch (pt)
{
@ -370,16 +408,18 @@ inline Square msb(Bitboard b) {
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard
inline Square pop_lsb(Bitboard* b) {
assert(*b);
const Square s = lsb(*b);
*b &= *b - 1;
return s;
}
/// frontmost_sq() and backmost_sq() return the most/least advanced square in
/// the given bitboard relative to the given color.
inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); }
inline Square backmost_sq(Color c, Bitboard b) { return c == WHITE ? lsb(b) : msb(b); }
/// frontmost_sq() returns the most advanced square for the given color,
/// requires a non-zero bitboard.
inline Square frontmost_sq(Color c, Bitboard b) {
assert(b);
return c == WHITE ? msb(b) : lsb(b);
}
#endif // #ifndef BITBOARD_H_INCLUDED

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -24,42 +24,23 @@
#include "endgame.h"
#include "movegen.h"
using std::string;
namespace {
// Table used to drive the king towards the edge of the board
// Used to drive the king towards the edge of the board
// in KX vs K and KQ vs KR endgames.
constexpr int PushToEdges[SQUARE_NB] = {
100, 90, 80, 70, 70, 80, 90, 100,
90, 70, 60, 50, 50, 60, 70, 90,
80, 60, 40, 30, 30, 40, 60, 80,
70, 50, 30, 20, 20, 30, 50, 70,
70, 50, 30, 20, 20, 30, 50, 70,
80, 60, 40, 30, 30, 40, 60, 80,
90, 70, 60, 50, 50, 60, 70, 90,
100, 90, 80, 70, 70, 80, 90, 100
};
inline int push_to_edge(Square s) {
int rd = edge_distance(rank_of(s)), fd = edge_distance(file_of(s));
return 90 - (7 * fd * fd / 2 + 7 * rd * rd / 2);
}
// Table used to drive the king towards a corner square of the
// right color in KBN vs K endgames.
constexpr int PushToCorners[SQUARE_NB] = {
6400, 6080, 5760, 5440, 5120, 4800, 4480, 4160,
6080, 5760, 5440, 5120, 4800, 4480, 4160, 4480,
5760, 5440, 4960, 4480, 4480, 4000, 4480, 4800,
5440, 5120, 4480, 3840, 3520, 4480, 4800, 5120,
5120, 4800, 4480, 3520, 3840, 4480, 5120, 5440,
4800, 4480, 4000, 4480, 4480, 4960, 5440, 5760,
4480, 4160, 4480, 4800, 5120, 5440, 5760, 6080,
4160, 4480, 4800, 5120, 5440, 5760, 6080, 6400
};
// Used to drive the king towards A1H8 corners in KBN vs K endgames.
inline int push_to_corner(Square s) {
return abs(7 - rank_of(s) - file_of(s));
}
// Tables used to drive a piece towards or away from another piece
constexpr int PushClose[8] = { 0, 0, 100, 80, 60, 40, 20, 10 };
constexpr int PushAway [8] = { 0, 5, 20, 40, 60, 80, 90, 100 };
// Pawn Rank based scaling factors used in KRPPKRP endgame
constexpr int KRPPKRPScaleFactors[RANK_NB] = { 0, 9, 10, 14, 21, 44, 0, 0 };
// Drive a piece close to or away from another piece
inline int push_close(Square s1, Square s2) { return 140 - 20 * distance(s1, s2); }
inline int push_away(Square s1, Square s2) { return 120 - push_close(s1, s2); }
#ifndef NDEBUG
bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) {
@ -74,9 +55,9 @@ namespace {
assert(pos.count<PAWN>(strongSide) == 1);
if (file_of(pos.square<PAWN>(strongSide)) >= FILE_E)
sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1
sq = flip_file(sq);
return strongSide == WHITE ? sq : ~sq;
return strongSide == WHITE ? sq : flip_rank(sq);
}
} // namespace
@ -88,27 +69,26 @@ namespace Endgames {
void init() {
add<KPK>("KPK");
add<KNNK>("KNNK");
add<KBNK>("KBNK");
add<KRKP>("KRKP");
add<KRKB>("KRKB");
add<KRKN>("KRKN");
add<KQKP>("KQKP");
add<KQKR>("KQKR");
add<KNNKP>("KNNKP");
add<KPK>("KPK");
add<KNNK>("KNNK");
add<KBNK>("KBNK");
add<KRKP>("KRKP");
add<KRKB>("KRKB");
add<KRKN>("KRKN");
add<KQKP>("KQKP");
add<KQKR>("KQKR");
add<KNNKP>("KNNKP");
add<KNPK>("KNPK");
add<KNPKB>("KNPKB");
add<KRPKR>("KRPKR");
add<KRPKB>("KRPKB");
add<KBPKB>("KBPKB");
add<KBPKN>("KBPKN");
add<KBPPKB>("KBPPKB");
add<KRPPKRP>("KRPPKRP");
add<KRPKR>("KRPKR");
add<KRPKB>("KRPKB");
add<KBPKB>("KBPKB");
add<KBPKN>("KBPKN");
add<KBPPKB>("KBPPKB");
add<KRPPKRP>("KRPPKRP");
}
}
/// Mate with KX vs K. This function is used to evaluate positions with
/// king and plenty of material vs a lone king. It simply gives the
/// attacking side a bonus for driving the defending king towards the edge
@ -128,15 +108,15 @@ Value Endgame<KXK>::operator()(const Position& pos) const {
Value result = pos.non_pawn_material(strongSide)
+ pos.count<PAWN>(strongSide) * PawnValueEg
+ PushToEdges[loserKSq]
+ PushClose[distance(winnerKSq, loserKSq)];
+ push_to_edge(loserKSq)
+ push_close(winnerKSq, loserKSq);
if ( pos.count<QUEEN>(strongSide)
|| pos.count<ROOK>(strongSide)
||(pos.count<BISHOP>(strongSide) && pos.count<KNIGHT>(strongSide))
|| ( (pos.pieces(strongSide, BISHOP) & ~DarkSquares)
&& (pos.pieces(strongSide, BISHOP) & DarkSquares)))
result = std::min(result + VALUE_KNOWN_WIN, VALUE_MATE_IN_MAX_PLY - 1);
result = std::min(result + VALUE_KNOWN_WIN, VALUE_TB_WIN_IN_MAX_PLY - 1);
return strongSide == pos.side_to_move() ? result : -result;
}
@ -154,19 +134,19 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
Square loserKSq = pos.square<KING>(weakSide);
Square bishopSq = pos.square<BISHOP>(strongSide);
// If our Bishop does not attack A1/H8, we flip the enemy king square
// If our bishop does not attack A1/H8, we flip the enemy king square
// to drive to opposite corners (A8/H1).
Value result = VALUE_KNOWN_WIN
+ PushClose[distance(winnerKSq, loserKSq)]
+ PushToCorners[opposite_colors(bishopSq, SQ_A1) ? ~loserKSq : loserKSq];
Value result = (VALUE_KNOWN_WIN + 3520)
+ push_close(winnerKSq, loserKSq)
+ 420 * push_to_corner(opposite_colors(bishopSq, SQ_A1) ? flip_file(loserKSq) : loserKSq);
assert(abs(result) < VALUE_MATE_IN_MAX_PLY);
assert(abs(result) < VALUE_TB_WIN_IN_MAX_PLY);
return strongSide == pos.side_to_move() ? result : -result;
}
/// KP vs K. This endgame is evaluated with the help of a bitbase.
/// KP vs K. This endgame is evaluated with the help of a bitbase
template<>
Value Endgame<KPK>::operator()(const Position& pos) const {
@ -242,7 +222,7 @@ Value Endgame<KRKB>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 0));
assert(verify_material(pos, weakSide, BishopValueMg, 0));
Value result = Value(PushToEdges[pos.square<KING>(weakSide)]);
Value result = Value(push_to_edge(pos.square<KING>(weakSide)));
return strongSide == pos.side_to_move() ? result : -result;
}
@ -257,7 +237,7 @@ Value Endgame<KRKN>::operator()(const Position& pos) const {
Square bksq = pos.square<KING>(weakSide);
Square bnsq = pos.square<KNIGHT>(weakSide);
Value result = Value(PushToEdges[bksq] + PushAway[distance(bksq, bnsq)]);
Value result = Value(push_to_edge(bksq) + push_away(bksq, bnsq));
return strongSide == pos.side_to_move() ? result : -result;
}
@ -276,11 +256,11 @@ Value Endgame<KQKP>::operator()(const Position& pos) const {
Square loserKSq = pos.square<KING>(weakSide);
Square pawnSq = pos.square<PAWN>(weakSide);
Value result = Value(PushClose[distance(winnerKSq, loserKSq)]);
Value result = Value(push_close(winnerKSq, loserKSq));
if ( relative_rank(weakSide, pawnSq) != RANK_7
|| distance(loserKSq, pawnSq) != 1
|| !((FileABB | FileCBB | FileFBB | FileHBB) & pawnSq))
|| ((FileBBB | FileDBB | FileEBB | FileGBB) & pawnSq))
result += QueenValueEg - PawnValueEg;
return strongSide == pos.side_to_move() ? result : -result;
@ -302,23 +282,24 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
Value result = QueenValueEg
- RookValueEg
+ PushToEdges[loserKSq]
+ PushClose[distance(winnerKSq, loserKSq)];
+ push_to_edge(loserKSq)
+ push_close(winnerKSq, loserKSq);
return strongSide == pos.side_to_move() ? result : -result;
}
/// KNN vs KP. Simply push the opposing king to the corner
/// KNN vs KP. Very drawish, but there are some mate opportunities if we can
// press the weakSide King to a corner before the pawn advances too much.
template<>
Value Endgame<KNNKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
Value result = 2 * KnightValueEg
- PawnValueEg
+ PushToEdges[pos.square<KING>(weakSide)];
Value result = PawnValueEg
+ 2 * push_to_edge(pos.square<KING>(weakSide))
- 10 * relative_rank(weakSide, pos.square<PAWN>(weakSide));
return strongSide == pos.side_to_move() ? result : -result;
}
@ -341,30 +322,28 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
// No assertions about the material of weakSide, because we want draws to
// be detected even when the weaker side has some pawns.
Bitboard pawns = pos.pieces(strongSide, PAWN);
File pawnsFile = file_of(lsb(pawns));
Bitboard strongPawns = pos.pieces(strongSide, PAWN);
Bitboard allPawns = pos.pieces(PAWN);
// All pawns are on a single rook file?
if ( (pawnsFile == FILE_A || pawnsFile == FILE_H)
&& !(pawns & ~file_bb(pawnsFile)))
// All strongSide pawns are on a single rook file?
if (!(strongPawns & ~FileABB) || !(strongPawns & ~FileHBB))
{
Square bishopSq = pos.square<BISHOP>(strongSide);
Square queeningSq = relative_square(strongSide, make_square(pawnsFile, RANK_8));
Square kingSq = pos.square<KING>(weakSide);
Square queeningSq = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8));
Square weakKingSq = pos.square<KING>(weakSide);
if ( opposite_colors(queeningSq, bishopSq)
&& distance(queeningSq, kingSq) <= 1)
&& distance(queeningSq, weakKingSq) <= 1)
return SCALE_FACTOR_DRAW;
}
// If all the pawns are on the same B or G file, then it's potentially a draw
if ( (pawnsFile == FILE_B || pawnsFile == FILE_G)
&& !(pos.pieces(PAWN) & ~file_bb(pawnsFile))
if ((!(allPawns & ~FileBBB) || !(allPawns & ~FileGBB))
&& pos.non_pawn_material(weakSide) == 0
&& pos.count<PAWN>(weakSide) >= 1)
{
// Get weakSide pawn that is closest to the home rank
Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN));
// Get the least advanced weakSide pawn
Square weakPawnSq = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN));
Square strongKingSq = pos.square<KING>(strongSide);
Square weakKingSq = pos.square<KING>(weakSide);
@ -373,8 +352,8 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
// There's potential for a draw if our pawn is blocked on the 7th rank,
// the bishop cannot attack it or they only have one pawn left
if ( relative_rank(strongSide, weakPawnSq) == RANK_7
&& (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide)))
&& (opposite_colors(bishopSq, weakPawnSq) || pos.count<PAWN>(strongSide) == 1))
&& (strongPawns & (weakPawnSq + pawn_push(weakSide)))
&& (opposite_colors(bishopSq, weakPawnSq) || !more_than_one(strongPawns)))
{
int strongKingDist = distance(weakPawnSq, strongKingSq);
int weakKingDist = distance(weakPawnSq, weakKingSq);
@ -412,8 +391,8 @@ ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
&& relative_rank(weakSide, pos.square<KING>(strongSide)) >= RANK_4
&& relative_rank(weakSide, rsq) == RANK_3
&& ( pos.pieces(weakSide, PAWN)
& pos.attacks_from<KING>(kingSq)
& pos.attacks_from<PAWN>(rsq, strongSide)))
& attacks_bb<KING>(kingSq)
& pawn_attacks_bb(strongSide, rsq)))
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
@ -556,7 +535,7 @@ ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
// the corner
if ( rk == RANK_6
&& distance(psq + 2 * push, ksq) <= 1
&& (PseudoAttacks[BISHOP][bsq] & (psq + push))
&& (attacks_bb<BISHOP>(bsq) & (psq + push))
&& distance<File>(bsq, psq) >= 2)
return ScaleFactor(8);
}
@ -587,7 +566,7 @@ ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
&& relative_rank(strongSide, bksq) > r)
{
assert(r > RANK_1 && r < RANK_7);
return ScaleFactor(KRPPKRPScaleFactors[r]);
return ScaleFactor(7 * r);
}
return SCALE_FACTOR_NONE;
}
@ -605,11 +584,9 @@ ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
Square ksq = pos.square<KING>(weakSide);
Bitboard pawns = pos.pieces(strongSide, PAWN);
// If all pawns are ahead of the king, on a single rook file and
// the king is within one file of the pawns, it's a draw.
if ( !(pawns & ~forward_ranks_bb(weakSide, ksq))
&& !((pawns & ~FileABB) && (pawns & ~FileHBB))
&& distance<File>(ksq, lsb(pawns)) <= 1)
// If all pawns are ahead of the king on a single rook file, it's a draw.
if (!((pawns & ~FileABB) || (pawns & ~FileHBB)) &&
!(pawns & ~passed_pawn_span(weakSide, ksq)))
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
@ -632,8 +609,7 @@ ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
Square weakKingSq = pos.square<KING>(weakSide);
// Case 1: Defending king blocks the pawn, and cannot be driven away
if ( file_of(weakKingSq) == file_of(pawnSq)
&& relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq)
if ( (forward_file_bb(strongSide, pawnSq) & weakKingSq)
&& ( opposite_colors(weakKingSq, strongBishopSq)
|| relative_rank(strongSide, weakKingSq) <= RANK_6))
return SCALE_FACTOR_DRAW;
@ -694,14 +670,14 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
if ( ksq == blockSq1
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq2
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP))
|| (attacks_bb<BISHOP>(blockSq2, pos.pieces()) & pos.pieces(weakSide, BISHOP))
|| distance<Rank>(psq1, psq2) >= 2))
return SCALE_FACTOR_DRAW;
else if ( ksq == blockSq2
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq1
|| (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(weakSide, BISHOP))))
|| (attacks_bb<BISHOP>(blockSq1, pos.pieces()) & pos.pieces(weakSide, BISHOP))))
return SCALE_FACTOR_DRAW;
else
return SCALE_FACTOR_NONE;
@ -736,46 +712,6 @@ ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
}
/// KNP vs K. There is a single rule: if the pawn is a rook pawn on the 7th rank
/// and the defending king prevents the pawn from advancing, the position is drawn.
template<>
ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, KnightValueMg, 1));
assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
// Assume strongSide is white and the pawn is on files A-D
Square pawnSq = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Square weakKingSq = normalize(pos, strongSide, pos.square<KING>(weakSide));
if (pawnSq == SQ_A7 && distance(SQ_A8, weakKingSq) <= 1)
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
}
/// KNP vs KB. If knight can block bishop from taking pawn, it's a win.
/// Otherwise the position is drawn.
template<>
ScaleFactor Endgame<KNPKB>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, KnightValueMg, 1));
assert(verify_material(pos, weakSide, BishopValueMg, 0));
Square pawnSq = pos.square<PAWN>(strongSide);
Square bishopSq = pos.square<BISHOP>(weakSide);
Square weakKingSq = pos.square<KING>(weakSide);
// King needs to get close to promoting pawn to prevent knight from blocking.
// Rules for this are very tricky, so just approximate.
if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq))
return ScaleFactor(distance(weakKingSq, pawnSq));
return SCALE_FACTOR_NONE;
}
/// KP vs KP. This is done by removing the weakest side's pawn and probing the
/// KP vs K bitbase: If the weakest side has a draw without the pawn, it probably
/// has at least a draw with the pawn as well. The exception is when the stronger

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -48,6 +48,11 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
#include <sstream>
#include <vector>
#if defined(__linux__) && !defined(__ANDROID__)
#include <stdlib.h>
#include <sys/mman.h>
#endif
#include "misc.h"
#include "thread.h"
@ -103,6 +108,13 @@ public:
if (!fname.empty() && !l.file.is_open())
{
l.file.open(fname, ifstream::out);
if (!l.file.is_open())
{
cerr << "Unable to open debug log file " << fname << endl;
exit(EXIT_FAILURE);
}
cin.rdbuf(&l.in);
cout.rdbuf(&l.out);
}
@ -145,8 +157,79 @@ const string engine_info(bool to_uci) {
}
/// compiler_info() returns a string trying to describe the compiler we use
const std::string compiler_info() {
#define stringify2(x) #x
#define stringify(x) stringify2(x)
#define make_version_string(major, minor, patch) stringify(major) "." stringify(minor) "." stringify(patch)
/// Predefined macros hell:
///
/// __GNUC__ Compiler is gcc, Clang or Intel on Linux
/// __INTEL_COMPILER Compiler is Intel
/// _MSC_VER Compiler is MSVC or Intel on Windows
/// _WIN32 Building on Windows (any)
/// _WIN64 Building on Windows 64 bit
std::string compiler = "\nCompiled by ";
#ifdef __clang__
compiler += "clang++ ";
compiler += make_version_string(__clang_major__, __clang_minor__, __clang_patchlevel__);
#elif __INTEL_COMPILER
compiler += "Intel compiler ";
compiler += "(version ";
compiler += stringify(__INTEL_COMPILER) " update " stringify(__INTEL_COMPILER_UPDATE);
compiler += ")";
#elif _MSC_VER
compiler += "MSVC ";
compiler += "(version ";
compiler += stringify(_MSC_FULL_VER) "." stringify(_MSC_BUILD);
compiler += ")";
#elif __GNUC__
compiler += "g++ (GNUC) ";
compiler += make_version_string(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
#else
compiler += "Unknown compiler ";
compiler += "(unknown version)";
#endif
#if defined(__APPLE__)
compiler += " on Apple";
#elif defined(__CYGWIN__)
compiler += " on Cygwin";
#elif defined(__MINGW64__)
compiler += " on MinGW64";
#elif defined(__MINGW32__)
compiler += " on MinGW32";
#elif defined(__ANDROID__)
compiler += " on Android";
#elif defined(__linux__)
compiler += " on Linux";
#elif defined(_WIN64)
compiler += " on Microsoft Windows 64-bit";
#elif defined(_WIN32)
compiler += " on Microsoft Windows 32-bit";
#else
compiler += " on unknown system";
#endif
compiler += "\n __VERSION__ macro expands to: ";
#ifdef __VERSION__
compiler += __VERSION__;
#else
compiler += "(undefined macro)";
#endif
compiler += "\n";
return compiler;
}
/// Debug functions used mainly to collect run-time statistics
static int64_t hits[2], means[2];
static std::atomic<int64_t> hits[2], means[2];
void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; }
void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); }
@ -169,7 +252,7 @@ void dbg_print() {
std::ostream& operator<<(std::ostream& os, SyncCout sc) {
static Mutex m;
static std::mutex m;
if (sc == IO_LOCK)
m.lock();
@ -211,6 +294,130 @@ void prefetch(void* addr) {
#endif
/// aligned_ttmem_alloc will return suitably aligned memory, and if possible use large pages.
/// The returned pointer is the aligned one, while the mem argument is the one that needs to be passed to free.
/// With c++17 some of this functionality can be simplified.
#if defined(__linux__) && !defined(__ANDROID__)
void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page sizes
size_t size = ((allocSize + alignment - 1) / alignment) * alignment; // multiple of alignment
if (posix_memalign(&mem, alignment, size))
mem = nullptr;
madvise(mem, allocSize, MADV_HUGEPAGE);
return mem;
}
#elif defined(_WIN64)
static void* aligned_ttmem_alloc_large_pages(size_t allocSize) {
HANDLE hProcessToken { };
LUID luid { };
void* mem = nullptr;
const size_t largePageSize = GetLargePageMinimum();
if (!largePageSize)
return nullptr;
// We need SeLockMemoryPrivilege, so try to enable it for the process
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken))
return nullptr;
if (LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luid))
{
TOKEN_PRIVILEGES tp { };
TOKEN_PRIVILEGES prevTp { };
DWORD prevTpLen = 0;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds,
// we still need to query GetLastError() to ensure that the privileges were actually obtained...
if (AdjustTokenPrivileges(
hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) &&
GetLastError() == ERROR_SUCCESS)
{
// round up size to full pages and allocate
allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1);
mem = VirtualAlloc(
NULL, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
// privilege no longer needed, restore previous state
AdjustTokenPrivileges(hProcessToken, FALSE, &prevTp, 0, NULL, NULL);
}
}
CloseHandle(hProcessToken);
return mem;
}
void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
static bool firstCall = true;
// try to allocate large pages
mem = aligned_ttmem_alloc_large_pages(allocSize);
// Suppress info strings on the first call. The first call occurs before 'uci'
// is received and in that case this output confuses some GUIs.
if (!firstCall)
{
if (mem)
sync_cout << "info string Hash table allocation: Windows large pages used." << sync_endl;
else
sync_cout << "info string Hash table allocation: Windows large pages not used." << sync_endl;
}
firstCall = false;
// fall back to regular, page aligned, allocation if necessary
if (!mem)
mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
return mem;
}
#else
void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
constexpr size_t alignment = 64; // assumed cache line size
size_t size = allocSize + alignment - 1; // allocate some extra space
mem = malloc(size);
void* ret = reinterpret_cast<void*>((uintptr_t(mem) + alignment - 1) & ~uintptr_t(alignment - 1));
return ret;
}
#endif
/// aligned_ttmem_free will free the previously allocated ttmem
#if defined(_WIN64)
void aligned_ttmem_free(void* mem) {
if (mem && !VirtualFree(mem, 0, MEM_RELEASE))
{
DWORD err = GetLastError();
std::cerr << "Failed to free transposition table. Error code: 0x" <<
std::hex << err << std::dec << std::endl;
exit(EXIT_FAILURE);
}
}
#else
void aligned_ttmem_free(void *mem) {
free(mem);
}
#endif
namespace WinProcGroup {
#ifndef _WIN32

View file

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

View file

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

View file

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

View file

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

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -80,7 +80,7 @@ struct Stats<T, D, Size> : public std::array<StatsEntry<T, D>, Size> {};
/// In stats table, D=0 means that the template parameter is not used
enum StatsParams { NOT_USED = 0 };
enum StatsType { NoCaptures, Captures };
/// ButterflyHistory records how often quiet moves have been successful or
/// unsuccessful during the current search, and is used for reduction and move
@ -88,6 +88,12 @@ enum StatsParams { NOT_USED = 0 };
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
typedef Stats<int16_t, 10692, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
/// LowPlyHistory at higher depths records successful quiet moves on plies 0 to 3
/// and quiet moves which are/were in the PV (ttPv)
/// It get cleared with each new search and get filled during iterative deepening
constexpr int MAX_LPH = 4;
typedef Stats<int16_t, 10692, MAX_LPH, int(SQUARE_NB) * int(SQUARE_NB)> LowPlyHistory;
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
/// move, see www.chessprogramming.org/Countermove_Heuristic
typedef Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB> CounterMoveHistory;
@ -123,10 +129,12 @@ public:
const PieceToHistory**,
Square);
MovePicker(const Position&, Move, Depth, const ButterflyHistory*,
const LowPlyHistory*,
const CapturePieceToHistory*,
const PieceToHistory**,
Move,
Move*);
Move*,
int);
Move next_move(bool skipQuiets = false);
private:
@ -137,6 +145,7 @@ private:
const Position& pos;
const ButterflyHistory* mainHistory;
const LowPlyHistory* lowPlyHistory;
const CapturePieceToHistory* captureHistory;
const PieceToHistory** continuationHistory;
Move ttMove;
@ -145,6 +154,7 @@ private:
Square recaptureSquare;
Value threshold;
Depth depth;
int ply;
ExtMove moves[MAX_MOVES];
};

View file

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

View file

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

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -50,41 +50,6 @@ const string PieceToChar(" PNBRQK pnbrqk");
constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING };
// min_attacker() is a helper function used by see_ge() to locate the least
// valuable attacker for the side to move, remove the attacker we just found
// from the bitboards and scan for new X-ray attacks behind it.
template<int Pt>
PieceType min_attacker(const Bitboard* byTypeBB, Square to, Bitboard stmAttackers,
Bitboard& occupied, Bitboard& attackers) {
Bitboard b = stmAttackers & byTypeBB[Pt];
if (!b)
return min_attacker<Pt + 1>(byTypeBB, to, stmAttackers, occupied, attackers);
occupied ^= lsb(b); // Remove the attacker from occupied
// Add any X-ray attack behind the just removed piece. For instance with
// rooks in a8 and a7 attacking a1, after removing a7 we add rook in a8.
// Note that new added attackers can be of any color.
if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN)
attackers |= attacks_bb<BISHOP>(to, occupied) & (byTypeBB[BISHOP] | byTypeBB[QUEEN]);
if (Pt == ROOK || Pt == QUEEN)
attackers |= attacks_bb<ROOK>(to, occupied) & (byTypeBB[ROOK] | byTypeBB[QUEEN]);
// X-ray may add already processed pieces because byTypeBB[] is constant: in
// the rook example, now attackers contains _again_ rook in a7, so remove it.
attackers &= occupied;
return (PieceType)Pt;
}
template<>
PieceType min_attacker<KING>(const Bitboard*, Square, Bitboard, Bitboard&, Bitboard&) {
return KING; // No need to update bitboards: it is the last cycle
}
} // namespace
@ -174,7 +139,7 @@ void Position::init() {
for (Piece pc : Pieces)
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2)
if (PseudoAttacks[type_of(pc)][s1] & s2)
if ((type_of(pc) != PAWN) && (attacks_bb(type_of(pc), s1, 0) & s2))
{
Move move = make_move(s1, s2);
Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side;
@ -352,19 +317,18 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
void Position::set_castling_right(Color c, Square rfrom) {
Square kfrom = square<KING>(c);
CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE;
CastlingRight cr = (c | cs);
CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE: QUEEN_SIDE);
st->castlingRights |= cr;
castlingRightsMask[kfrom] |= cr;
castlingRightsMask[rfrom] |= cr;
castlingRookSquare[cr] = rfrom;
Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(c, cr & KING_SIDE ? SQ_F1 : SQ_D1);
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
& ~(square_bb(kfrom) | rfrom);
& ~(kfrom | rfrom);
}
@ -377,10 +341,10 @@ void Position::set_check_info(StateInfo* si) const {
Square ksq = square<KING>(~sideToMove);
si->checkSquares[PAWN] = attacks_from<PAWN>(ksq, ~sideToMove);
si->checkSquares[KNIGHT] = attacks_from<KNIGHT>(ksq);
si->checkSquares[BISHOP] = attacks_from<BISHOP>(ksq);
si->checkSquares[ROOK] = attacks_from<ROOK>(ksq);
si->checkSquares[PAWN] = pawn_attacks_bb(~sideToMove, ksq);
si->checkSquares[KNIGHT] = attacks_bb<KNIGHT>(ksq);
si->checkSquares[BISHOP] = attacks_bb<BISHOP>(ksq, pieces());
si->checkSquares[ROOK] = attacks_bb<ROOK>(ksq, pieces());
si->checkSquares[QUEEN] = si->checkSquares[BISHOP] | si->checkSquares[ROOK];
si->checkSquares[KING] = 0;
}
@ -409,7 +373,7 @@ void Position::set_state(StateInfo* si) const {
if (type_of(pc) == PAWN)
si->pawnKey ^= Zobrist::psq[pc][s];
else if (type_of(pc) != PAWN && type_of(pc) != KING)
else if (type_of(pc) != KING)
si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc];
}
@ -433,11 +397,13 @@ void Position::set_state(StateInfo* si) const {
Position& Position::set(const string& code, Color c, StateInfo* si) {
assert(code.length() > 0 && code.length() < 8);
assert(code[0] == 'K');
string sides[] = { code.substr(code.find('K', 1)), // Weak
code.substr(0, code.find('K', 1)) }; // Strong
code.substr(0, std::min(code.find('v'), code.find('K', 1))) }; // Strong
assert(sides[0].length() > 0 && sides[0].length() < 8);
assert(sides[1].length() > 0 && sides[1].length() < 8);
std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
@ -511,9 +477,9 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners
pinners = 0;
// Snipers are sliders that attack 's' when a piece and other snipers are removed
Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK))
| (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
Bitboard occupancy = pieces() & ~snipers;
Bitboard snipers = ( (attacks_bb< ROOK>(s) & pieces(QUEEN, ROOK))
| (attacks_bb<BISHOP>(s) & pieces(QUEEN, BISHOP))) & sliders;
Bitboard occupancy = pieces() ^ snipers;
while (snipers)
{
@ -536,12 +502,12 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners
Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
return (attacks_from<PAWN>(s, BLACK) & pieces(WHITE, PAWN))
| (attacks_from<PAWN>(s, WHITE) & pieces(BLACK, PAWN))
| (attacks_from<KNIGHT>(s) & pieces(KNIGHT))
return (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN))
| (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN))
| (attacks_bb<KNIGHT>(s) & pieces(KNIGHT))
| (attacks_bb< ROOK>(s, occupied) & pieces( ROOK, QUEEN))
| (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
| (attacks_from<KING>(s) & pieces(KING));
| (attacks_bb<KING>(s) & pieces(KING));
}
@ -644,15 +610,15 @@ bool Position::pseudo_legal(const Move m) const {
if ((Rank8BB | Rank1BB) & to)
return false;
if ( !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture
if ( !(pawn_attacks_bb(us, from) & pieces(~us) & to) // Not a capture
&& !((from + pawn_push(us) == to) && empty(to)) // Not a single push
&& !( (from + 2 * pawn_push(us) == to) // Not a double push
&& (rank_of(from) == relative_rank(us, RANK_2))
&& (relative_rank(us, from) == RANK_2)
&& empty(to)
&& empty(to - pawn_push(us))))
return false;
}
else if (!(attacks_from(type_of(pc), from) & to))
else if (!(attacks_bb(type_of(pc), from, pieces()) & to))
return false;
// Evasions generator already takes care to avoid some kind of illegal moves
@ -691,11 +657,11 @@ bool Position::gives_check(Move m) const {
Square to = to_sq(m);
// Is there a direct check?
if (st->checkSquares[type_of(piece_on(from))] & to)
if (check_squares(type_of(piece_on(from))) & to)
return true;
// Is there a discovered check?
if ( (st->blockersForKing[~sideToMove] & from)
if ( (blockers_for_king(~sideToMove) & from)
&& !aligned(from, to, square<KING>(~sideToMove)))
return true;
@ -722,11 +688,11 @@ bool Position::gives_check(Move m) const {
case CASTLING:
{
Square kfrom = from;
Square rfrom = to; // Castling is encoded as 'King captures the rook'
Square rfrom = to; // Castling is encoded as 'king captures the rook'
Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1);
Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1);
return (PseudoAttacks[ROOK][rto] & square<KING>(~sideToMove))
return (attacks_bb<ROOK>(rto) & square<KING>(~sideToMove))
&& (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
}
default:
@ -821,7 +787,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
piece_no1 = piece_no_of(capsq);
#endif // defined(EVAL_NNUE)
board[capsq] = NO_PIECE; // Not done by remove_piece()
//board[capsq] = NO_PIECE; // Not done by remove_piece()
#if defined(EVAL_NNUE)
evalList.piece_no_list_board[capsq] = PIECE_NUMBER_NB;
#endif // defined(EVAL_NNUE)
@ -843,7 +809,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
}
// Update board and piece lists
remove_piece(captured, capsq);
remove_piece(capsq);
if (type_of(m) == ENPASSANT)
board[capsq] = NO_PIECE;
// Update material hash key and prefetch access to materialTable
k ^= Zobrist::psq[captured][capsq];
@ -893,7 +862,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
piece_no0 = piece_no_of(from);
#endif // defined(EVAL_NNUE)
move_piece(pc, from, to);
move_piece(from, to);
#if defined(EVAL_NNUE)
dp.pieceNo[0] = piece_no0;
@ -909,7 +878,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
{
// Set en-passant square if the moved pawn can be captured
if ( (int(to) ^ int(from)) == 16
&& (attacks_from<PAWN>(to - pawn_push(us), us) & pieces(them, PAWN)))
&& (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN)))
{
st->epSquare = to - pawn_push(us);
k ^= Zobrist::enpassant[file_of(st->epSquare)];
@ -922,7 +891,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
assert(relative_rank(us, to) == RANK_8);
assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
remove_piece(pc, to);
remove_piece(to);
put_piece(promotion, to);
#if defined(EVAL_NNUE)
@ -944,7 +913,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
st->nonPawnMaterial[us] += PieceValue[MG][promotion];
}
// Update pawn hash key and prefetch access to pawnsTable
// Update pawn hash key
st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
// Reset rule 50 draw counter
@ -973,7 +942,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
if (end >= 4)
{
StateInfo* stp = st->previous->previous;
for (int i=4; i <= end; i += 2)
for (int i = 4; i <= end; i += 2)
{
stp = stp->previous->previous;
if (stp->key == st->key)
@ -1016,7 +985,7 @@ void Position::undo_move(Move m) {
assert(type_of(pc) == promotion_type(m));
assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN);
remove_piece(pc, to);
remove_piece(to);
pc = make_piece(us, PAWN);
put_piece(pc, to);
@ -1034,7 +1003,7 @@ void Position::undo_move(Move m) {
else
{
move_piece(pc, to, from); // Put the piece back at the source square
move_piece(to, from); // Put the piece back at the source square
#if defined(EVAL_NNUE)
PieceNumber piece_no0 = st->dirtyPiece.pieceNo[0];
@ -1110,9 +1079,9 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ
#endif // defined(EVAL_NNUE)
// Remove both pieces first since squares could overlap in Chess960
remove_piece(make_piece(us, KING), Do ? from : to);
remove_piece(make_piece(us, ROOK), Do ? rfrom : rto);
board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us
remove_piece(Do ? from : to);
remove_piece(Do ? rfrom : rto);
board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do this for us
put_piece(make_piece(us, KING), Do ? to : from);
put_piece(make_piece(us, ROOK), Do ? rto : rfrom);
@ -1140,7 +1109,7 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ
}
/// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips
/// Position::do(undo)_null_move() is used to do(undo) a "null move": it flips
/// the side to move without executing any move on the board.
void Position::do_null_move(StateInfo& newSt) {
@ -1217,77 +1186,96 @@ bool Position::see_ge(Move m, Value threshold) const {
if (type_of(m) != NORMAL)
return VALUE_ZERO >= threshold;
Bitboard stmAttackers;
Square from = from_sq(m), to = to_sq(m);
PieceType nextVictim = type_of(piece_on(from));
Color us = color_of(piece_on(from));
Color stm = ~us; // First consider opponent's move
Value balance; // Values of the pieces taken by us minus opponent's ones
// The opponent may be able to recapture so this is the best result
// we can hope for.
balance = PieceValue[MG][piece_on(to)] - threshold;
if (balance < VALUE_ZERO)
int swap = PieceValue[MG][piece_on(to)] - threshold;
if (swap < 0)
return false;
// Now assume the worst possible result: that the opponent can
// capture our piece for free.
balance -= PieceValue[MG][nextVictim];
// If it is enough (like in PxQ) then return immediately. Note that
// in case nextVictim == KING we always return here, this is ok
// if the given move is legal.
if (balance >= VALUE_ZERO)
swap = PieceValue[MG][piece_on(from)] - swap;
if (swap <= 0)
return true;
// Find all attackers to the destination square, with the moving piece
// removed, but possibly an X-ray attacker added behind it.
Bitboard occupied = pieces() ^ from ^ to;
Bitboard attackers = attackers_to(to, occupied) & occupied;
Color stm = color_of(piece_on(from));
Bitboard attackers = attackers_to(to, occupied);
Bitboard stmAttackers, bb;
int res = 1;
while (true)
{
stmAttackers = attackers & pieces(stm);
stm = ~stm;
attackers &= occupied;
// If stm has no more attackers then give up: stm loses
if (!(stmAttackers = attackers & pieces(stm)))
break;
// Don't allow pinned pieces to attack (except the king) as long as
// any pinners are on their original square.
// there are pinners on their original square.
if (st->pinners[~stm] & occupied)
stmAttackers &= ~st->blockersForKing[stm];
// If stm has no more attackers then give up: stm loses
if (!stmAttackers)
break;
res ^= 1;
// Locate and remove the next least valuable attacker, and add to
// the bitboard 'attackers' the possibly X-ray attackers behind it.
nextVictim = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
stm = ~stm; // Switch side to move
// Negamax the balance with alpha = balance, beta = balance+1 and
// add nextVictim's value.
//
// (balance, balance+1) -> (-balance-1, -balance)
//
assert(balance < VALUE_ZERO);
balance = -balance - 1 - PieceValue[MG][nextVictim];
// If balance is still non-negative after giving away nextVictim then we
// win. The only thing to be careful about it is that we should revert
// stm if we captured with the king when the opponent still has attackers.
if (balance >= VALUE_ZERO)
// the bitboard 'attackers' any X-ray attackers behind it.
if ((bb = stmAttackers & pieces(PAWN)))
{
if (nextVictim == KING && (attackers & pieces(stm)))
stm = ~stm;
break;
}
assert(nextVictim != KING);
}
return us != stm; // We break the above loop when stm loses
}
if ((swap = PawnValueMg - swap) < res)
break;
occupied ^= lsb(bb);
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
}
else if ((bb = stmAttackers & pieces(KNIGHT)))
{
if ((swap = KnightValueMg - swap) < res)
break;
occupied ^= lsb(bb);
}
else if ((bb = stmAttackers & pieces(BISHOP)))
{
if ((swap = BishopValueMg - swap) < res)
break;
occupied ^= lsb(bb);
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
}
else if ((bb = stmAttackers & pieces(ROOK)))
{
if ((swap = RookValueMg - swap) < res)
break;
occupied ^= lsb(bb);
attackers |= attacks_bb<ROOK>(to, occupied) & pieces(ROOK, QUEEN);
}
else if ((bb = stmAttackers & pieces(QUEEN)))
{
if ((swap = QueenValueMg - swap) < res)
break;
occupied ^= lsb(bb);
attackers |= (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN))
| (attacks_bb<ROOK >(to, occupied) & pieces(ROOK , QUEEN));
}
else // KING
// If we "capture" with the king but opponent still has attackers,
// reverse the result.
return (attackers & ~pieces(stm)) ? res ^ 1 : res;
}
return bool(res);
}
/// Position::is_draw() tests whether the position is drawn by 50-move rule
/// or by repetition. It does not detect stalemates.
@ -1299,10 +1287,7 @@ bool Position::is_draw(int ply) const {
// Return a draw score if a position repeats once earlier but strictly
// after the root, or repeats twice before or at the root.
if (st->repetition && st->repetition < ply)
return true;
return false;
return st->repetition && st->repetition < ply;
}
@ -1356,10 +1341,10 @@ bool Position::has_game_cycle(int ply) const {
if (ply > i)
return true;
// For nodes before or at the root, check that the move is a repetition one
// rather than a move to the current position.
// In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in the same
// location, so we have to select which square to check.
// For nodes before or at the root, check that the move is a
// repetition rather than a move to the current position.
// In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in
// the same location, so we have to select which square to check.
if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move())
continue;
@ -1463,15 +1448,15 @@ bool Position::pos_is_ok() const {
assert(0 && "pos_is_ok: Index");
}
for (Color c = WHITE; c <= BLACK; ++c)
for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
for (Color c : { WHITE, BLACK })
for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE})
{
if (!can_castle(c | s))
if (!can_castle(cr))
continue;
if ( piece_on(castlingRookSquare[c | s]) != make_piece(c, ROOK)
|| castlingRightsMask[castlingRookSquare[c | s]] != (c | s)
|| (castlingRightsMask[square<KING>(c)] & (c | s)) != (c | s))
if ( piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK)
|| castlingRightsMask[castlingRookSquare[cr]] != cr
|| (castlingRightsMask[square<KING>(c)] & cr) != cr)
assert(0 && "pos_is_ok: Castling");
}

View file

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

View file

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

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

View file

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

View file

@ -1,7 +1,7 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (c) 2013 Ronald de Man
Copyright (C) 2016-2019 Marco Costalba, Lucas Braesch
Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View file

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

View file

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

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,63 +21,19 @@
#ifndef THREAD_WIN32_OSX_H_INCLUDED
#define THREAD_WIN32_OSX_H_INCLUDED
/// STL thread library used by mingw and gcc when cross compiling for Windows
/// relies on libwinpthread. Currently libwinpthread implements mutexes directly
/// on top of Windows semaphores. Semaphores, being kernel objects, require kernel
/// mode transition in order to lock or unlock, which is very slow compared to
/// interlocked operations (about 30% slower on bench test). To work around this
/// issue, we define our wrappers to the low level Win32 calls. We use critical
/// sections to support Windows XP and older versions. Unfortunately, cond_wait()
/// is racy between unlock() and WaitForSingleObject() but they have the same
/// speed performance as the SRW locks.
#include <condition_variable>
#include <mutex>
#include <thread>
#if defined(_WIN32) && !defined(_MSC_VER)
#ifndef NOMINMAX
# define NOMINMAX // Disable macros min() and max()
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
/// Mutex and ConditionVariable struct are wrappers of the low level locking
/// machinery and are modeled after the corresponding C++11 classes.
struct Mutex {
Mutex() { InitializeCriticalSection(&cs); }
~Mutex() { DeleteCriticalSection(&cs); }
void lock() { EnterCriticalSection(&cs); }
void unlock() { LeaveCriticalSection(&cs); }
private:
CRITICAL_SECTION cs;
};
typedef std::condition_variable_any ConditionVariable;
#else // Default case: use STL classes
typedef std::mutex Mutex;
typedef std::condition_variable ConditionVariable;
#endif
/// On OSX threads other than the main thread are created with a reduced stack
/// size of 512KB by default, this is dangerously low for deep searches, so
/// adjust it to TH_STACK_SIZE. The implementation calls pthread_create() with
/// proper stack size parameter.
/// size of 512KB by default, this is too low for deep searches, which require
/// somewhat more than 1MB stack, so adjust it to TH_STACK_SIZE.
/// The implementation calls pthread_create() with the stack size parameter
/// equal to the linux 8MB default, on platforms that support it.
#if defined(__APPLE__)
#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__)
#include <pthread.h>
static const size_t TH_STACK_SIZE = 2 * 1024 * 1024;
static const size_t TH_STACK_SIZE = 8 * 1024 * 1024;
template <class T, class P = std::pair<T*, void(T::*)()>>
void* start_routine(void* ptr)

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -28,66 +28,21 @@
TimeManagement Time; // Our global time management object
namespace {
enum TimeType { OptimumTime, MaxTime };
constexpr int MoveHorizon = 50; // Plan time management at most this many moves ahead
constexpr double MaxRatio = 7.3; // When in trouble, we can step over reserved time with this ratio
constexpr double StealRatio = 0.34; // However we must not steal time from remaining moves over this ratio
// move_importance() is a skew-logistic function based on naive statistical
// analysis of "how many games are still undecided after n half-moves". Game
// is considered "undecided" as long as neither side has >275cp advantage.
// Data was extracted from the CCRL game database with some simple filtering criteria.
double move_importance(int ply) {
constexpr double XScale = 6.85;
constexpr double XShift = 64.5;
constexpr double Skew = 0.171;
return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero
}
template<TimeType T>
TimePoint remaining(TimePoint myTime, int movesToGo, int ply, TimePoint slowMover) {
constexpr double TMaxRatio = (T == OptimumTime ? 1.0 : MaxRatio);
constexpr double TStealRatio = (T == OptimumTime ? 0.0 : StealRatio);
double moveImportance = (move_importance(ply) * slowMover) / 100.0;
double otherMovesImportance = 0.0;
for (int i = 1; i < movesToGo; ++i)
otherMovesImportance += move_importance(ply + 2 * i);
double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance);
double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance);
return TimePoint(myTime * std::min(ratio1, ratio2)); // Intel C++ asks for an explicit cast
}
} // namespace
/// init() is called at the beginning of the search and calculates the allowed
/// thinking time out of the time control and current game ply. We support four
/// different kinds of time controls, passed in 'limits':
///
/// inc == 0 && movestogo == 0 means: x basetime [sudden death!]
/// inc == 0 && movestogo != 0 means: x moves in y minutes
/// inc > 0 && movestogo == 0 means: x basetime + z increment
/// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment
/// init() is called at the beginning of the search and calculates the bounds
/// of time allowed for the current game ply. We currently support:
// 1) x basetime (+z increment)
// 2) x moves in y seconds (+z increment)
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
TimePoint minThinkingTime = Options["Minimum Thinking Time"];
TimePoint moveOverhead = Options["Move Overhead"];
TimePoint slowMover = Options["Slow Mover"];
TimePoint npmsec = Options["nodestime"];
TimePoint hypMyTime;
TimePoint minThinkingTime = TimePoint(Options["Minimum Thinking Time"]);
TimePoint moveOverhead = TimePoint(Options["Move Overhead"]);
TimePoint slowMover = TimePoint(Options["Slow Mover"]);
TimePoint npmsec = TimePoint(Options["nodestime"]);
// opt_scale is a percentage of available time to use for the current move.
// max_scale is a multiplier applied to optimumTime.
double opt_scale, max_scale;
// If we have to play in 'nodes as time' mode, then convert from time
// to nodes, and use resulting values in time management formulas.
@ -105,29 +60,40 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
}
startTime = limits.startTime;
optimumTime = maximumTime = std::max(limits.time[us], minThinkingTime);
const int maxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon;
//Maximum move horizon of 50 moves
int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50;
// We calculate optimum time usage for different hypothetical "moves to go" values
// and choose the minimum of calculated search time values. Usually the greatest
// hypMTG gives the minimum values.
for (int hypMTG = 1; hypMTG <= maxMTG; ++hypMTG)
// Make sure timeLeft is > 0 since we may use it as a divisor
TimePoint timeLeft = std::max(TimePoint(1),
limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg));
// A user may scale time usage by setting UCI option "Slow Mover"
// Default is 100 and changing this value will probably lose elo.
timeLeft = slowMover * timeLeft / 100;
// x basetime (+ z increment)
// If there is a healthy increment, timeLeft can exceed actual available
// game time for the current move, so also cap to 20% of available game time.
if (limits.movestogo == 0)
{
// Calculate thinking time for hypothetical "moves to go"-value
hypMyTime = limits.time[us]
+ limits.inc[us] * (hypMTG - 1)
- moveOverhead * (2 + std::min(hypMTG, 40));
hypMyTime = std::max(hypMyTime, TimePoint(0));
TimePoint t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, ply, slowMover);
TimePoint t2 = minThinkingTime + remaining<MaxTime >(hypMyTime, hypMTG, ply, slowMover);
optimumTime = std::min(t1, optimumTime);
maximumTime = std::min(t2, maximumTime);
opt_scale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0,
0.2 * limits.time[us] / double(timeLeft));
max_scale = 4 + std::min(36, ply) / 12.0;
}
// x moves in y seconds (+ z increment)
else
{
opt_scale = std::min((0.8 + ply / 128.0) / mtg,
0.8 * limits.time[us] / double(timeLeft));
max_scale = std::min(6.3, 1.5 + 0.11 * mtg);
}
// Never use more than 80% of the available time for this move
optimumTime = std::max(minThinkingTime, TimePoint(opt_scale * timeLeft));
maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, max_scale * optimumTime));
if (Options["Ponder"])
optimumTime += optimumTime / 4;
}

View file

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

View file

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

View file

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

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

View file

@ -2,7 +2,7 @@
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -154,7 +154,7 @@ namespace {
limits.startTime = now(); // As early as possible!
while (is >> token)
if (token == "searchmoves")
if (token == "searchmoves") // Needs to be the last command on the line
while (is >> token)
limits.searchmoves.push_back(UCI::to_move(pos, token));
@ -185,7 +185,7 @@ namespace {
uint64_t num, nodes = 0, cnt = 1;
vector<string> list = setup_bench(pos, args);
num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0; });
num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0 || s.find("eval") == 0; });
TimePoint elapsed = now();
@ -194,16 +194,21 @@ namespace {
istringstream is(cmd);
is >> skipws >> token;
if (token == "go")
if (token == "go" || token == "eval")
{
cerr << "\nPosition: " << cnt++ << '/' << num << endl;
go(pos, is, states);
Threads.main()->wait_for_search_finished();
nodes += Threads.nodes_searched();
if (token == "go")
{
go(pos, is, states);
Threads.main()->wait_for_search_finished();
nodes += Threads.nodes_searched();
}
else
sync_cout << "\n" << Eval::trace(pos) << sync_endl;
}
else if (token == "setoption") setoption(is);
else if (token == "position") position(pos, is, states);
else if (token == "ucinewgame") Search::clear();
else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } // Search::clear() may take some while
}
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
@ -337,9 +342,8 @@ void UCI::loop(int argc, char* argv[]) {
Position pos;
string token, cmd;
StateListPtr states(new std::deque<StateInfo>(1));
auto uiThread = std::make_shared<Thread>(0);
pos.set(StartFEN, false, &states->back(), uiThread.get());
pos.set(StartFEN, false, &states->back(), Threads.main());
for (int i = 1; i < argc; ++i)
cmd += std::string(argv[i]) + " ";
@ -375,11 +379,13 @@ void UCI::loop(int argc, char* argv[]) {
else if (token == "ucinewgame") Search::clear();
else if (token == "isready") is_ready();
// Additional custom non-UCI commands, mainly for debugging
else if (token == "flip") pos.flip();
else if (token == "bench") bench(pos, is, states);
else if (token == "d") sync_cout << pos << sync_endl;
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
// Additional custom non-UCI commands, mainly for debugging.
// Do not use these commands during a search!
else if (token == "flip") pos.flip();
else if (token == "bench") bench(pos, is, states);
else if (token == "d") sync_cout << pos << sync_endl;
else if (token == "eval") sync_cout << Eval::trace(pos) << sync_endl;
else if (token == "compiler") sync_cout << compiler_info() << sync_endl;
#if defined (EVAL_LEARN)
else if (token == "gensfen") Learner::gen_sfen(pos, is);
else if (token == "learn") Learner::learn(pos, is);
@ -398,7 +404,6 @@ void UCI::loop(int argc, char* argv[]) {
// ƒeƒXƒgƒRƒ}ƒ“ƒh
else if (token == "test") test_cmd(pos, is);
#endif
else
sync_cout << "Unknown command: " << cmd << sync_endl;
@ -419,7 +424,7 @@ string UCI::value(Value v) {
stringstream ss;
if (abs(v) < VALUE_MATE - MAX_PLY)
if (abs(v) < VALUE_MATE_IN_MAX_PLY)
ss << "cp " << v * 100 / PawnValueEg;
else
ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;

View file

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

View file

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