177 changes: 126 additions & 51 deletions Readme.md
@@ -1,66 +1,123 @@
### Overview
## 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 is a free UCI chess engine derived from Glaurung 2.1. It is
not a complete chess program and requires some UCI-compatible GUI
(e.g. XBoard with PolyGlot, eboard, Arena, Sigma Chess, Shredder, Chess
Partner or Fritz) in order to be used comfortably. Read the
documentation for your GUI of choice for information about how to use
[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
UCI-compatible GUI (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard, Arena,
Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably.
Read the documentation for your GUI of choice for information about how to use
Stockfish with it.

This version of Stockfish supports up to 512 cores. The engine defaults
to one search thread, so it is therefore recommended to inspect the value of
the *Threads* UCI parameter, and to make sure it equals the number of CPU
cores on your computer.

This version of Stockfish has support for Syzygybases.


### Files
## Files

This distribution of Stockfish consists of the following files:

* Readme.md, the file you are currently reading.

* Copying.txt, a text file containing the GNU General Public License.
* Copying.txt, a text file containing the GNU General Public License version 3.

* src, a subdirectory containing the full source code, including a Makefile
that can be used to compile Stockfish on Unix-like systems.


### Syzygybases
## UCI parameters

Currently, Stockfish has the following UCI options:

* #### Debug Log File
Write all communication to and from the engine into a text file.

* #### Contempt
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"
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
this equal to the number of CPU cores available.

* #### Hash
The size of the hash table in MB.

* #### Clear Hash
Clear the hash table.

* #### Ponder
Let Stockfish ponder its next move while the opponent is thinking.

* #### MultiPV
Output the N best lines (principal variations, PVs) when searching.
Leave at 1 for best performance.

* #### Skill Level
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.

**Configuration**
* #### Move Overhead
Assume a time delay of x ms due to network and GUI overheads. This is useful to
avoid losses on time in those cases.

Syzygybases are configured using the UCI options "SyzygyPath",
"SyzygyProbeDepth", "Syzygy50MoveRule" and "SyzygyProbeLimit".
* #### Minimum Thinking Time
Search for at least x ms per move.

The option "SyzygyPath" should be set to the directory or directories that
contain the .rtbw and .rtbz files. Multiple directories should be
separated by ";" on Windows and by ":" on Unix-based operating systems.
**Do not use spaces around the ";" or ":".**
* #### Slow Mover
Lower values will make Stockfish take less time in games, higher values will
make it think longer.

Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6`
* #### nodestime
Tells the engine to use nodes searched instead of wall time to account for
elapsed time. Useful for engine testing.

It is recommended to store .rtbw files on an SSD. There is no loss in
storing the .rtbz files on a regular HD.
* #### UCI_Chess960
An option handled by your GUI. If true, Stockfish will play Chess960.

Increasing the "SyzygyProbeDepth" option lets the engine probe less
aggressively. Set this option to a higher value if you experience too much
slowdown (in terms of nps) due to TB probing.
* #### UCI_AnalyseMode
An option handled by your GUI.

Set the "Syzygy50MoveRule" option to false if you want tablebase positions
that are drawn by the 50-move rule to count as win or loss. This may be useful
for correspondence games (because of tablebase adjudication).
* #### 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
operating systems. Do not use spaces around the ";" or ":".

The "SyzygyProbeLimit" option should normally be left at its default value.
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
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.

* #### SyzygyProbeDepth
Minimum remaining search depth for which a position is probed. Set this option
to a higher value to probe less agressively if you experience too much slowdown
(in terms of nps) due to TB probing.

* #### Syzygy50MoveRule
Disable to let fifty-move rule draws detected by Syzygy tablebase probes count
as wins or losses. This is useful for ICCF correspondence games.

* #### SyzygyProbeLimit
Limit Syzygy tablebase probing to positions with at most this many pieces left
(including kings and pawns).


## What to expect from Syzygybases?

**What to expect**
If the engine is searching a position that is not in the tablebases (e.g.
a position with 8 pieces), it will access the tablebases during the search.
If the engine reports a very large score (typically 123.xx), this means
If the engine reports a very large score (typically 153.xx), this means
that it has found a winning line into a tablebase position.

If the engine is given a position to search that is in the tablebases, it
Expand All @@ -71,7 +128,7 @@ It will then perform a search only on those moves. **The engine will not move
immediately**, unless there is only a single good move. **The engine likely
will not report a mate score even if the position is known to be won.**

It is therefore clear that behaviour is not identical to what one might
It is therefore clear that this behaviour is not identical to what one might
be used to with Nalimov tablebases. There are technical reasons for this
difference, the main technical reason being that Nalimov tablebases use the
DTM metric (distance-to-mate), while Syzygybases use a variation of the
Expand All @@ -82,7 +139,7 @@ needed for optimal play and in addition being able to take into account
the 50-move rule.


### Compiling it yourself
## Compiling Stockfish yourself from the sources

On Unix-like systems, it should be possible to compile Stockfish
directly from the source code with the included Makefile.
Expand All @@ -96,23 +153,41 @@ 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.

### Resource For Understanding the Code Base

* [Chess Programming Wiki](https://www.chessprogramming.org/Main_Page)
has good overall chess engines explanations
(techniques used here are well explained like hash maps etc), it was
also recommended by the [support team at stockfish.](http://support.stockfishchess.org/discussions/questions/1132-how-to-understand-stockfish-sources)
## Understanding the code base and participating in the project

Stockfish's improvement over the last couple of years has been a great
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:-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 resources:

* [In this wiki,](https://www.chessprogramming.org) many techniques used in
Stockfish are explained with a lot of background information.

* [The section on Stockfish](https://www.chessprogramming.org/Stockfish)
describes many features and techniques used by Stockfish. However, it is
generic rather than being focused on Stockfish's precise implementation.
Nevertheless, a helpful resource.

* [Here](https://www.chessprogramming.org/Stockfish) you can find a set
of features and techniques used by Stockfish and each of them is explained
at the wiki, however, it's a generic way rather than focusing on Stockfish's
own implementation, but it will still help you.
* 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](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.


### Terms of use
## Terms of use

Stockfish is free, and distributed under the **GNU General Public License**
(GPL). Essentially, this means that you are free to do almost exactly
Stockfish is free, and distributed under the **GNU General Public License version 3**
(GPL v3). Essentially, this means that you are free to do almost exactly
what you want with the program, including distributing it among your
friends, making it available for download from your web site, selling
it (either by itself or as part of some bigger software package), or
Expand All @@ -123,5 +198,5 @@ some way, you must always include the full source code, or a pointer
to where the source code can be found. If you make any changes to the
source code, these changes must also be made available under the GPL.

For full details, read the copy of the GPL found in the file named
For full details, read the copy of the GPL v3 found in the file named
*Copying.txt*.
296 changes: 152 additions & 144 deletions Top CPU Contributors.txt
@@ -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
4 changes: 2 additions & 2 deletions appveyor.yml
Expand Up @@ -7,7 +7,7 @@ branches:
- appveyor

# Operating system (build VM template)
os: Visual Studio 2015
os: Visual Studio 2017

# Build platform, i.e. x86, x64, AnyCPU. This setting is optional.
platform:
Expand Down Expand Up @@ -51,7 +51,7 @@ before_build:
$b = git log HEAD | sls "\b[Bb]ench[ :]+[0-9]{7}" | select -first 1
$bench = $b -match '\D+(\d+)' | % { $matches[1] }
Write-Host "Reference bench:" $bench
$g = "Visual Studio 14 2015"
$g = "Visual Studio 15 2017"
If (${env:PLATFORM} -eq 'x64') { $g = $g + ' Win64' }
cmake -G "${g}" .
Write-Host "Generated files for: " $g
Expand Down
22 changes: 13 additions & 9 deletions src/Makefile
Expand Up @@ -136,6 +136,8 @@ endif
ifeq ($(ARCH),ppc-64)
arch = ppc64
bits = 64
popcnt = yes
prefetch = yes
endif


Expand Down Expand Up @@ -288,7 +290,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
Expand All @@ -313,7 +315,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
Expand All @@ -324,7 +328,7 @@ endif
ifeq ($(pext),yes)
CXXFLAGS += -DUSE_PEXT
ifeq ($(comp),$(filter $(comp),gcc clang mingw))
CXXFLAGS += -mbmi2
CXXFLAGS += -msse4 -mbmi2
endif
endif

Expand Down Expand Up @@ -375,10 +379,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"
Expand All @@ -401,7 +405,7 @@ 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 ""


Expand Down Expand Up @@ -481,7 +485,7 @@ config-sanity:
@echo "Testing config sanity. If this fails, try 'make help' ..."
@echo ""
@test "$(debug)" = "yes" || test "$(debug)" = "no"
@test "$(sanitize)" = "undefined" || test "$(sanitize)" = "thread" || test "$(sanitize)" = "no"
@test "$(sanitize)" = "undefined" || test "$(sanitize)" = "thread" || test "$(sanitize)" = "address" || test "$(sanitize)" = "no"
@test "$(optimize)" = "yes" || test "$(optimize)" = "no"
@test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "armv7"
Expand Down
10 changes: 7 additions & 3 deletions src/benchmark.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -61,6 +61,10 @@ 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",

// 5-man positions
"8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate
Expand Down Expand Up @@ -113,7 +117,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;
Expand All @@ -139,9 +143,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)
Expand Down
8 changes: 4 additions & 4 deletions src/bitbase.cpp
Expand Up @@ -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
Expand All @@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <algorithm>
#include <cassert>
#include <numeric>
#include <vector>
Expand All @@ -28,7 +27,8 @@

namespace {

// There are 24 possible pawn squares: the first 4 files and ranks from 2 to 7
// There are 24 possible pawn squares: files A to D and ranks from 2 to 7.
// 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
Expand All @@ -44,7 +44,7 @@ namespace {
// 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);
return int(wksq) | (bksq << 6) | (us << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15);
}

enum Result {
Expand Down
100 changes: 29 additions & 71 deletions src/bitboard.cpp
Expand Up @@ -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
Expand All @@ -19,24 +19,16 @@
*/

#include <algorithm>
#include <bitset>

#include "bitboard.h"
#include "misc.h"

uint8_t PopCnt16[1 << 16];
int SquareDistance[SQUARE_NB][SQUARE_NB];
uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];

Bitboard SquareBB[SQUARE_NB];
Bitboard FileBB[FILE_NB];
Bitboard RankBB[RANK_NB];
Bitboard AdjacentFilesBB[FILE_NB];
Bitboard ForwardRanksBB[COLOR_NB][RANK_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
Bitboard DistanceRingBB[SQUARE_NB][8];
Bitboard ForwardFileBB[COLOR_NB][SQUARE_NB];
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];

Expand All @@ -49,15 +41,6 @@ namespace {
Bitboard BishopTable[0x1480]; // To store bishop attacks

void init_magics(Bitboard table[], Magic magics[], Direction directions[]);

// popcount16() counts the non-zero bits using SWAR-Popcount algorithm

unsigned popcount16(unsigned u) {
u -= (u >> 1) & 0x5555U;
u = ((u >> 2) & 0x3333U) + (u & 0x3333U);
u = ((u >> 4) + u) & 0x0F0FU;
return (u * 0x0101U) >> 8;
}
}


Expand Down Expand Up @@ -86,56 +69,36 @@ const std::string Bitboards::pretty(Bitboard b) {
void Bitboards::init() {

for (unsigned i = 0; i < (1 << 16); ++i)
PopCnt16[i] = (uint8_t) popcount16(i);
PopCnt16[i] = std::bitset<16>(i).count();

for (Square s = SQ_A1; s <= SQ_H8; ++s)
SquareBB[s] = (1ULL << s);

for (File f = FILE_A; f <= FILE_H; ++f)
FileBB[f] = f > FILE_A ? FileBB[f - 1] << 1 : FileABB;

for (Rank r = RANK_1; r <= RANK_8; ++r)
RankBB[r] = r > RANK_1 ? RankBB[r - 1] << 8 : Rank1BB;
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));

for (File f = FILE_A; f <= FILE_H; ++f)
AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
PawnAttacks[WHITE][s] = pawn_attacks_bb<WHITE>(square_bb(s));
PawnAttacks[BLACK][s] = pawn_attacks_bb<BLACK>(square_bb(s));
}

for (Rank r = RANK_1; r < RANK_8; ++r)
ForwardRanksBB[WHITE][r] = ~(ForwardRanksBB[BLACK][r + 1] = ForwardRanksBB[BLACK][r] | RankBB[r]);
// Helper returning the target bitboard of a step from a square
auto landing_square_bb = [&](Square s, int step)
{
Square to = Square(s + step);
return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
};

for (Color c = WHITE; c <= BLACK; ++c)
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
ForwardFileBB [c][s] = ForwardRanksBB[c][rank_of(s)] & FileBB[file_of(s)];
PawnAttackSpan[c][s] = ForwardRanksBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)];
PassedPawnMask[c][s] = ForwardFileBB [c][s] | PawnAttackSpan[c][s];
}
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
for (int step : {-9, -8, -7, -1, 1, 7, 8, 9} )
PseudoAttacks[KING][s] |= landing_square_bb(s, step);

for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
if (s1 != s2)
{
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
DistanceRingBB[s1][SquareDistance[s1][s2]] |= 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;
}
}
for (int step : {-17, -15, -10, -6, 6, 10, 15, 17} )
PseudoAttacks[KNIGHT][s] |= landing_square_bb(s, step);
}

Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST };
Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
Expand All @@ -150,13 +113,8 @@ void Bitboards::init() {

for (PieceType pt : { BISHOP, ROOK })
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
{
if (!(PseudoAttacks[pt][s1] & s2))
continue;

LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
BetweenBB[s1][s2] = attacks_bb(pt, s1, SquareBB[s2]) & attacks_bb(pt, s2, SquareBB[s1]);
}
if (PseudoAttacks[pt][s1] & s2)
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
}
}

Expand Down Expand Up @@ -184,8 +142,8 @@ namespace {

// init_magics() computes all rook and bishop attacks at startup. Magic
// bitboards are used to look up attacks of sliding pieces. As a reference see
// chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
// use the so called "fancy" approach.
// www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so
// called "fancy" approach.

void init_magics(Bitboard table[], Magic magics[], Direction directions[]) {

Expand Down
154 changes: 80 additions & 74 deletions src/bitboard.h
Expand Up @@ -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
Expand Down Expand Up @@ -60,19 +60,22 @@ constexpr Bitboard Rank6BB = Rank1BB << (8 * 5);
constexpr Bitboard Rank7BB = Rank1BB << (8 * 6);
constexpr Bitboard Rank8BB = Rank1BB << (8 * 7);

extern int SquareDistance[SQUARE_NB][SQUARE_NB];
constexpr Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB;
constexpr Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB;
constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB);

constexpr Bitboard KingFlank[FILE_NB] = {
QueenSide ^ FileDBB, QueenSide, QueenSide,
CenterFiles, CenterFiles,
KingSide, KingSide, KingSide ^ FileEBB
};

extern uint8_t PopCnt16[1 << 16];
extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];

extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard FileBB[FILE_NB];
extern Bitboard RankBB[RANK_NB];
extern Bitboard AdjacentFilesBB[FILE_NB];
extern Bitboard ForwardRanksBB[COLOR_NB][RANK_NB];
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
extern Bitboard DistanceRingBB[SQUARE_NB][8];
extern Bitboard ForwardFileBB[COLOR_NB][SQUARE_NB];
extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];

Expand Down Expand Up @@ -102,73 +105,70 @@ struct Magic {
extern Magic RookMagics[SQUARE_NB];
extern Magic BishopMagics[SQUARE_NB];

inline Bitboard square_bb(Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return SquareBB[s];
}

/// Overloads of bitwise operators between a Bitboard and a Square for testing
/// whether a given bit is set in a bitboard, and for setting and clearing bits.

inline Bitboard operator&(Bitboard b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b & SquareBB[s];
}
inline Bitboard operator&( Bitboard b, Square s) { return b & square_bb(s); }
inline Bitboard operator|( Bitboard b, Square s) { return b | square_bb(s); }
inline Bitboard operator^( Bitboard b, Square s) { return b ^ square_bb(s); }
inline Bitboard& operator|=(Bitboard& b, Square s) { return b |= square_bb(s); }
inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); }

inline Bitboard operator|(Bitboard b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b | SquareBB[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^(Bitboard b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b ^ SquareBB[s];
}
inline Bitboard operator|(Square s, Square s2) { return square_bb(s) | square_bb(s2); }

inline Bitboard& operator|=(Bitboard& b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b |= SquareBB[s];
constexpr bool more_than_one(Bitboard b) {
return b & (b - 1);
}

inline Bitboard& operator^=(Bitboard& b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b ^= SquareBB[s];
inline bool opposite_colors(Square s1, Square s2) {
return bool(DarkSquares & s1) != bool(DarkSquares & s2);
}

constexpr bool more_than_one(Bitboard b) {
return b & (b - 1);
}

/// rank_bb() and file_bb() return a bitboard representing all the squares on
/// the given file or rank.

inline Bitboard rank_bb(Rank r) {
return RankBB[r];
return Rank1BB << (8 * r);
}

inline Bitboard rank_bb(Square s) {
return RankBB[rank_of(s)];
return rank_bb(rank_of(s));
}

inline Bitboard file_bb(File f) {
return FileBB[f];
return FileABB << f;
}

inline Bitboard file_bb(Square s) {
return FileBB[file_of(s)];
return file_bb(file_of(s));
}


/// shift() moves a bitboard one step along direction D (mainly for pawns)
/// shift() moves a bitboard one step along direction D

template<Direction D>
constexpr Bitboard shift(Bitboard b) {
return D == NORTH ? b << 8 : D == SOUTH ? b >> 8
: D == NORTH+NORTH? b <<16 : D == SOUTH+SOUTH? b >>16
: D == EAST ? (b & ~FileHBB) << 1 : D == WEST ? (b & ~FileABB) >> 1
: D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == NORTH_WEST ? (b & ~FileABB) << 7
: D == SOUTH_EAST ? (b & ~FileHBB) >> 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9
: 0;
}


/// pawn_attacks_bb() returns the pawn attacks for the given color from the
/// squares in the given bitboard.
/// pawn_attacks_bb() returns the squares attacked by pawns of the given color
/// from the squares in the given bitboard.

template<Color C>
constexpr Bitboard pawn_attacks_bb(Bitboard b) {
Expand All @@ -177,58 +177,65 @@ constexpr Bitboard pawn_attacks_bb(Bitboard b) {
}


/// pawn_double_attacks_bb() returns the squares doubly attacked by pawns of the
/// given color from the squares in the given bitboard.

template<Color C>
constexpr Bitboard pawn_double_attacks_bb(Bitboard b) {
return C == WHITE ? shift<NORTH_WEST>(b) & shift<NORTH_EAST>(b)
: shift<SOUTH_WEST>(b) & shift<SOUTH_EAST>(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 AdjacentFilesBB[f];
inline Bitboard adjacent_files_bb(Square s) {
return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s));
}


/// between_bb() returns a bitboard representing all the squares between the two
/// given ones. For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with
/// the bits for square d5 and e6 set. If s1 and s2 are not on the same rank, file
/// or diagonal, 0 is returned.
/// between_bb() returns squares that are linearly between the given squares
/// If the given squares are not on a same file/rank/diagonal, return 0.

inline Bitboard between_bb(Square s1, Square s2) {
return BetweenBB[s1][s2];
return LineBB[s1][s2] & ( (AllSquares << (s1 + (s1 < s2)))
^(AllSquares << (s2 + !(s1 < s2))));
}


/// forward_ranks_bb() returns a bitboard representing the squares on all the ranks
/// forward_ranks_bb() returns a bitboard representing the squares on the ranks
/// in front of the given one, from the point of view of the given color. For instance,
/// 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 ForwardRanksBB[c][rank_of(s)];
return c == WHITE ? ~Rank1BB << 8 * (rank_of(s) - RANK_1)
: ~Rank8BB >> 8 * (RANK_8 - rank_of(s));
}


/// forward_file_bb() returns a bitboard representing all the squares along the line
/// in front of the given one, from the point of view of the given color:
/// ForwardFileBB[c][s] = forward_ranks_bb(c, s) & file_bb(s)
/// forward_file_bb() returns a bitboard representing all the squares along the
/// line in front of the given one, from the point of view of the given color.

inline Bitboard forward_file_bb(Color c, Square s) {
return ForwardFileBB[c][s];
return forward_ranks_bb(c, s) & file_bb(s);
}


/// pawn_attack_span() returns a bitboard representing all the squares that can be
/// attacked by a pawn of the given color when it moves along its file, starting
/// from the given square:
/// PawnAttackSpan[c][s] = forward_ranks_bb(c, s) & adjacent_files_bb(file_of(s));
/// pawn_attack_span() returns a bitboard representing all the squares that can
/// be attacked by a pawn of the given color when it moves along its file,
/// starting from the given square.

inline Bitboard pawn_attack_span(Color c, Square s) {
return PawnAttackSpan[c][s];
return forward_ranks_bb(c, s) & adjacent_files_bb(s);
}


/// passed_pawn_mask() returns a bitboard mask which can be used to test if a
/// pawn of the given color and on the given square is a passed pawn:
/// PassedPawnMask[c][s] = pawn_attack_span(c, s) | forward_file_bb(c, s)
/// passed_pawn_span() returns a bitboard which can be used to test if a pawn of
/// the given color and on the given square is a passed pawn.

inline Bitboard passed_pawn_mask(Color c, Square s) {
return PassedPawnMask[c][s];
inline Bitboard passed_pawn_span(Color c, Square s) {
return forward_ranks_bb(c, s) & (adjacent_files_bb(s) | file_bb(s));
}


Expand All @@ -241,15 +248,16 @@ inline bool aligned(Square s1, Square s2, Square s3) {


/// distance() functions return the distance between x and y, defined as the
/// number of steps for a king in x to reach y. Works with squares, ranks, files.
/// number of steps for a king in x to reach y.

template<typename T> inline int distance(T x, T y) { return x < y ? y - x : x - y; }
template<typename T1 = Square> inline int distance(Square x, Square y);
template<> inline int distance<File>(Square x, Square y) { return std::abs(file_of(x) - file_of(y)); }
template<> inline int distance<Rank>(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); }
template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; }

template<typename T1, typename T2> inline int distance(T2 x, T2 y);
template<> inline int distance<File>(Square x, Square y) { return distance(file_of(x), file_of(y)); }
template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_of(x), rank_of(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;
}

/// attacks_bb() returns a bitboard representing all the squares attacked by a
/// piece of type Pt (bishop or rook) placed on 's'.
Expand Down Expand Up @@ -281,7 +289,6 @@ inline int popcount(Bitboard b) {

#ifndef USE_POPCNT

extern uint8_t PopCnt16[1 << 16];
union { Bitboard bb; uint16_t u[4]; } v = { b };
return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]];

Expand Down Expand Up @@ -375,10 +382,9 @@ inline Square pop_lsb(Bitboard* b) {
}


/// frontmost_sq() and backmost_sq() return the square corresponding to the
/// most/least advanced bit 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
inline Square frontmost_sq(Color c, Bitboard b) {
return c == WHITE ? msb(b) : lsb(b);
}

#endif // #ifndef BITBOARD_H_INCLUDED
95 changes: 65 additions & 30 deletions src/endgame.cpp
Expand Up @@ -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
Expand All @@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <algorithm>
#include <cassert>

#include "bitboard.h"
Expand All @@ -45,14 +44,14 @@ namespace {
// Table used to drive the king towards a corner square of the
// right color in KBN vs K endgames.
constexpr int PushToCorners[SQUARE_NB] = {
200, 190, 180, 170, 160, 150, 140, 130,
190, 180, 170, 160, 150, 140, 130, 140,
180, 170, 155, 140, 140, 125, 140, 150,
170, 160, 140, 120, 110, 140, 150, 160,
160, 150, 140, 110, 120, 140, 160, 170,
150, 140, 125, 140, 140, 155, 170, 180,
140, 130, 140, 150, 160, 170, 180, 190,
130, 140, 150, 160, 170, 180, 190, 200
6400, 6080, 5760, 5440, 5120, 4800, 4480, 4160,
6080, 5760, 5440, 5120, 4800, 4480, 4160, 4480,
5760, 5440, 4960, 4480, 4480, 4000, 4480, 4800,
5440, 5120, 4480, 3840, 3520, 4480, 4800, 5120,
5120, 4800, 4480, 3520, 3840, 4480, 5120, 5440,
4800, 4480, 4000, 4480, 4480, 4960, 5440, 5760,
4480, 4160, 4480, 4800, 5120, 5440, 5760, 6080,
4160, 4480, 4800, 5120, 5440, 5760, 6080, 6400
};

// Tables used to drive a piece towards or away from another piece
Expand All @@ -75,17 +74,42 @@ 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 = Square(int(sq) ^ 7); // Mirror SQ_H1 -> SQ_A1

if (strongSide == BLACK)
sq = ~sq;

return sq;
return strongSide == WHITE ? sq : ~sq;
}

} // namespace


namespace Endgames {

std::pair<Map<Value>, Map<ScaleFactor>> maps;

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<KNPK>("KNPK");
add<KNPKB>("KNPKB");
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
Expand Down Expand Up @@ -120,7 +144,7 @@ Value Endgame<KXK>::operator()(const Position& pos) const {


/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the
/// defending king towards a corner square of the right color.
/// defending king towards a corner square that our bishop attacks.
template<>
Value Endgame<KBNK>::operator()(const Position& pos) const {

Expand All @@ -131,24 +155,19 @@ Value Endgame<KBNK>::operator()(const Position& pos) const {
Square loserKSq = pos.square<KING>(weakSide);
Square bishopSq = pos.square<BISHOP>(strongSide);

// kbnk_mate_table() tries to drive toward corners A1 or H8. If we have a
// bishop that cannot reach the above squares, we flip the kings in order
// to drive the enemy toward corners A8 or H1.
if (opposite_colors(bishopSq, SQ_A1))
{
winnerKSq = ~winnerKSq;
loserKSq = ~loserKSq;
}
// 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[loserKSq];
+ PushToCorners[opposite_colors(bishopSq, SQ_A1) ? ~loserKSq : loserKSq];

assert(abs(result) < VALUE_MATE_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 {

Expand Down Expand Up @@ -291,6 +310,21 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
}


/// KNN vs KP. Simply push the opposing king to the corner
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)];

return strongSide == pos.side_to_move() ? result : -result;
}


/// Some cases of trivial draws
template<> Value Endgame<KNNK>::operator()(const Position&) const { return VALUE_DRAW; }

Expand Down Expand Up @@ -331,7 +365,7 @@ ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
&& pos.count<PAWN>(weakSide) >= 1)
{
// Get weakSide pawn that is closest to the home rank
Square weakPawnSq = backmost_sq(weakSide, pos.pieces(weakSide, PAWN));
Square weakPawnSq = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN));

Square strongKingSq = pos.square<KING>(strongSide);
Square weakKingSq = pos.square<KING>(weakSide);
Expand Down Expand Up @@ -629,8 +663,6 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
Square ksq = pos.square<KING>(weakSide);
Square psq1 = pos.squares<PAWN>(strongSide)[0];
Square psq2 = pos.squares<PAWN>(strongSide)[1];
Rank r1 = rank_of(psq1);
Rank r2 = rank_of(psq2);
Square blockSq1, blockSq2;

if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2))
Expand Down Expand Up @@ -664,7 +696,7 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq2
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP))
|| distance(r1, r2) >= 2))
|| distance<Rank>(psq1, psq2) >= 2))
return SCALE_FACTOR_DRAW;

else if ( ksq == blockSq2
Expand Down Expand Up @@ -729,6 +761,9 @@ ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
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);
Expand Down
44 changes: 13 additions & 31 deletions src/endgame.h
Expand Up @@ -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
Expand All @@ -21,7 +21,7 @@
#ifndef ENDGAME_H_INCLUDED
#define ENDGAME_H_INCLUDED

#include <map>
#include <unordered_map>
#include <memory>
#include <string>
#include <type_traits>
Expand All @@ -37,6 +37,7 @@ enum EndgameCode {

EVALUATION_FUNCTIONS,
KNNK, // KNN vs K
KNNKP, // KNN vs KP
KXK, // Generic "mate lone king" eval
KBNK, // KBN vs K
KPK, // KP vs K
Expand Down Expand Up @@ -90,14 +91,18 @@ 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().

class Endgames {
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() {
Expand All @@ -112,34 +117,11 @@ class Endgames {
map<T>()[Position().set(code, BLACK, &st).material_key()] = Ptr<T>(new Endgame<E>(BLACK));
}

std::pair<Map<Value>, Map<ScaleFactor>> maps;

public:
Endgames() {

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<KNPK>("KNPK");
add<KNPKB>("KNPKB");
add<KRPKR>("KRPKR");
add<KRPKB>("KRPKB");
add<KBPKB>("KBPKB");
add<KBPKN>("KBPKN");
add<KBPPKB>("KBPPKB");
add<KRPPKRP>("KRPPKRP");
}

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;
}
};
}

#endif // #ifndef ENDGAME_H_INCLUDED
513 changes: 241 additions & 272 deletions src/evaluate.cpp

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/evaluate.h
Expand Up @@ -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
Expand All @@ -29,7 +29,7 @@ class Position;

namespace Eval {

constexpr Value Tempo = Value(20); // Must be visible to search
constexpr Value Tempo = Value(28); // Must be visible to search

std::string trace(const Position& pos);

Expand Down
6 changes: 3 additions & 3 deletions src/main.cpp
Expand Up @@ -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
Expand All @@ -26,6 +26,7 @@
#include "thread.h"
#include "tt.h"
#include "uci.h"
#include "endgame.h"
#include "syzygy/tbprobe.h"

namespace PSQT {
Expand All @@ -41,8 +42,7 @@ int main(int argc, char* argv[]) {
Bitboards::init();
Position::init();
Bitbases::init();
Search::init();
Pawns::init();
Endgames::init();
Threads.set(Options["Threads"]);
Search::clear(); // After threads are up

Expand Down
17 changes: 7 additions & 10 deletions src/material.cpp
Expand Up @@ -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
Expand All @@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <algorithm> // For std::min
#include <cassert>
#include <cstring> // For std::memset

Expand Down Expand Up @@ -70,14 +69,12 @@ namespace {

bool is_KBPsK(const Position& pos, Color us) {
return pos.non_pawn_material(us) == BishopValueMg
&& pos.count<BISHOP>(us) == 1
&& pos.count<PAWN >(us) >= 1;
}

bool is_KQKRPs(const Position& pos, Color us) {
return !pos.count<PAWN>(us)
&& pos.non_pawn_material(us) == QueenValueMg
&& pos.count<QUEEN>(us) == 1
&& pos.count<ROOK>(~us) == 1
&& pos.count<PAWN>(~us) >= 1;
}
Expand Down Expand Up @@ -132,18 +129,18 @@ Entry* probe(const Position& pos) {

Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK);
Value npm = std::max(EndgameLimit, std::min(npm_w + npm_b, MidgameLimit));
Value npm = 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));

// Let's look if we have a specialized evaluation function for this particular
// material configuration. Firstly we look for a fixed configuration one, then
// for a generic one if the previous search failed.
if ((e->evaluationFunction = pos.this_thread()->endgames.probe<Value>(key)) != nullptr)
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];
Expand All @@ -152,9 +149,9 @@ Entry* probe(const Position& pos) {

// OK, we didn't find any special evaluation function for the current material
// configuration. Is there a suitable specialized scaling function?
const EndgameBase<ScaleFactor>* sf;
const auto* sf = Endgames::probe<ScaleFactor>(key);

if ((sf = pos.this_thread()->endgames.probe<ScaleFactor>(key)) != nullptr)
if (sf)
{
e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned
return e;
Expand All @@ -163,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];
Expand Down
4 changes: 2 additions & 2 deletions src/material.h
Expand Up @@ -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
Expand Down Expand Up @@ -58,7 +58,7 @@ struct Entry {
Key key;
const EndgameBase<Value>* evaluationFunction;
const EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each
// side (e.g. KPKP, KBPsKs)
// side (e.g. KPKP, KBPsK)
int16_t value;
uint8_t factor[COLOR_NB];
Phase gamePhase;
Expand Down
95 changes: 84 additions & 11 deletions src/misc.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -56,7 +56,7 @@ namespace {

/// Version number. If Version is left empty, then compile date in the format
/// DD-MM-YY and show in engine_info.
const string Version = "10";
const string Version = "";

/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
Expand Down Expand Up @@ -102,6 +102,13 @@ class Logger {
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);
}
Expand Down Expand Up @@ -144,8 +151,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 VER_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 += VER_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 += VER_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); }
Expand All @@ -168,7 +246,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();
Expand Down Expand Up @@ -210,12 +288,6 @@ void prefetch(void* addr) {

#endif

void prefetch2(void* addr) {

prefetch(addr);
prefetch((uint8_t*)addr + 64);
}

namespace WinProcGroup {

#ifndef _WIN32
Expand Down Expand Up @@ -257,7 +329,7 @@ int best_group(size_t idx) {
return -1;
}

while (ptr->Size > 0 && byteOffset + ptr->Size <= returnLength)
while (byteOffset < returnLength)
{
if (ptr->Relationship == RelationNumaNode)
nodes++;
Expand All @@ -268,6 +340,7 @@ int best_group(size_t idx) {
threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1;
}

assert(ptr->Size);
byteOffset += ptr->Size;
ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size);
}
Expand Down
6 changes: 3 additions & 3 deletions src/misc.h
Expand Up @@ -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
Expand Down Expand Up @@ -30,8 +30,8 @@
#include "types.h"

const std::string engine_info(bool to_uci = false);
const std::string compiler_info();
void prefetch(void* addr);
void prefetch2(void* addr);
void start_logger(const std::string& fname);

void dbg_hit_on(bool b);
Expand All @@ -53,7 +53,7 @@ struct HashTable {
Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; }

private:
std::vector<Entry> table = std::vector<Entry>(Size);
std::vector<Entry> table = std::vector<Entry>(Size); // Allocate on the heap
};


Expand Down
98 changes: 23 additions & 75 deletions src/movegen.cpp
Expand Up @@ -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
Expand All @@ -25,47 +25,6 @@

namespace {

template<Color Us, CastlingSide Cs, bool Checks, bool Chess960>
ExtMove* generate_castling(const Position& pos, ExtMove* moveList) {

constexpr CastlingRight Cr = Us | Cs;
constexpr bool KingSide = (Cr == WHITE_OO || Cr == BLACK_OO);

if (pos.castling_impeded(Cr) || !pos.can_castle(Cr))
return moveList;

// After castling, the rook and king final positions are the same in Chess960
// as they would be in standard chess.
Square kfrom = pos.square<KING>(Us);
Square rfrom = pos.castling_rook_square(Cr);
Square kto = relative_square(Us, KingSide ? SQ_G1 : SQ_C1);
Bitboard enemies = pos.pieces(~Us);

assert(!pos.checkers());

const Direction step = Chess960 ? kto > kfrom ? WEST : EAST
: KingSide ? WEST : EAST;

for (Square s = kto; s != kfrom; s += step)
if (pos.attackers_to(s) & enemies)
return moveList;

// Because we generate only legal castling moves we need to verify that
// when moving the castling rook we do not discover some hidden checker.
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
if (Chess960 && (attacks_bb<ROOK>(kto, pos.pieces() ^ rfrom) & pos.pieces(~Us, ROOK, QUEEN)))
return moveList;

Move m = make<CASTLING>(kfrom, rfrom);

if (Checks && !pos.gives_check(m))
return moveList;

*moveList++ = m;
return moveList;
}


template<GenType Type, Direction D>
ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {

Expand Down Expand Up @@ -93,16 +52,15 @@ namespace {
template<Color Us, GenType Type>
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {

// Compute our parametrized parameters at compile time, named according to
// the point of view of white side.
// Compute some compile time parameters relative to the white side
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
constexpr Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
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;
Expand All @@ -127,19 +85,17 @@ 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);

// 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
// don't generate captures. Note that a possible discovery check
// promotion has been already generated amongst the captures.
Bitboard dcCandidates = pos.blockers_for_king(Them);
if (pawnsNotOn7 & dcCandidates)
Bitboard dcCandidateQuiets = pos.blockers_for_king(Them) & pawnsNotOn7;
if (dcCandidateQuiets)
{
Bitboard dc1 = shift<Up>(pawnsNotOn7 & dcCandidates) & emptySquares & ~file_bb(ksq);
Bitboard dc1 = shift<Up>(dcCandidateQuiets) & emptySquares & ~file_bb(ksq);
Bitboard dc2 = shift<Up>(dc1 & TRank3BB) & emptySquares;

b1 |= dc1;
Expand All @@ -161,7 +117,7 @@ namespace {
}

// Promotions and underpromotions
if (pawnsOn7 && (Type != EVASIONS || (target & TRank8BB)))
if (pawnsOn7)
{
if (Type == CAPTURES)
emptySquares = ~pos.pieces();
Expand All @@ -173,8 +129,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);

Expand Down Expand Up @@ -230,7 +184,7 @@ namespace {
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us,
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);

Expand Down Expand Up @@ -262,7 +216,9 @@ namespace {
template<Color Us, GenType Type>
ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) {

constexpr bool Checks = Type == QUIET_CHECKS;
constexpr CastlingRights OO = Us & KING_SIDE;
constexpr CastlingRights OOO = Us & QUEEN_SIDE;
constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations

moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target);
Expand All @@ -276,19 +232,14 @@ namespace {
Bitboard b = pos.attacks_from<KING>(ksq) & target;
while (b)
*moveList++ = make_move(ksq, pop_lsb(&b));
}

if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us))
{
if (pos.is_chess960())
{
moveList = generate_castling<Us, KING_SIDE, Checks, true>(pos, moveList);
moveList = generate_castling<Us, QUEEN_SIDE, Checks, true>(pos, moveList);
}
else
if (Type != CAPTURES && pos.can_castle(CastlingRights(OO | OOO)))
{
moveList = generate_castling<Us, KING_SIDE, Checks, false>(pos, moveList);
moveList = generate_castling<Us, QUEEN_SIDE, Checks, false>(pos, moveList);
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));
}
}

Expand All @@ -298,19 +249,16 @@ namespace {
} // namespace


/// generate<CAPTURES> generates all pseudo-legal captures and queen
/// promotions. Returns a pointer to the end of the move list.
///
/// generate<QUIETS> generates all pseudo-legal non-captures and
/// underpromotions. Returns a pointer to the end of the move list.
/// <CAPTURES> Generates all pseudo-legal captures and queen promotions
/// <QUIETS> Generates all pseudo-legal non-captures and underpromotions
/// <NON_EVASIONS> Generates all pseudo-legal captures and non-captures
///
/// generate<NON_EVASIONS> generates all pseudo-legal captures and
/// non-captures. Returns a pointer to the end of the move list.
/// Returns a pointer to the end of the move list.

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();
Expand Down
2 changes: 1 addition & 1 deletion src/movegen.h
Expand Up @@ -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
Expand Down
84 changes: 43 additions & 41 deletions src/movepick.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -31,9 +31,6 @@ namespace {
QSEARCH_TT, QCAPTURE_INIT, QCAPTURE, QCHECK_INIT, QCHECK
};

// Helper filter used with select()
const auto Any = [](){ return true; };

// partial_insertion_sort() sorts moves in descending order up to and including
// a given limit. The order of moves smaller than the limit is left unspecified.
void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) {
Expand Down Expand Up @@ -64,7 +61,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch),
refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) {

assert(d > DEPTH_ZERO);
assert(d > 0);

stage = pos.checkers() ? EVASION_TT : MAIN_TT;
ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
Expand All @@ -76,12 +73,12 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs)
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), recaptureSquare(rs), depth(d) {

assert(d <= DEPTH_ZERO);
assert(d <= 0);

stage = pos.checkers() ? EVASION_TT : QSEARCH_TT;
ttMove = ttm
&& pos.pseudo_legal(ttm)
&& (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) ? ttm : MOVE_NONE;
ttMove = ttm
&& (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare)
&& pos.pseudo_legal(ttm) ? ttm : MOVE_NONE;
stage += (ttMove == MOVE_NONE);
}

Expand All @@ -94,8 +91,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece

stage = PROBCUT_TT;
ttMove = ttm
&& pos.pseudo_legal(ttm)
&& pos.capture(ttm)
&& pos.pseudo_legal(ttm)
&& pos.see_ge(ttm, threshold) ? ttm : MOVE_NONE;
stage += (ttMove == MOVE_NONE);
}
Expand All @@ -110,14 +107,15 @@ 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)];
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)];

else // Type == EVASIONS
{
Expand All @@ -141,12 +139,12 @@ Move MovePicker::select(Pred filter) {
if (T == Best)
std::swap(*cur, *std::max_element(cur, endMoves));

move = *cur++;
if (*cur != ttMove && filter())
return *cur++;

if (move != ttMove && filter())
return move;
cur++;
}
return move = MOVE_NONE;
return MOVE_NONE;
}

/// MovePicker::next_move() is the most important method of the MovePicker class. It
Expand Down Expand Up @@ -176,10 +174,10 @@ Move MovePicker::next_move(bool skipQuiets) {

case GOOD_CAPTURE:
if (select<Best>([&](){
return pos.see_ge(move, Value(-55 * (cur-1)->value / 1024)) ?
return pos.see_ge(*cur, Value(-55 * cur->value / 1024)) ?
// Move losing capture to endBadCaptures to be tried later
true : (*endBadCaptures++ = move, false); }))
return move;
true : (*endBadCaptures++ = *cur, false); }))
return *(cur - 1);

// Prepare the pointers to loop over the refutations array
cur = std::begin(refutations);
Expand All @@ -194,28 +192,32 @@ Move MovePicker::next_move(bool skipQuiets) {
/* fallthrough */

case REFUTATION:
if (select<Next>([&](){ return move != MOVE_NONE
&& !pos.capture(move)
&& pos.pseudo_legal(move); }))
return move;
if (select<Next>([&](){ return *cur != MOVE_NONE
&& !pos.capture(*cur)
&& pos.pseudo_legal(*cur); }))
return *(cur - 1);
++stage;
/* 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 */

case QUIET:
if ( !skipQuiets
&& select<Next>([&](){return move != refutations[0]
&& move != refutations[1]
&& move != refutations[2];}))
return move;
&& select<Next>([&](){return *cur != refutations[0].move
&& *cur != refutations[1].move
&& *cur != refutations[2].move;}))
return *(cur - 1);

// Prepare the pointers to loop over the bad captures
cur = moves;
Expand All @@ -225,7 +227,7 @@ Move MovePicker::next_move(bool skipQuiets) {
/* fallthrough */

case BAD_CAPTURE:
return select<Next>(Any);
return select<Next>([](){ return true; });

case EVASION_INIT:
cur = moves;
Expand All @@ -236,15 +238,15 @@ Move MovePicker::next_move(bool skipQuiets) {
/* fallthrough */

case EVASION:
return select<Best>(Any);
return select<Best>([](){ return true; });

case PROBCUT:
return select<Best>([&](){ return pos.see_ge(move, threshold); });
return select<Best>([&](){ return pos.see_ge(*cur, threshold); });

case QCAPTURE:
if (select<Best>([&](){ return depth > DEPTH_QS_RECAPTURES
|| to_sq(move) == recaptureSquare; }))
return move;
|| to_sq(*cur) == recaptureSquare; }))
return *(cur - 1);

// If we did not find any move and we do not try checks, we have finished
if (depth != DEPTH_QS_CHECKS)
Expand All @@ -261,7 +263,7 @@ Move MovePicker::next_move(bool skipQuiets) {
/* fallthrough */

case QCHECK:
return select<Next>(Any);
return select<Next>([](){ return true; });
}

assert(false);
Expand Down
9 changes: 4 additions & 5 deletions src/movepick.h
Expand Up @@ -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
Expand Down Expand Up @@ -80,16 +80,16 @@ 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
/// ordering decisions. It uses 2 tables (one for each color) indexed by
/// the move's from and to squares, see chessprogramming.wikispaces.com/Butterfly+Boards
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
typedef Stats<int16_t, 10692, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;

/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
/// move, see chessprogramming.wikispaces.com/Countermove+Heuristic
/// move, see www.chessprogramming.org/Countermove_Heuristic
typedef Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB> CounterMoveHistory;

/// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
Expand Down Expand Up @@ -142,7 +142,6 @@ class MovePicker {
Move ttMove;
ExtMove refutations[3], *cur, *endMoves, *endBadCaptures;
int stage;
Move move;
Square recaptureSquare;
Value threshold;
Depth depth;
Expand Down
200 changes: 96 additions & 104 deletions src/pawns.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -32,12 +32,15 @@ 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 BlockedStorm = S(82, 82);
constexpr Score Doubled = S(11, 56);
constexpr Score Isolated = S( 5, 15);
constexpr Score WeakLever = S( 0, 56);
constexpr Score WeakUnopposed = S(13, 27);

// Connected pawn bonus by opposed, phalanx, #support and rank
Score Connected[2][2][3][RANK_NB];
// Connected pawn bonus
constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 };

// Strength of pawn shelter for our king by [distance from edge][rank].
// RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king.
Expand All @@ -50,12 +53,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
Expand All @@ -65,80 +69,86 @@ namespace {
Score evaluate(const Position& pos, Pawns::Entry* e) {

constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
constexpr Direction Up = pawn_push(Us);

Bitboard b, neighbours, stoppers, doubled, supported, 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->semiopenFiles[Us] = 0xFF;
e->kingSquares[Us] = SQ_NONE;
e->pawnAttacks[Us] = pawn_attacks_bb<Us>(ourPawns);
e->pawnsOnSquares[Us][BLACK] = popcount(ourPawns & DarkSquares);
e->pawnsOnSquares[Us][WHITE] = pos.count<PAWN>(Us) - e->pawnsOnSquares[Us][BLACK];
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);

// 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);

e->semiopenFiles[Us] &= ~(1 << f);
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
Rank r = relative_rank(Us, s);

// Flag the pawn
opposed = theirPawns & forward_file_bb(Us, s);
stoppers = theirPawns & passed_pawn_mask(Us, s);
blocked = theirPawns & (s + Up);
stoppers = theirPawns & passed_pawn_span(Us, s);
lever = theirPawns & PawnAttacks[Us][s];
leverPush = theirPawns & PawnAttacks[Us][s + Up];
doubled = ourPawns & (s - Up);
neighbours = ourPawns & adjacent_files_bb(f);
neighbours = ourPawns & adjacent_files_bb(s);
phalanx = neighbours & rank_bb(s);
supported = 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)));

// 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)
&& popcount(supported) >= popcount(lever) - 1
&& popcount(phalanx) >= popcount(leverPush))
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 safely advance.
backward = !(neighbours & forward_ranks_bb(Them, s + Up))
&& (leverPush | blocked);

// 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.
passed = !(stoppers ^ lever)
|| ( !(stoppers ^ leverPush)
&& popcount(phalanx) >= popcount(leverPush))
|| ( stoppers == blocked && r >= RANK_5
&& (shift<Up>(support) & ~(theirPawns | doubleAttackThem)));

// Passed pawns will be properly scored later in evaluation when we have
// full attack info.
if (passed)
e->passedPawns[Us] |= s;

else if ( stoppers == SquareBB[s + Up]
&& relative_rank(Us, s) >= RANK_5)
// Score this pawn
if (support | phalanx)
{
b = shift<Up>(supported) & ~theirPawns;
while (b)
if (!more_than_one(theirPawns & PawnAttacks[Us][pop_lsb(&b)]))
e->passedPawns[Us] |= s;
}
int v = Connected[r] * (2 + bool(phalanx) - bool(opposed))
+ 21 * popcount(support);

// Score this pawn
if (supported | phalanx)
score += Connected[opposed][bool(phalanx)][popcount(supported)][relative_rank(Us, s)];
score += make_score(v, v * (r - 2) / 4);
}

else if (!neighbours)
score -= Isolated, e->weakUnopposed[Us] += !opposed;
score -= Isolated
+ WeakUnopposed * !opposed;

else if (backward)
score -= Backward, e->weakUnopposed[Us] += !opposed;
score -= Backward
+ WeakUnopposed * !opposed;

if (doubled && !supported)
score -= Doubled;
if (!support)
score -= Doubled * doubled
+ WeakLever * more_than_one(lever);
}

return score;
Expand All @@ -148,27 +158,6 @@ namespace {

namespace Pawns {

/// Pawns::init() initializes some tables needed by evaluation. Instead of using
/// hard-coded tables, when makes sense, we prefer to calculate them with a formula
/// to reduce independent parameters and to allow easier tuning and better insight.

void init() {

static constexpr int Seed[RANK_NB] = { 0, 13, 24, 18, 65, 100, 175, 330 };

for (int opposed = 0; opposed <= 1; ++opposed)
for (int phalanx = 0; phalanx <= 1; ++phalanx)
for (int support = 0; support <= 2; ++support)
for (Rank r = RANK_2; r < RANK_8; ++r)
{
int v = 17 * support;
v += (Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0)) >> opposed;

Connected[opposed][phalanx][support][r] = make_score(v, v * (r - 2) / 4);
}
}


/// Pawns::probe() looks up the current position's pawns configuration in
/// the pawns hash table. It returns a pointer to the Entry if the position
/// is found. Otherwise a new Entry is computed and stored there, so we don't
Expand All @@ -185,9 +174,6 @@ Entry* probe(const Position& pos) {
e->key = key;
e->scores[WHITE] = evaluate<WHITE>(pos, e);
e->scores[BLACK] = evaluate<BLACK>(pos, e);
e->openFiles = popcount(e->semiopenFiles[WHITE] & e->semiopenFiles[BLACK]);
e->asymmetry = popcount( (e->passedPawns[WHITE] | e->passedPawns[BLACK])
| (e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]));

return e;
}
Expand All @@ -197,35 +183,35 @@ Entry* probe(const Position& pos) {
/// penalty for a king, looking at the king file and the two closest files.

template<Color Us>
Value Entry::evaluate_shelter(const Position& pos, Square ksq) {
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 BlockRanks = (Us == WHITE ? Rank1BB | Rank2BB : Rank8BB | Rank7BB);
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);

Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq);
Bitboard ourPawns = b & pos.pieces(Us);
Bitboard theirPawns = b & pos.pieces(Them);

Value safety = (shift<Down>(theirPawns) & (FileABB | FileHBB) & BlockRanks & ksq) ?
Value(374) : Value(5);
Score bonus = make_score(5, 5);

File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));
File center = clamp(file_of(ksq), FILE_B, FILE_G);
for (File f = File(center - 1); f <= File(center + 1); ++f)
{
b = ourPawns & file_bb(f);
int ourRank = b ? relative_rank(Us, backmost_sq(Us, b)) : 0;
int ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0;

b = theirPawns & file_bb(f);
int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0;

int d = std::min(f, ~f);
safety += ShelterStrength[d][ourRank];
safety -= (ourRank && (ourRank == theirRank - 1)) ? 66 * (theirRank == RANK_3)
: UnblockedStorm[d][theirRank];
File d = map_to_queenside(f);
bonus += make_score(ShelterStrength[d][ourRank], 0);

if (ourRank && (ourRank == theirRank - 1))
bonus -= BlockedStorm * int(theirRank == RANK_3);
else
bonus -= make_score(UnblockedStorm[d][theirRank], 0);
}

return safety;
return bonus;
}


Expand All @@ -237,23 +223,29 @@ Score Entry::do_king_safety(const Position& pos) {

Square ksq = pos.square<KING>(Us);
kingSquares[Us] = ksq;
castlingRights[Us] = pos.can_castle(Us);
int minKingPawnDistance = 0;
castlingRights[Us] = pos.castling_rights(Us);
auto compare = [](Score a, Score b) { return mg_value(a) < mg_value(b); };

Bitboard pawns = pos.pieces(Us, PAWN);
if (pawns)
while (!(DistanceRingBB[ksq][++minKingPawnDistance] & pawns)) {}
Score shelter = evaluate_shelter<Us>(pos, ksq);

// If we can castle use the bonus after castling if it is bigger

Value bonus = evaluate_shelter<Us>(pos, ksq);
if (pos.can_castle(Us & KING_SIDE))
shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1)), compare);

// If we can castle use the bonus after the castling if it is bigger
if (pos.can_castle(Us | KING_SIDE))
bonus = std::max(bonus, evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1)));
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;

if (pos.can_castle(Us | QUEEN_SIDE))
bonus = std::max(bonus, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1)));
if (pawns & PseudoAttacks[KING][ksq])
minPawnDist = 1;
else while (pawns)
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns)));

return make_score(bonus, -16 * minKingPawnDistance);
return shelter - make_score(0, 16 * minPawnDist);
}

// Explicit template instantiation
Expand Down