Merge remote-tracking branch 'upstream/master' into data_generation

This commit is contained in:
Tomasz Sobczyk
2021-04-18 19:45:46 +02:00
61 changed files with 1444 additions and 1577 deletions
+12
View File
@@ -0,0 +1,12 @@
# Files from build
**/*.o
**/*.s
src/.depend
# Built binary
src/stockfish*
src/-lstdc++.res
# Neural network for the NNUE evaluation
**/*.nnue
+7 -1
View File
@@ -1,4 +1,4 @@
# List of authors for Stockfish, as of August 4, 2020
# List of authors for Stockfish, as of March 31, 2021
# Founders of the Stockfish project and fishtest infrastructure
Tord Romstad (romstad)
@@ -24,6 +24,7 @@ Ali AlZhrani (Cooffe)
Andrew Grant (AndyGrant)
Andrey Neporada (nepal)
Andy Duplain
Antoine Champion (antoinechampion)
Aram Tumanian (atumanian)
Arjun Temurnikar
Auguste Pop
@@ -33,6 +34,7 @@ Bill Henry (VoyagerOne)
Bojun Guo (noobpwnftw, Nooby)
braich
Brian Sheppard (SapphireBrand, briansheppard-toast)
Bruno de Melo Costa (BM123499)
Bryan Cross (crossbr)
candirufish
Chess13234
@@ -45,6 +47,7 @@ Dariusz Orzechowski (dorzechowski)
David Zar
Daylen Yang (daylen)
Deshawn Mohan-Smith (GoldenRare)
Dieter Dobbelaere (ddobbelaere)
DiscanX
Dominik Schlösser (domschl)
double-beep
@@ -98,6 +101,7 @@ Ken Takusagawa
kinderchocolate
Kiran Panditrao (Krgp)
Kojirion
Krystian Kuzniarek (kuzkry)
Leonardo Ljubičić (ICCF World Champion)
Leonid Pechenik (lp--)
Linus Arver (listx)
@@ -110,6 +114,7 @@ Maciej Żenczykowski (zenczykowski)
Malcolm Campbell (xoto10)
Mark Tenzer (31m059)
marotear
Matt Ginsberg (mattginsberg)
Matthew Lai (matthewlai)
Matthew Sullivan (Matt14916)
Maxim Molchanov (Maxim)
@@ -163,6 +168,7 @@ Sergio Vieri (sergiovieri)
sf-x
Shane Booth (shane31)
Shawn Varghese (xXH4CKST3RXx)
Siad Daboul (Topologist)
Stefan Geschwentner (locutus2)
Stefano Cardanobile (Stefano80)
Steinar Gunderson (sesse)
+27 -59
View File
@@ -1,11 +1,32 @@
<p align="center">
<img src="https://cdn.discordapp.com/attachments/724700045525647420/729135226365804594/SFNNUE2.png">
</p>
<h1 align="center">Stockfish NNUE</h1>
## 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?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. Stockfish is not a complete chess program and requires a
UCI-compatible graphical user interface (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.
The Stockfish engine features two evaluation functions for chess, the classical
evaluation based on handcrafted terms, and the NNUE evaluation based on efficiently
updatable neural networks. The classical evaluation runs efficiently on almost all
CPU architectures, while the NNUE evaluation benefits from the vector
intrinsics available on most CPUs (sse2, avx2, neon, or similar).
## 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 version 3.
* AUTHORS, a text file with the list of authors for the project
Stockfish NNUE is a port of a shogi neural network named NNUE (efficiently updateable neural network backwards) to Stockfish 11. To learn more about the Stockfish chess engine, look [here](stockfish.md) for an overview and [here](https://github.com/official-stockfish/Stockfish) for the official repository.
=======
@@ -33,8 +54,6 @@ armv7`, `armv7-neon`, `armv8`, `apple-silicon`, `general-64`, `general-32`.
Additional options:
- `blas=[yes/no]` - whether to use an external BLAS library. Default is `no`. Using an external BLAS library may have a significantly improve learning performance and by default expects openBLAS to be installed.
### Building Instructions for Mac
1. Ensure that you have OpenBlas Installed
@@ -78,49 +97,8 @@ This will create a file named "generated_kifu.binpack" in the same folder as the
You will also need validation data that is used for loss calculation and accuracy computation. Validation data is generated in the same way as training data, but generally at most 1 million positions should be used as there's no need for more and it would just slow the learning process down. It may also be better to slightly increase the depth for validation data. After generation you can rename the validation data file to "val.binpack" and drop it in a folder named "validationdata" in the same directory to make it easier.
More information about gensfen and available options can be found in the [docs](docs/gensfen.md)
### Training a network
#### Training a Completely New Network
Whether a new network is created or not is controlled by the UCI option `SkipLoadingEval`. If set to true then a new network will be created, which allows learning from scratch. If left at its default (false) then a network will be loaded and trained further. The second scenario is described in the reinforcement learning paragraph.
A simple command chain to start with training could look like this:
```
uci
setoption name EnableTranspositionTable value false
setoption name PruneAtShallowDepth value false
setoption name SkipLoadingEval value true
setoption name Use NNUE value pure
setoption name Threads value x
isready
learn targetdir trainingdata epochs 10000 batchsize 1000000 use_draw_in_training 1 use_draw_in_validation 1 lr 1 lambda 1 eval_limit 32000 nn_batch_size 1000 newbob_decay 0.5 eval_save_interval 250000000 loss_output_interval 1000000 validation_set_file_name validationdata\val.binpack
```
This will utilize training data files in the "trainingdata" directory and validation data from file "validationdata\val.bin". Produced nets are saved in the "evalsave" folder.
More information about learn and available parameters can be found in the [docs](docs/learn.md)
#### Reinforcement Learning
If you would like to do some reinforcement learning on your original network, you must first generate training data with the setting `Use NNUE` set to `pure` and using the previous network (either name it "nn.bin" and put into alongside the binary or provide the `EvalFile` UCI option). Use the commands specified above. You should aim to generate less positions than the first run, around 1/10 of the number of positions generated in the first run. The depth should be higher as well. You should also do the same for validation data, with the depth being higher than the last run.
After you have generated the training data, you must move it into your training data folder and move the older data so that the binary does not train on the same data again. Do the same for the validation data. Make sure the "evalsave" folder is empty. Then, using the same binary, type in the training commands shown above. Do __NOT__ set `SkipLoadingEval` to true, it must be false or you will get a completely new network, instead of a network trained with reinforcement learning. You should also set `eval_save_interval` to a number that is lower than the amount of positions in your training data, perhaps also 1/10 of the original value.
After training is finished, your new net should be located in the "final" folder under the "evalsave" directory. You should test this new network against the older network to see if there are any improvements. Don't rely on the automatic rejection for network quality, sometimes even rejected nets can be better than the previous ones.
## Using Your Trained Net
If you want to use your generated net, copy the net located in the "final" folder under the "evalsave" directory and move it into a new folder named "eval" under the directory with the binaries. You can then use the halfkp_256x2 binaries pertaining to your CPU with a standard chess GUI, such as Cutechess. Refer to the [releases page](https://abrok.eu/stockfish) to find out which binary is best for your CPU.
If the engine does not load any net file, or shows "Error! *** not found or wrong format", please try to specify the net with the full file path with the `EvalFile` UCI option by typing the command `setoption name EvalFile value path` where path is the full file path. The `Use NNUE` UCI option must be set either to `true` or `pure` with the command `setoption name Use NNUE value true/pure`.
## Training data formats.
Currently there are 3 training data formats. Two of them are supported directly.
- `.bin` - the original training data format. Uses 40 bytes per entry. Is supported directly by the `gensfen` and `learn` commands.
- `.plain` - a human readable training data format. This one is not supported directly by the `gensfen` and `learn` commands. It should not be used for data exchange because it's less compact than other formats. It is mostly useful for inspection of the data.
- `.binpack` - a compact binary training data format that exploits positions chains to further reduce size. It uses on average between 2 to 3 bytes per entry when generating data with `gensfen`. It is supported directly by `gensfen` and `learn` commands. It is currently the default for the `gensfen` command. A more in depth description can be found [here](docs/binpack.md)
@@ -129,14 +107,4 @@ Currently there are 3 training data formats. Two of them are supported directly.
There is a builting converted that support all 3 formats described above. Any of them can be converted to any other. For more information and usage guide see [here](docs/convert.md).
## Resources
- [Training NNUE for SF](https://docs.google.com/document/d/1os5GH8GGJbV0nKAfXD-qySBclFzKKtXKHbAnA-un8tA/edit) google document with important information and coding priorities
- [Gensfen data (vondele)](https://drive.google.com/drive/folders/1mftuzYdl9o6tBaceR3d_VBQIrgKJsFpl) over 2b fens available
- [Stockfish NNUE Wiki](https://www.qhapaq.org/shogi/shogiwiki/stockfish-nnue/)
- [Training instructions](https://twitter.com/mktakizawa/status/1273042640280252416) from the creator of the Elmo shogi engine
- [Original Talkchess thread](http://talkchess.com/forum3/viewtopic.php?t=74059) discussing Stockfish NNUE
- [Guide to Stockfish NNUE](http://yaneuraou.yaneu.com/2020/06/19/stockfish-nnue-the-complete-guide/)
- [Unofficial Stockfish Discord](https://discord.gg/nv8gDtt)
A more updated list can be found in the #sf-nnue-resources channel in the Discord.
+187 -171
View File
@@ -1,173 +1,189 @@
Contributors with >10,000 CPU hours as of Sept 2, 2020
Contributors to Fishtest with >10,000 CPU hours, as of Feb 15, 2021.
Thank you!
Username CPU Hours Games played
--------------------------------------------------
noobpwnftw 19352969 1231459677
mlang 957168 61657446
dew 949885 56893432
mibere 703817 46865007
crunchy 427035 27344275
cw 416006 27521077
JojoM 415904 24479564
fastgm 404873 23953472
CSU_Dynasty 335774 22850550
tvijlbrief 335199 21871270
Fisherman 325053 21786603
gvreuls 311480 20751516
ctoks 275877 18710423
velislav 241267 15596372
glinscott 217799 13780820
nordlandia 211692 13484886
bcross 206213 14934233
bking_US 198894 11876016
leszek 189170 11446821
mgrabiak 183896 11778092
drabel 181408 12489478
TueRens 181349 12192000
Thanar 179852 12365359
vdv 175171 9881246
robal 166948 10702862
spams 157128 10319326
marrco 149947 9376421
sqrt2 147963 9724586
vdbergh 137041 8926915
CoffeeOne 136294 5004100
malala 136182 8002293
mhoram 128934 8177193
davar 122092 7960001
dsmith 122059 7570238
xoto 119696 8222144
grandphish2 116481 7582197
Data 113305 8220352
BrunoBanani 112960 7436849
ElbertoOne 99028 7023771
MaZePallas 98571 6362619
brabos 92118 6186135
psk 89957 5984901
sunu 88463 6007033
sterni1971 86948 5613788
Vizvezdenec 83752 5343724
BRAVONE 81239 5054681
nssy 76497 5259388
teddybaer 75125 5407666
Pking_cda 73776 5293873
jromang 70695 4940891
solarlight 70517 5028306
dv8silencer 70287 3883992
Bobo1239 68515 4652287
racerschmacer 67468 4935996
manap 66273 4121774
tinker 63458 4213726
linrock 59082 4516053
robnjr 57262 4053117
Freja 56938 3733019
ttruscott 56005 3679485
renouve 53811 3501516
cuistot 52532 3014920
finfish 51360 3370515
eva42 51272 3599691
rkl 50759 3840947
rap 49985 3219146
pb00067 49727 3298270
ronaldjerum 47654 3240695
bigpen0r 47278 3291647
biffhero 46564 3111352
VoyagerOne 45386 3445881
speedycpu 43842 3003273
jbwiebe 43305 2805433
Antihistamine 41788 2761312
mhunt 41735 2691355
eastorwest 40387 2812173
homyur 39893 2850481
gri 39871 2515779
oryx 38228 2941656
0x3C33 37773 2529097
SC 37290 2731014
csnodgrass 36207 2688994
jmdana 36108 2205261
strelock 34716 2074055
Garf 33800 2747562
EthanOConnor 33370 2090311
slakovv 32915 2021889
Spprtr 32591 2139601
Prcuvu 30377 2170122
anst 30301 2190091
jkiiski 30136 1904470
hyperbolic.tom 29840 2017394
Pyafue 29650 1902349
OuaisBla 27629 1578000
chriswk 26902 1868317
achambord 26582 1767323
Patrick_G 26276 1801617
yorkman 26193 1992080
SFTUser 25182 1675689
nabildanial 24942 1519409
Sharaf_DG 24765 1786697
ncfish1 24411 1520927
agg177 23890 1395014
JanErik 23408 1703875
Isidor 23388 1680691
Norabor 22976 1587862
cisco2015 22880 1759669
Zirie 22542 1472937
team-oh 22272 1636708
MazeOfGalious 21978 1629593
sg4032 21945 1643065
ianh2105 21725 1632562
xor12 21628 1680365
dex 21612 1467203
nesoneg 21494 1463031
horst.prack 20878 1465656
0xB00B1ES 20590 1208666
j3corre 20405 941444
Adrian.Schmidt123 20316 1281436
wei 19973 1745989
rstoesser 19569 1293588
eudhan 19274 1283717
Ente 19070 1373058
jundery 18445 1115855
iisiraider 18247 1101015
ville 17883 1384026
chris 17698 1487385
purplefishies 17595 1092533
DragonLord 17014 1162790
dju 16515 929427
IgorLeMasson 16064 1147232
ako027ako 15671 1173203
Nikolay.IT 15154 1068349
Andrew Grant 15114 895539
yurikvelo 15027 1165616
OssumOpossum 14857 1007129
enedene 14476 905279
bpfliegel 14298 884523
jpulman 13982 870599
joster 13794 950160
Nesa92 13786 1114691
Dark_wizzie 13422 1007152
Hjax 13350 900887
Fifis 13313 965473
mabichito 12903 749391
thijsk 12886 722107
crocogoat 12876 1048802
AdrianSA 12860 804972
Flopzee 12698 894821
fatmurphy 12547 853210
SapphireBrand 12416 969604
modolief 12386 896470
scuzzi 12362 833465
pgontarz 12151 848794
stocky 11954 699440
mschmidt 11941 803401
infinity 11470 727027
torbjo 11387 728873
Thomas A. Anderson 11372 732094
snicolet 11106 869170
amicic 10779 733593
rpngn 10712 688203
d64 10680 771144
basepi 10637 744851
jjoshua2 10559 670905
dzjp 10343 732529
ols 10259 570669
lbraesch 10252 647825
Username CPU Hours Games played
----------------------------------------------------
noobpwnftw 23930906 1560559941
dew 1169948 70333008
mlang 957168 61657446
mibere 703840 46867607
tvijlbrief 517888 33379462
JojoM 515404 30334272
cw 443276 29385549
crunchy 427035 27344275
grandphish2 425794 26347253
fastgm 414133 24519696
gvreuls 377843 24708884
CSU_Dynasty 338718 23030006
Fisherman 326795 21820747
TueRens 313730 19490246
ctoks 298442 20052551
velislav 270519 17355456
bcross 241064 17196165
glinscott 217799 13780820
nordlandia 211692 13484886
bking_US 198894 11876016
drabel 191096 13129722
leszek 189170 11446821
mgrabiak 187153 12013300
robal 181389 11539242
Thanar 179852 12365359
vdv 175274 9889046
spams 157128 10319326
marrco 150292 9401741
sqrt2 147963 9724586
CoffeeOne 137086 5022516
vdbergh 137041 8926915
malala 136182 8002293
mhoram 132780 8398229
xoto 124729 8652088
davar 122092 7960001
dsmith 122059 7570238
Data 113305 8220352
BrunoBanani 112960 7436849
pemo 109598 5036441
Dantist 106768 6431396
MaZePallas 102741 6630419
ElbertoOne 99028 7023771
brabos 92118 6186135
linrock 90903 6708639
psk 89957 5984901
sunu 88614 6020673
sterni1971 86948 5613788
Vizvezdenec 83761 5344740
BRAVONE 81239 5054681
nssy 76497 5259388
cuistot 76366 4370584
racerschmacer 75753 5442626
teddybaer 75125 5407666
Pking_cda 73776 5293873
0x3C33 73133 4670293
jromang 72117 5054915
solarlight 70517 5028306
dv8silencer 70287 3883992
Bobo1239 68515 4652287
manap 66273 4121774
tinker 64321 4268390
robnjr 57262 4053117
Freja 56938 3733019
ttruscott 56010 3680085
rkl 54986 4150767
renouve 53811 3501516
finfish 51360 3370515
eva42 51272 3599691
rap 49985 3219146
pb00067 49727 3298270
amicic 49691 3042481
ronaldjerum 47654 3240695
bigpen0r 47278 3291647
biffhero 46564 3111352
VoyagerOne 45476 3452465
eastorwest 45033 3071805
speedycpu 43842 3003273
jbwiebe 43305 2805433
Antihistamine 41788 2761312
mhunt 41735 2691355
homyur 39893 2850481
gri 39871 2515779
oryx 38282 2944400
Spprtr 38157 2470529
SC 37290 2731014
csnodgrass 36207 2688994
jmdana 36157 2210661
strelock 34716 2074055
Garf 33800 2747562
skiminki 33515 2055584
EthanOConnor 33370 2090311
slakovv 32915 2021889
yurikvelo 32600 2255966
Prcuvu 30377 2170122
manapbk 30326 1770143
anst 30301 2190091
jkiiski 30136 1904470
hyperbolic.tom 29840 2017394
Pyafue 29650 1902349
qurashee 27758 1509620
OuaisBla 27636 1578800
chriswk 26902 1868317
achambord 26582 1767323
Fifis 26376 1776853
Patrick_G 26276 1801617
yorkman 26193 1992080
SFTUser 25182 1675689
nabildanial 24942 1519409
Sharaf_DG 24765 1786697
ncfish1 24411 1520927
agg177 23890 1395014
JanErik 23408 1703875
Isidor 23388 1680691
Norabor 23164 1591830
cisco2015 22895 1762069
Zirie 22542 1472937
team-oh 22272 1636708
MazeOfGalious 21978 1629593
sg4032 21945 1643065
ianh2105 21725 1632562
xor12 21628 1680365
dex 21612 1467203
nesoneg 21494 1463031
jjoshua2 20997 1422689
horst.prack 20878 1465656
0xB00B1ES 20590 1208666
sphinx 20515 1352368
j3corre 20405 941444
Adrian.Schmidt123 20316 1281436
Ente 20017 1432602
wei 19973 1745989
rstoesser 19569 1293588
eudhan 19274 1283717
jundery 18445 1115855
iisiraider 18247 1101015
ville 17883 1384026
chris 17698 1487385
purplefishies 17595 1092533
DMBK 17357 1279152
DragonLord 17014 1162790
dju 16515 929427
IgorLeMasson 16064 1147232
ako027ako 15671 1173203
Nikolay.IT 15154 1068349
Andrew Grant 15114 895539
OssumOpossum 14857 1007129
enedene 14476 905279
bpfliegel 14298 884523
jpulman 13982 870599
joster 13794 950160
Nesa92 13786 1114691
crocogoat 13753 1114622
Hjax 13535 915487
Dark_wizzie 13422 1007152
mpx86 12941 693640
mabichito 12903 749391
thijsk 12886 722107
AdrianSA 12860 804972
Flopzee 12698 894821
fatmurphy 12547 853210
scuzzi 12511 845761
Karby 12429 735880
SapphireBrand 12416 969604
modolief 12386 896470
pgontarz 12151 848794
stocky 11954 699440
mschmidt 11941 803401
infinity 11470 727027
torbjo 11395 729145
Thomas A. Anderson 11372 732094
d64 11263 789184
Maxim 11129 804704
snicolet 11106 869170
MooTheCow 11008 694942
savage84 10965 641068
Rudolphous 10915 741268
Wolfgang 10809 580032
rpngn 10712 688203
basepi 10637 744851
michaelrpg 10409 735127
dzjp 10343 732529
ali-al-zhrani 10324 726502
ols 10259 570669
lbraesch 10252 647825
+16 -8
View File
@@ -1,7 +1,5 @@
# 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) 2004-2021 The Stockfish developers (see AUTHORS file)
#
# Stockfish is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -39,9 +37,13 @@ PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
### Built-in benchmark for pgo-builds
PGO_TRAINING_DATA_FILE = pgo_training_data.bin
PGOBENCH = ./$(EXE) bench
PGOGENSFEN = ./$(EXE) gensfen depth 3 loop 1000 sfen_format bin output_file_name $(PGO_TRAINING_DATA_FILE)
ifeq ($(SDE_PATH),)
PGOBENCH = ./$(EXE) bench
PGOGENSFEN = ./$(EXE) gensfen depth 3 loop 1000 sfen_format bin output_file_name $(PGO_TRAINING_DATA_FILE)
else
PGOBENCH = $(SDE_PATH) -- ./$(EXE) bench
PGOGENSFEN = $(SDE_PATH) -- ./$(EXE) gensfen depth 3 loop 1000 sfen_format bin output_file_name $(PGO_TRAINING_DATA_FILE)
endif
### Source and object files
SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \
@@ -377,9 +379,11 @@ ifeq ($(COMP),clang)
ifneq ($(KERNEL),Darwin)
ifneq ($(KERNEL),OpenBSD)
ifneq ($(KERNEL),FreeBSD)
LDFLAGS += -latomic
endif
endif
endif
ifeq ($(arch),$(filter $(arch),armv7 armv8))
ifeq ($(OS),Android)
@@ -512,6 +516,10 @@ ifeq ($(optimize),yes)
CXXFLAGS += -mdynamic-no-pic
endif
endif
ifeq ($(comp),clang)
CXXFLAGS += -fexperimental-new-pass-manager
endif
endif
### 3.4 Bits
@@ -621,7 +629,7 @@ endif
ifeq ($(optimize),yes)
ifeq ($(debug), no)
ifeq ($(comp),clang)
CXXFLAGS += -flto=thin
CXXFLAGS += -flto
ifneq ($(findstring MINGW,$(KERNEL)),)
CXXFLAGS += -fuse-ld=lld
else ifneq ($(findstring MSYS,$(KERNEL)),)
@@ -641,7 +649,7 @@ ifeq ($(debug), no)
LDFLAGS += -save-temps
endif
else
CXXFLAGS += -flto=thin
CXXFLAGS += -flto
LDFLAGS += $(CXXFLAGS)
endif
+5 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -92,6 +92,8 @@ const vector<string> Defaults = {
} // namespace
namespace Stockfish {
/// setup_bench() builds a list of UCI commands to be run by bench. There
/// are five parameters: TT size in MB, number of search threads that
/// should be used, the limit value spent for each position, a file name
@@ -168,3 +170,5 @@ vector<string> setup_bench(const Position& current, istream& is) {
return list;
}
} // namespace Stockfish
+7 -5
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,6 +23,8 @@
#include "bitboard.h"
#include "types.h"
namespace Stockfish {
namespace {
// There are 24 possible pawn squares: files A to D and ranks from 2 to 7.
@@ -66,7 +68,6 @@ namespace {
} // namespace
bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) {
assert(file_of(wpsq) <= FILE_D);
@@ -96,7 +97,6 @@ void Bitbases::init() {
KPKBitbase.set(idx);
}
namespace {
KPKPosition::KPKPosition(unsigned idx) {
@@ -150,8 +150,8 @@ namespace {
Bitboard b = attacks_bb<KING>(ksq[stm]);
while (b)
r |= stm == WHITE ? db[index(BLACK, ksq[BLACK] , pop_lsb(&b), psq)]
: db[index(WHITE, pop_lsb(&b), ksq[WHITE], psq)];
r |= stm == WHITE ? db[index(BLACK, ksq[BLACK], pop_lsb(b), psq)]
: db[index(WHITE, pop_lsb(b), ksq[WHITE], psq)];
if (stm == WHITE)
{
@@ -168,3 +168,5 @@ namespace {
}
} // namespace
} // namespace Stockfish
+15 -6
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,11 +22,14 @@
#include "bitboard.h"
#include "misc.h"
namespace Stockfish {
uint8_t PopCnt16[1 << 16];
uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
Bitboard SquareBB[SQUARE_NB];
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
@@ -42,7 +45,6 @@ namespace {
}
/// safe_destination() returns the bitboard of target square for the given step
/// from the given square. If the step is off the board, returns empty bitboard.
@@ -55,7 +57,7 @@ inline Bitboard safe_destination(Square s, int step) {
/// Bitboards::pretty() returns an ASCII representation of a bitboard suitable
/// to be printed to standard output. Useful for debugging.
const std::string Bitboards::pretty(Bitboard b) {
std::string Bitboards::pretty(Bitboard b) {
std::string s = "+---+---+---+---+---+---+---+---+\n";
@@ -106,12 +108,17 @@ void Bitboards::init() {
for (PieceType pt : { BISHOP, ROOK })
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
{
if (PseudoAttacks[pt][s1] & s2)
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
{
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
BetweenBB[s1][s2] = (attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1)));
}
BetweenBB[s1][s2] |= s2;
}
}
}
namespace {
Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
@@ -123,7 +130,7 @@ namespace {
for (Direction d : (pt == ROOK ? RookDirections : BishopDirections))
{
Square s = sq;
while(safe_destination(s, d) && !(occupied & s))
while (safe_destination(s, d) && !(occupied & s))
attacks |= (s += d);
}
@@ -211,3 +218,5 @@ namespace {
}
}
}
} // namespace Stockfish
+34 -16
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,19 +23,21 @@
#include "types.h"
namespace Stockfish {
namespace Bitbases {
void init();
bool probe(Square wksq, Square wpsq, Square bksq, Color us);
}
} // namespace Stockfish::Bitbases
namespace Bitboards {
void init();
const std::string pretty(Bitboard b);
std::string pretty(Bitboard b);
}
} // namespace Stockfish::Bitboards
constexpr Bitboard AllSquares = ~Bitboard(0);
constexpr Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
@@ -73,6 +75,7 @@ extern uint8_t PopCnt16[1 << 16];
extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
@@ -209,23 +212,29 @@ constexpr Bitboard adjacent_files_bb(Square s) {
inline Bitboard line_bb(Square s1, Square s2) {
assert(is_ok(s1) && is_ok(s2));
return LineBB[s1][s2];
}
/// between_bb() returns a bitboard representing squares that are linearly
/// between the two given squares (excluding the given squares). If the given
/// squares are not on a same file/rank/diagonal, we return 0. For instance,
/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5 and E6.
/// between_bb(s1, s2) returns a bitboard representing the squares in the semi-open
/// segment between the squares s1 and s2 (excluding s1 but including s2). If the
/// given squares are not on a same file/rank/diagonal, it returns s2. For instance,
/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5, E6 and F7, but
/// between_bb(SQ_E6, SQ_F8) will return a bitboard with the square F8. This trick
/// allows to generate non-king evasion moves faster: the defending piece must either
/// interpose itself to cover the check or capture the checking piece.
inline Bitboard between_bb(Square s1, Square s2) {
Bitboard b = line_bb(s1, s2) & ((AllSquares << s1) ^ (AllSquares << s2));
return b & (b - 1); //exclude lsb
assert(is_ok(s1) && is_ok(s2));
return BetweenBB[s1][s2];
}
/// 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() 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.
constexpr Bitboard forward_ranks_bb(Color c, Square s) {
@@ -412,13 +421,20 @@ inline Square msb(Bitboard b) {
#endif
/// least_significant_square_bb() returns the bitboard of the least significant
/// square of a non-zero bitboard. It is equivalent to square_bb(lsb(bb)).
inline Bitboard least_significant_square_bb(Bitboard b) {
assert(b);
return b & -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;
inline Square pop_lsb(Bitboard& b) {
assert(b);
const Square s = lsb(b);
b &= b - 1;
return s;
}
@@ -430,4 +446,6 @@ inline Square frontmost_sq(Color c, Bitboard b) {
return c == WHITE ? msb(b) : lsb(b);
}
} // namespace Stockfish
#endif // #ifndef BITBOARD_H_INCLUDED
+9 -5
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,6 +22,8 @@
#include "endgame.h"
#include "movegen.h"
namespace Stockfish {
namespace {
// Used to drive the king towards the edge of the board
@@ -553,8 +555,8 @@ ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 2));
assert(verify_material(pos, weakSide, RookValueMg, 1));
Square strongPawn1 = pos.squares<PAWN>(strongSide)[0];
Square strongPawn2 = pos.squares<PAWN>(strongSide)[1];
Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN));
Square strongPawn2 = msb(pos.pieces(strongSide, PAWN));
Square weakKing = pos.square<KING>(weakSide);
// Does the stronger side have a passed pawn?
@@ -638,8 +640,8 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
return SCALE_FACTOR_NONE;
Square weakKing = pos.square<KING>(weakSide);
Square strongPawn1 = pos.squares<PAWN>(strongSide)[0];
Square strongPawn2 = pos.squares<PAWN>(strongSide)[1];
Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN));
Square strongPawn2 = msb(pos.pieces(strongSide, PAWN));
Square blockSq1, blockSq2;
if (relative_rank(strongSide, strongPawn1) > relative_rank(strongSide, strongPawn2))
@@ -741,3 +743,5 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
// it's probably at least a draw even with the pawn.
return Bitbases::probe(strongKing, strongPawn, weakKing, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
}
} // namespace Stockfish
+4 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@
#include "position.h"
#include "types.h"
namespace Stockfish {
/// EndgameCode lists all supported endgame functions by corresponding codes
@@ -120,4 +121,6 @@ namespace Endgames {
}
}
} // namespace Stockfish
#endif // #ifndef ENDGAME_H_INCLUDED
+137 -56
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -122,7 +122,7 @@ namespace {
// BishopPawns[distance from edge] contains a file-dependent penalty for pawns on
// squares of the same color as our bishop.
constexpr Score BishopPawns[int(FILE_NB) / 2] = {
S(3, 8), S(3, 9), S(1, 8), S(3, 7)
S(3, 8), S(3, 9), S(2, 8), S(3, 8)
};
// KingProtector[knight/bishop] contains penalty for each distance unit to own king
@@ -130,16 +130,15 @@ namespace {
// Outpost[knight/bishop] contains bonuses for each knight or bishop occupying a
// pawn protected square on rank 4 to 6 which is also safe from a pawn attack.
constexpr Score Outpost[] = { S(56, 34), S(31, 23) };
constexpr Score Outpost[] = { S(57, 38), S(31, 24) };
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn
constexpr Score PassedRank[RANK_NB] = {
S(0, 0), S(9, 28), S(15, 31), S(17, 39), S(64, 70), S(171, 177), S(277, 260)
S(0, 0), S(7, 27), S(16, 32), S(17, 40), S(64, 71), S(170, 174), S(278, 262)
};
// RookOnFile[semiopen/open] contains bonuses for each rook when there is
// no (friendly) pawn on the rook file.
constexpr Score RookOnFile[] = { S(19, 7), S(48, 27) };
constexpr Score RookOnClosedFile = S(10, 5);
constexpr Score RookOnOpenFile[] = { S(19, 6), S(47, 26) };
// ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to
// which piece type attacks which one. Attacks on lesser pieces which are
@@ -152,11 +151,12 @@ namespace {
S(0, 0), S(3, 44), S(37, 68), S(42, 60), S(0, 39), S(58, 43)
};
constexpr Value CorneredBishop = Value(50);
// Assorted bonuses and penalties
constexpr Score BadOutpost = S( -7, 36);
constexpr Score UncontestedOutpost = S( 1, 10);
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 KnightOnQueen = S( 16, 11);
@@ -285,15 +285,16 @@ namespace {
constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
: Rank5BB | Rank4BB | Rank3BB);
const Square* pl = pos.squares<Pt>(Us);
Bitboard b1 = pos.pieces(Us, Pt);
Bitboard b, bb;
Score score = SCORE_ZERO;
attackedBy[Us][Pt] = 0;
for (Square s = *pl; s != SQ_NONE; s = *++pl)
while (b1)
{
Square s = pop_lsb(b1);
// Find attacked squares, including x-ray attacks for bishops and rooks
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))
@@ -320,13 +321,12 @@ namespace {
score += BishopOnKingRing;
int mob = popcount(b & mobilityArea[Us]);
mobility[Us] += MobilityBonus[Pt - 2][mob];
if (Pt == BISHOP || Pt == KNIGHT)
{
// Bonus if the piece is on an outpost square or can reach one
// Reduced bonus for knights (BadOutpost) if few relevant targets
// Bonus for knights (UncontestedOutpost) if few relevant targets
bb = OutpostRanks & (attackedBy[Us][PAWN] | shift<Down>(pos.pieces(PAWN)))
& ~pe->pawn_attacks_span(Them);
Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN);
@@ -335,7 +335,7 @@ namespace {
&& bb & s & ~CenterFiles // on a side outpost
&& !(b & targets) // no relevant attacks
&& (!more_than_one(targets & (s & QueenSide ? QueenSide : KingSide))))
score += BadOutpost;
score += UncontestedOutpost * popcount(pos.pieces(PAWN) & (s & QueenSide ? QueenSide : KingSide));
else if (bb & s)
score += Outpost[Pt == BISHOP];
else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
@@ -348,7 +348,7 @@ namespace {
// Penalty if the piece is far from the king
score -= KingProtector[Pt == BISHOP] * distance(pos.square<KING>(Us), s);
if (Pt == BISHOP)
if constexpr (Pt == BISHOP)
{
// Penalty according to the number of our pawns on the same color square as the
// bishop, bigger when the center files are blocked with pawns and smaller
@@ -373,29 +373,40 @@ namespace {
{
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;
score -= !pos.empty(s + d + pawn_push(Us)) ? 4 * make_score(CorneredBishop, CorneredBishop)
: 3 * make_score(CorneredBishop, CorneredBishop);
}
}
}
if (Pt == ROOK)
if constexpr (Pt == ROOK)
{
// Bonus for rook on an open or semi-open file
// Bonuses for rook on a (semi-)open or closed file
if (pos.is_on_semiopen_file(Us, s))
score += RookOnFile[pos.is_on_semiopen_file(Them, s)];
// Penalty when trapped by the king, even more if the king cannot castle
else if (mob <= 3)
{
File kf = file_of(pos.square<KING>(Us));
if ((kf < FILE_E) == (file_of(s) < kf))
score -= TrappedRook * (1 + !pos.castling_rights(Us));
score += RookOnOpenFile[pos.is_on_semiopen_file(Them, s)];
}
else
{
// If our pawn on this file is blocked, increase penalty
if ( pos.pieces(Us, PAWN)
& shift<Down>(pos.pieces())
& file_bb(s))
{
score -= RookOnClosedFile;
}
// Penalty when trapped by the king, even more if the king cannot castle
if (mob <= 3)
{
File kf = file_of(pos.square<KING>(Us));
if ((kf < FILE_E) == (file_of(s) < kf))
score -= TrappedRook * (1 + !pos.castling_rights(Us));
}
}
}
if (Pt == QUEEN)
if constexpr (Pt == QUEEN)
{
// Penalty if any relative pin or discovered attack against the queen
Bitboard queenPinners;
@@ -403,7 +414,7 @@ namespace {
score -= WeakQueen;
}
}
if (T)
if constexpr (T)
Trace::add(Pt, Us, score);
return score;
@@ -480,7 +491,7 @@ namespace {
int kingFlankDefense = popcount(b3);
kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] // (~10 Elo)
+ 185 * popcount(kingRing[Us] & weak) // (~15 Elo)
+ 183 * popcount(kingRing[Us] & weak) // (~15 Elo)
+ 148 * popcount(unsafeChecks) // (~4 Elo)
+ 98 * popcount(pos.blockers_for_king(Us)) // (~2 Elo)
+ 69 * kingAttacksCount[Them] // (~0.5 Elo)
@@ -503,7 +514,7 @@ namespace {
// Penalty if king flank is under attack, potentially moving toward the king
score -= FlankAttacks * kingFlankAttack;
if (T)
if constexpr (T)
Trace::add(KING, Us, score);
return score;
@@ -542,11 +553,11 @@ namespace {
{
b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]);
while (b)
score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(&b)))];
score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(b)))];
b = weak & attackedBy[Us][ROOK];
while (b)
score += ThreatByRook[type_of(pos.piece_on(pop_lsb(&b)))];
score += ThreatByRook[type_of(pos.piece_on(pop_lsb(b)))];
if (weak & attackedBy[Us][KING])
score += ThreatByKing;
@@ -604,7 +615,7 @@ namespace {
score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance);
}
if (T)
if constexpr (T)
Trace::add(THREAT, Us, score);
return score;
@@ -644,7 +655,7 @@ namespace {
while (b)
{
Square s = pop_lsb(&b);
Square s = pop_lsb(b);
assert(!(pos.pieces(Them, PAWN) & forward_file_bb(Us, s + Up)));
@@ -674,14 +685,16 @@ namespace {
bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN);
if (!(pos.pieces(Them) & bb))
unsafeSquares &= attackedBy[Them][ALL_PIECES];
unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
// If there are no enemy attacks on passed pawn span, assign a big bonus.
// If there are no enemy pieces or attacks on passed pawn span, assign a big bonus.
// Or if there is some, but they are all attacked by our pawns, assign a bit smaller bonus.
// Otherwise assign a smaller bonus if the path to queen is not attacked
// and even smaller bonus if it is attacked but block square is not.
int k = !unsafeSquares ? 35 :
!(unsafeSquares & squaresToQueen) ? 20 :
!(unsafeSquares & blockSq) ? 9 :
int k = !unsafeSquares ? 36 :
!(unsafeSquares & ~attackedBy[Us][PAWN]) ? 30 :
!(unsafeSquares & squaresToQueen) ? 17 :
!(unsafeSquares & blockSq) ? 7 :
0 ;
// Assign a larger bonus if the block square is defended
@@ -695,7 +708,7 @@ namespace {
score += bonus - PassedFile * edge_distance(file_of(s));
}
if (T)
if constexpr (T)
Trace::add(PASSED, Us, score);
return score;
@@ -730,11 +743,13 @@ namespace {
behind |= shift<Down>(behind);
behind |= shift<Down+Down>(behind);
// Compute space score based on the number of safe squares and number of our pieces
// increased with number of total blocked pawns in position.
int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]);
int weight = pos.count<ALL_PIECES>(Us) - 3 + std::min(pe->blocked_count(), 9);
Score score = make_score(bonus * weight * weight / 16, 0);
if (T)
if constexpr (T)
Trace::add(SPACE, Us, score);
return score;
@@ -749,7 +764,7 @@ namespace {
Value Evaluation<T>::winnable(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));
+ int(rank_of(pos.square<KING>(WHITE)) - rank_of(pos.square<KING>(BLACK)));
bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide)
&& (pos.pieces(PAWN) & KingSide);
@@ -791,24 +806,36 @@ namespace {
{
if (pos.opposite_bishops())
{
// For pure opposite colored bishops endgames use scale factor
// based on the number of passed pawns of the strong side.
if ( pos.non_pawn_material(WHITE) == BishopValueMg
&& pos.non_pawn_material(BLACK) == BishopValueMg)
sf = 18 + 4 * popcount(pe->passed_pawns(strongSide));
// For every other opposite colored bishops endgames use scale factor
// based on the number of all pieces of the strong side.
else
sf = 22 + 3 * pos.count<ALL_PIECES>(strongSide);
}
// For rook endgames with strong side not having overwhelming pawn number advantage
// and its pawns being on one flank and weak side protecting its pieces with a king
// use lower scale factor.
else if ( pos.non_pawn_material(WHITE) == RookValueMg
&& pos.non_pawn_material(BLACK) == RookValueMg
&& pos.count<PAWN>(strongSide) - pos.count<PAWN>(~strongSide) <= 1
&& bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN))
&& (attacks_bb<KING>(pos.square<KING>(~strongSide)) & pos.pieces(~strongSide, PAWN)))
sf = 36;
// For queen vs no queen endgames use scale factor
// based on number of minors of side that doesn't have queen.
else if (pos.count<QUEEN>() == 1)
sf = 37 + 3 * (pos.count<QUEEN>(WHITE) == 1 ? pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK)
: pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE));
// In every other case use scale factor based on
// the number of pawns of the strong side reduced if pawns are on a single flank.
else
sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide)) - 4 * !pawnsOnBothFlanks;
// Reduce scale factor in case of pawns being on a single flank
sf -= 4 * !pawnsOnBothFlanks;
}
@@ -817,7 +844,7 @@ namespace {
+ eg * int(PHASE_MIDGAME - me->game_phase()) * ScaleFactor(sf) / SCALE_FACTOR_NORMAL;
v /= PHASE_MIDGAME;
if (T)
if constexpr (T)
{
Trace::add(WINNABLE, make_score(u, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL - eg_value(score)));
Trace::add(TOTAL, make_score(mg, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL));
@@ -889,7 +916,7 @@ make_v:
Value v = winnable(score);
// In case of tracing add all remaining individual evaluation terms
if (T)
if constexpr (T)
{
Trace::add(MATERIAL, pos.psq_score());
Trace::add(IMBALANCE, me->imbalance());
@@ -906,7 +933,43 @@ make_v:
return v;
}
} // namespace
/// Fisher Random Chess: correction for cornered bishops, to fix chess960 play with NNUE
Value fix_FRC(const Position& pos) {
constexpr Bitboard Corners = 1ULL << SQ_A1 | 1ULL << SQ_H1 | 1ULL << SQ_A8 | 1ULL << SQ_H8;
if (!(pos.pieces(BISHOP) & Corners))
return VALUE_ZERO;
int correction = 0;
if ( pos.piece_on(SQ_A1) == W_BISHOP
&& pos.piece_on(SQ_B2) == W_PAWN)
correction += !pos.empty(SQ_B3) ? -CorneredBishop * 4
: -CorneredBishop * 3;
if ( pos.piece_on(SQ_H1) == W_BISHOP
&& pos.piece_on(SQ_G2) == W_PAWN)
correction += !pos.empty(SQ_G3) ? -CorneredBishop * 4
: -CorneredBishop * 3;
if ( pos.piece_on(SQ_A8) == B_BISHOP
&& pos.piece_on(SQ_B7) == B_PAWN)
correction += !pos.empty(SQ_B6) ? CorneredBishop * 4
: CorneredBishop * 3;
if ( pos.piece_on(SQ_H8) == B_BISHOP
&& pos.piece_on(SQ_G7) == B_PAWN)
correction += !pos.empty(SQ_G6) ? CorneredBishop * 4
: CorneredBishop * 3;
return pos.side_to_move() == WHITE ? Value(correction)
: -Value(correction);
}
} // namespace Eval
/// evaluate() is the evaluator for the outer world. It returns a static
@@ -931,25 +994,41 @@ Value Eval::evaluate(const Position& pos) {
else
{
// Scale and shift NNUE for compatibility with search and classical evaluation
auto adjusted_NNUE = [&](){
int mat = pos.non_pawn_material() + PawnValueMg * pos.count<PAWN>();
return NNUE::evaluate(pos) * (720 + mat / 32) / 1024 + Tempo;
auto adjusted_NNUE = [&]()
{
int material = pos.non_pawn_material() + 4 * PawnValueMg * pos.count<PAWN>();
int scale = 580
+ material / 32
- 4 * pos.rule50_count();
Value nnue = NNUE::evaluate(pos) * scale / 1024 + Tempo;
if (pos.is_chess960())
nnue += fix_FRC(pos);
return nnue;
};
// If there is PSQ imbalance use classical eval, with small probability if it is small
// If there is PSQ imbalance we use the classical eval. We also introduce
// a small probability of using the classical eval when PSQ imbalance is small.
Value psq = Value(abs(eg_value(pos.psq_score())));
int r50 = 16 + pos.rule50_count();
bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50;
bool classical = largePsq || (psq > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB));
bool strongClassical = pos.non_pawn_material() < 2 * RookValueMg && pos.count<PAWN>() < 2;
// Use classical evaluation for really low piece endgames.
// One critical case is the draw for bishop + A/H file pawn vs naked king.
bool lowPieceEndgame = pos.non_pawn_material() == BishopValueMg
|| (pos.non_pawn_material() < 2 * RookValueMg && pos.count<PAWN>() < 2);
v = classical || strongClassical ? Evaluation<NO_TRACE>(pos).value() : adjusted_NNUE();
v = classical || lowPieceEndgame ? Evaluation<NO_TRACE>(pos).value()
: adjusted_NNUE();
// If the classical eval is small and imbalance large, use NNUE nevertheless.
// For the case of opposite colored bishops, switch to NNUE eval with
// small probability if the classical eval is less than the threshold.
if ( largePsq && !strongClassical
// For the case of opposite colored bishops, switch to NNUE eval with small
// probability if the classical eval is less than the threshold.
if ( largePsq
&& !lowPieceEndgame
&& ( abs(v) * 16 < NNUEThreshold2 * r50
|| ( pos.opposite_bishops()
&& abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50
@@ -1024,3 +1103,5 @@ std::string Eval::trace(const Position& pos) {
return ss.str();
}
} // namespace Stockfish
+6 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,6 +23,8 @@
#include "types.h"
namespace Stockfish {
class Position;
namespace Eval {
@@ -32,8 +34,10 @@ namespace Eval {
// The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue
// for the build process (profile-build and fishtest) to work. Do not change the
// name of the macro, as it is used in the Makefile.
#define EvalFileDefaultName "nn-c3ca321c51c9.nnue"
#define EvalFileDefaultName "nn-62ef826d1a6d.nnue"
} // namespace Eval
} // namespace Stockfish
#endif // #ifndef EVALUATE_H_INCLUDED
+4 -5
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,15 +23,14 @@
#include "bitboard.h"
#include "endgame.h"
#include "position.h"
#include "psqt.h"
#include "search.h"
#include "syzygy/tbprobe.h"
#include "thread.h"
#include "tt.h"
#include "uci.h"
#include "syzygy/tbprobe.h"
namespace PSQT {
void init();
}
using namespace Stockfish;
int main(int argc, char* argv[]) {
+32 -23
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,32 +24,39 @@
using namespace std;
namespace Stockfish {
namespace {
#define S(mg, eg) make_score(mg, eg)
// Polynomial material imbalance parameters
constexpr int QuadraticOurs[][PIECE_TYPE_NB] = {
// OUR PIECES
// pair pawn knight bishop rook queen
{1438 }, // Bishop pair
{ 40, 38 }, // Pawn
{ 32, 255, -62 }, // Knight OUR PIECES
{ 0, 104, 4, 0 }, // Bishop
{ -26, -2, 47, 105, -208 }, // Rook
{-189, 24, 117, 133, -134, -6 } // Queen
// One Score parameter for each pair (our piece, another of our pieces)
constexpr Score QuadraticOurs[][PIECE_TYPE_NB] = {
// OUR PIECE 2
// bishop pair pawn knight bishop rook queen
{S(1419, 1455) }, // Bishop pair
{S( 101, 28), S( 37, 39) }, // Pawn
{S( 57, 64), S(249, 187), S(-49, -62) }, // Knight OUR PIECE 1
{S( 0, 0), S(118, 137), S( 10, 27), S( 0, 0) }, // Bishop
{S( -63, -68), S( -5, 3), S(100, 81), S(132, 118), S(-246, -244) }, // Rook
{S(-210, -211), S( 37, 14), S(147, 141), S(161, 105), S(-158, -174), S(-9,-31) } // Queen
};
constexpr int QuadraticTheirs[][PIECE_TYPE_NB] = {
// THEIR PIECES
// pair pawn knight bishop rook queen
{ }, // Bishop pair
{ 36, }, // Pawn
{ 9, 63, }, // Knight OUR PIECES
{ 59, 65, 42, }, // Bishop
{ 46, 39, 24, -24, }, // Rook
{ 97, 100, -42, 137, 268, } // Queen
// One Score parameter for each pair (our piece, their piece)
constexpr Score QuadraticTheirs[][PIECE_TYPE_NB] = {
// THEIR PIECE
// bishop pair pawn knight bishop rook queen
{ }, // Bishop pair
{S( 33, 30) }, // Pawn
{S( 46, 18), S(106, 84) }, // Knight OUR PIECE
{S( 75, 35), S( 59, 44), S( 60, 15) }, // Bishop
{S( 26, 35), S( 6, 22), S( 38, 39), S(-12, -2) }, // Rook
{S( 97, 93), S(100, 163), S(-58, -91), S(112, 192), S(276, 225) } // Queen
};
#undef S
// Endgame evaluation and scaling functions are accessed directly and not through
// the function maps because they correspond to more than one material hash key.
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
@@ -67,7 +74,7 @@ namespace {
bool is_KBPsK(const Position& pos, Color us) {
return pos.non_pawn_material(us) == BishopValueMg
&& pos.count<PAWN >(us) >= 1;
&& pos.count<PAWN>(us) >= 1;
}
bool is_KQKRPs(const Position& pos, Color us) {
@@ -82,11 +89,11 @@ namespace {
/// piece type for both colors.
template<Color Us>
int imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
Score imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
constexpr Color Them = ~Us;
int bonus = 0;
Score bonus = SCORE_ZERO;
// Second-degree polynomial material imbalance, by Tord Romstad
for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
@@ -213,8 +220,10 @@ Entry* probe(const Position& pos) {
{ pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK),
pos.count<BISHOP>(BLACK) , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } };
e->value = int16_t((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
e->score = (imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16;
return e;
}
} // namespace Material
} // namespace Stockfish
+7 -7
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,7 +24,7 @@
#include "position.h"
#include "types.h"
namespace Material {
namespace Stockfish::Material {
/// Material::Entry contains various information about a material configuration.
/// It contains a material imbalance evaluation, a function pointer to a special
@@ -37,8 +37,8 @@ namespace Material {
struct Entry {
Score imbalance() const { return make_score(value, value); }
Phase game_phase() const { return gamePhase; }
Score imbalance() const { return score; }
Phase game_phase() const { return (Phase)gamePhase; }
bool specialized_eval_exists() const { return evaluationFunction != nullptr; }
Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); }
@@ -57,15 +57,15 @@ struct Entry {
const EndgameBase<Value>* evaluationFunction;
const EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each
// side (e.g. KPKP, KBPsK)
int16_t value;
Score score;
int16_t gamePhase;
uint8_t factor[COLOR_NB];
Phase gamePhase;
};
typedef HashTable<Entry, 8192> Table;
Entry* probe(const Position& pos);
} // namespace Material
} // namespace Stockfish::Material
#endif // #ifndef MATERIAL_H_INCLUDED
+19 -7
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -63,6 +63,8 @@ using namespace std;
SynchronizedRegionLogger sync_region_cout(std::cout);
namespace Stockfish {
namespace {
/// Version number. If Version is left empty, then compile date in the format
@@ -140,7 +142,7 @@ public:
/// the program was compiled) or "Stockfish <Version>", depending on whether
/// Version is empty.
const string engine_info(bool to_uci) {
string engine_info(bool to_uci) {
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
string month, day, year;
@@ -163,7 +165,7 @@ const string engine_info(bool to_uci) {
/// compiler_info() returns a string trying to describe the compiler we use
const std::string compiler_info() {
std::string compiler_info() {
#define stringify2(x) #x
#define stringify(x) stringify2(x)
@@ -363,7 +365,11 @@ void std_aligned_free(void* ptr) {
#if defined(_WIN32)
static void* aligned_large_pages_alloc_win(size_t allocSize) {
static void* aligned_large_pages_alloc_windows(size_t allocSize) {
#if !defined(_WIN64)
return nullptr;
#else
HANDLE hProcessToken { };
LUID luid { };
@@ -406,12 +412,14 @@ static void* aligned_large_pages_alloc_win(size_t allocSize) {
CloseHandle(hProcessToken);
return mem;
#endif
}
void* aligned_large_pages_alloc(size_t allocSize) {
// Try to allocate large pages
void* mem = aligned_large_pages_alloc_win(allocSize);
void* mem = aligned_large_pages_alloc_windows(allocSize);
// Fall back to regular, page aligned, allocation if necessary
if (!mem)
@@ -451,8 +459,9 @@ void aligned_large_pages_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;
std::cerr << "Failed to free large page memory. Error code: 0x"
<< std::hex << err
<< std::dec << std::endl;
exit(EXIT_FAILURE);
}
}
@@ -628,6 +637,7 @@ void init(int argc, char* argv[]) {
} // namespace CommandLine
// Returns a string that represents the current time. (Used when learning evaluation functions)
std::string now_string()
{
@@ -734,3 +744,5 @@ int write_memory_to_file(std::string filename, void* ptr, uint64_t size)
fs.close();
return 0;
}
} // namespace Stockfish
+7 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -36,8 +36,10 @@
#include "types.h"
const std::string engine_info(bool to_uci = false);
const std::string compiler_info();
namespace Stockfish {
std::string engine_info(bool to_uci = false);
std::string compiler_info();
void prefetch(void* addr);
void start_logger(const std::string& fname);
void* std_aligned_alloc(size_t alignment, size_t size);
@@ -681,4 +683,6 @@ namespace CommandLine {
extern std::string workingDirectory; // path of the working directory
}
} // namespace Stockfish
#endif // #ifndef MISC_H_INCLUDED
+49 -63
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,6 +21,8 @@
#include "movegen.h"
#include "position.h"
namespace Stockfish {
namespace {
template<GenType Type, Direction D>
@@ -61,7 +63,7 @@ namespace {
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target:
Bitboard enemies = (Type == EVASIONS ? pos.checkers():
Type == CAPTURES ? target : pos.pieces(Them));
// Single and double pawn pushes, no promotions
@@ -85,7 +87,7 @@ namespace {
// 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
// don't generate captures. Note that a possible discovered check
// promotion has been already generated amongst the captures.
Bitboard dcCandidateQuiets = pos.blockers_for_king(Them) & pawnsNotOn7;
if (dcCandidateQuiets)
@@ -100,13 +102,13 @@ namespace {
while (b1)
{
Square to = pop_lsb(&b1);
Square to = pop_lsb(b1);
*moveList++ = make_move(to - Up, to);
}
while (b2)
{
Square to = pop_lsb(&b2);
Square to = pop_lsb(b2);
*moveList++ = make_move(to - Up - Up, to);
}
}
@@ -125,16 +127,16 @@ namespace {
Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares;
while (b1)
moveList = make_promotions<Type, UpRight>(moveList, pop_lsb(&b1), ksq);
moveList = make_promotions<Type, UpRight>(moveList, pop_lsb(b1), ksq);
while (b2)
moveList = make_promotions<Type, UpLeft >(moveList, pop_lsb(&b2), ksq);
moveList = make_promotions<Type, UpLeft >(moveList, pop_lsb(b2), ksq);
while (b3)
moveList = make_promotions<Type, Up >(moveList, pop_lsb(&b3), ksq);
moveList = make_promotions<Type, Up >(moveList, pop_lsb(b3), ksq);
}
// Standard and en-passant captures
// Standard and en passant captures
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{
Bitboard b1 = shift<UpRight>(pawnsNotOn7) & enemies;
@@ -142,13 +144,13 @@ namespace {
while (b1)
{
Square to = pop_lsb(&b1);
Square to = pop_lsb(b1);
*moveList++ = make_move(to - UpRight, to);
}
while (b2)
{
Square to = pop_lsb(&b2);
Square to = pop_lsb(b2);
*moveList++ = make_move(to - UpLeft, to);
}
@@ -156,10 +158,8 @@ namespace {
{
assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6));
// An en passant capture can be an evasion only if the checking piece
// is the double pushed pawn and so is in the target. Otherwise this
// is a discovery check and we are forced to do otherwise.
if (Type == EVASIONS && !(target & (pos.ep_square() - Up)))
// An en passant capture cannot resolve a discovered check
if (Type == EVASIONS && (target & (pos.ep_square() + Up)))
return moveList;
b1 = pawnsNotOn7 & pawn_attacks_bb(Them, pos.ep_square());
@@ -167,7 +167,7 @@ namespace {
assert(b1);
while (b1)
*moveList++ = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
*moveList++ = make<EN_PASSANT>(pop_lsb(b1), pos.ep_square());
}
}
@@ -175,32 +175,23 @@ namespace {
}
template<Color Us, PieceType Pt, bool Checks>
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
template<PieceType Pt, bool Checks>
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard piecesToMove, Bitboard target) {
static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()");
const Square* pl = pos.squares<Pt>(Us);
Bitboard bb = piecesToMove & pos.pieces(Pt);
for (Square from = *pl; from != SQ_NONE; from = *++pl)
while (bb)
{
if (Checks)
{
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
&& !(attacks_bb<Pt>(from) & target & pos.check_squares(Pt)))
continue;
if (pos.blockers_for_king(~Us) & from)
continue;
}
Square from = pop_lsb(bb);
Bitboard b = attacks_bb<Pt>(from, pos.pieces()) & target;
if (Checks)
if constexpr (Checks)
b &= pos.check_squares(Pt);
while (b)
*moveList++ = make_move(from, pop_lsb(&b));
*moveList++ = make_move(from, pop_lsb(b));
}
return moveList;
@@ -209,8 +200,14 @@ namespace {
template<Color Us, GenType Type>
ExtMove* generate_all(const Position& pos, ExtMove* moveList) {
constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations
Bitboard target;
static_assert(Type != LEGAL, "Unsupported type in generate_all()");
constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations
Bitboard target, piecesToMove = pos.pieces(Us);
if(Type == QUIET_CHECKS)
piecesToMove &= ~pos.blockers_for_king(~Us);
switch (Type)
{
@@ -222,30 +219,25 @@ namespace {
target = ~pos.pieces();
break;
case EVASIONS:
{
Square checksq = lsb(pos.checkers());
target = between_bb(pos.square<KING>(Us), checksq) | checksq;
target = between_bb(pos.square<KING>(Us), lsb(pos.checkers()));
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<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);
moveList = generate_moves<KNIGHT, Checks>(pos, moveList, piecesToMove, target);
moveList = generate_moves<BISHOP, Checks>(pos, moveList, piecesToMove, target);
moveList = generate_moves< ROOK, Checks>(pos, moveList, piecesToMove, target);
moveList = generate_moves< QUEEN, Checks>(pos, moveList, piecesToMove, target);
if (Type != QUIET_CHECKS && Type != EVASIONS)
{
Square ksq = pos.square<KING>(Us);
Bitboard b = attacks_bb<KING>(ksq) & target;
while (b)
*moveList++ = make_move(ksq, pop_lsb(&b));
*moveList++ = make_move(ksq, pop_lsb(b));
if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING))
for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } )
@@ -260,7 +252,7 @@ namespace {
/// <CAPTURES> Generates all pseudo-legal captures plus queen and checking knight promotions
/// <QUIETS> Generates all pseudo-legal non-captures and underpromotions(except checking knight)
/// <QUIETS> Generates all pseudo-legal non-captures and underpromotions (except checking knight)
/// <NON_EVASIONS> Generates all pseudo-legal captures and non-captures
///
/// Returns a pointer to the end of the move list.
@@ -283,8 +275,8 @@ template ExtMove* generate<QUIETS>(const Position&, ExtMove*);
template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
/// generate<QUIET_CHECKS> generates all pseudo-legal non-captures.
/// Returns a pointer to the end of the move list.
/// generate<QUIET_CHECKS> generates all pseudo-legal non-captures giving check,
/// except castling. Returns a pointer to the end of the move list.
template<>
ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
@@ -295,7 +287,7 @@ ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
while (dc)
{
Square from = pop_lsb(&dc);
Square from = pop_lsb(dc);
PieceType pt = type_of(pos.piece_on(from));
Bitboard b = attacks_bb(pt, from, pos.pieces()) & ~pos.pieces();
@@ -304,7 +296,7 @@ ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
b &= ~attacks_bb<QUEEN>(pos.square<KING>(~us));
while (b)
*moveList++ = make_move(from, pop_lsb(&b));
*moveList++ = make_move(from, pop_lsb(b));
}
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList)
@@ -321,24 +313,16 @@ ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* moveList) {
Color us = pos.side_to_move();
Square ksq = pos.square<KING>(us);
Bitboard sliderAttacks = 0;
Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN);
// Find all the squares attacked by slider checkers. We will remove them from
// the king evasions in order to skip known illegal moves, which avoids any
// useless legality checks later on.
while (sliders)
sliderAttacks |= line_bb(ksq, pop_lsb(&sliders)) & ~pos.checkers();
// Generate evasions for king, capture and non capture moves
Bitboard b = attacks_bb<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
// Generate evasions for king
Bitboard b = attacks_bb<KING>(ksq) & ~pos.pieces(us);
while (b)
*moveList++ = make_move(ksq, pop_lsb(&b));
*moveList++ = make_move(ksq, pop_lsb(b));
if (more_than_one(pos.checkers()))
return moveList; // Double check, only a king move can save the day
// Generate blocking evasions or captures of the checking piece
// Generate blocking interpositions or captures of the checking piece
return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList)
: generate_all<BLACK, EVASIONS>(pos, moveList);
}
@@ -357,7 +341,7 @@ ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
moveList = pos.checkers() ? generate<EVASIONS >(pos, moveList)
: generate<NON_EVASIONS>(pos, moveList);
while (cur != moveList)
if ( (pinned || from_sq(*cur) == ksq || type_of(*cur) == ENPASSANT)
if ( ((pinned && pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT)
&& !pos.legal(*cur))
*cur = (--moveList)->move;
else
@@ -365,3 +349,5 @@ ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
return moveList;
}
} // namespace Stockfish
+5 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,6 +23,8 @@
#include "types.h"
namespace Stockfish {
class Position;
enum GenType {
@@ -73,4 +75,6 @@ private:
ExtMove moveList[MAX_MOVES], *last;
};
} // namespace Stockfish
#endif // #ifndef MOVEGEN_H_INCLUDED
+12 -8
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,6 +20,8 @@
#include "movepick.h"
namespace Stockfish {
namespace {
enum Stages {
@@ -99,15 +101,15 @@ void MovePicker::score() {
static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
for (auto& m : *this)
if (Type == CAPTURES)
if constexpr (Type == CAPTURES)
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)
else if constexpr (Type == QUIETS)
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[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)]
+ (ply < MAX_LPH ? std::min(4, depth / 3) * (*lowPlyHistory)[ply][from_to(m)] : 0);
@@ -117,8 +119,8 @@ void MovePicker::score() {
m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
- Value(type_of(pos.moved_piece(m)));
else
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
+ (*continuationHistory[0])[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)]
- (1 << 28);
}
}
@@ -142,7 +144,7 @@ Move MovePicker::select(Pred filter) {
}
/// MovePicker::next_move() is the most important method of the MovePicker class. It
/// returns a new pseudo legal move every time it is called until there are no more
/// returns a new pseudo-legal move every time it is called until there are no more
/// moves left, picking the move with the highest score from a list of generated moves.
Move MovePicker::next_move(bool skipQuiets) {
@@ -263,3 +265,5 @@ top:
assert(false);
return MOVE_NONE; // Silence warning
}
} // namespace Stockfish
+11 -7
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -27,6 +27,8 @@
#include "position.h"
#include "types.h"
namespace Stockfish {
/// StatsEntry stores the stat table value. It is usually a number but could
/// be a move or even a nested history. We use a class instead of naked value
/// to directly call history update operator<<() on the entry so to use stats
@@ -84,7 +86,7 @@ enum StatsType { NoCaptures, Captures };
/// unsuccessful during the current search, and is used for reduction and move
/// ordering decisions. It uses 2 tables (one for each color) indexed by
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
typedef Stats<int16_t, 10692, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
typedef Stats<int16_t, 13365, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
/// At higher depths LowPlyHistory records successful quiet moves near the root
/// and quiet moves which are/were in the PV (ttPv). It is cleared with each new
@@ -108,12 +110,12 @@ typedef Stats<int16_t, 29952, PIECE_NB, SQUARE_NB> PieceToHistory;
typedef Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB> ContinuationHistory;
/// MovePicker class is used to pick one pseudo legal move at a time from the
/// MovePicker class is used to pick one pseudo-legal move at a time from the
/// current position. The most important method is next_move(), which returns a
/// new pseudo legal move each time it is called, until there are no moves left,
/// when MOVE_NONE is returned. In order to improve the efficiency of the alpha
/// beta algorithm, MovePicker attempts to return the moves which are most likely
/// to get a cut-off first.
/// new pseudo-legal move each time it is called, until there are no moves left,
/// when MOVE_NONE is returned. In order to improve the efficiency of the
/// alpha-beta algorithm, MovePicker attempts to return the moves which are most
/// likely to get a cut-off first.
class MovePicker {
enum PickType { Next, Best };
@@ -156,4 +158,6 @@ private:
ExtMove moves[MAX_MOVES];
};
} // namespace Stockfish
#endif // #ifndef MOVEPICK_H_INCLUDED
+4 -4
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,7 +28,7 @@
#include "nnue/layers/affine_transform.h"
#include "nnue/layers/clipped_relu.h"
namespace Eval::NNUE {
namespace Stockfish::Eval::NNUE {
// Input features used in evaluation function
using RawFeatures = Features::FeatureSet<
@@ -49,6 +49,6 @@ namespace Eval::NNUE {
using Network = Layers::OutputLayer;
} // namespace Eval::NNUE
} // namespace Stockfish::Eval::NNUE
#endif // #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED
+3 -86
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -38,28 +38,7 @@
#include "evaluate_nnue.h"
namespace Eval::NNUE {
const uint32_t kpp_board_index[PIECE_NB][COLOR_NB] = {
// convention: W - us, B - them
// viewed from other side, W and B are reversed
{ PS_NONE, PS_NONE },
{ PS_W_PAWN, PS_B_PAWN },
{ PS_W_KNIGHT, PS_B_KNIGHT },
{ PS_W_BISHOP, PS_B_BISHOP },
{ PS_W_ROOK, PS_B_ROOK },
{ PS_W_QUEEN, PS_B_QUEEN },
{ PS_W_KING, PS_B_KING },
{ PS_NONE, PS_NONE },
{ PS_NONE, PS_NONE },
{ PS_B_PAWN, PS_W_PAWN },
{ PS_B_KNIGHT, PS_W_KNIGHT },
{ PS_B_BISHOP, PS_W_BISHOP },
{ PS_B_ROOK, PS_W_ROOK },
{ PS_B_QUEEN, PS_W_QUEEN },
{ PS_B_KING, PS_W_KING },
{ PS_NONE, PS_NONE }
};
namespace Stockfish::Eval::NNUE {
// Input feature converter
LargePagePtr<FeatureTransformer> feature_transformer;
@@ -293,66 +272,4 @@ void init() {
}
}
#undef stringify2
#undef stringify
}
/// NNUE::verify() verifies that the last net used was loaded successfully
void verify_eval_file_loaded() {
std::string eval_file = std::string(Options["EvalFile"]);
if (useNNUE != UseNNUEMode::False && eval_file_loaded != eval_file)
{
UCI::OptionsMap defaults;
UCI::init(defaults);
std::string msg1 = "If the UCI option \"Use NNUE\" is set to true, network evaluation parameters compatible with the engine must be available.";
std::string msg2 = "The option is set to true, but the network file " + eval_file + " was not loaded successfully.";
std::string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file.";
std::string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + std::string(defaults["EvalFile"]);
std::string msg5 = "The engine will be terminated now.";
sync_cout << "info string ERROR: " << msg1 << sync_endl;
sync_cout << "info string ERROR: " << msg2 << sync_endl;
sync_cout << "info string ERROR: " << msg3 << sync_endl;
sync_cout << "info string ERROR: " << msg4 << sync_endl;
sync_cout << "info string ERROR: " << msg5 << sync_endl;
std::exit(EXIT_FAILURE);
}
if (useNNUE != UseNNUEMode::False)
sync_cout << "info string NNUE evaluation using " << eval_file << " enabled" << sync_endl;
else
sync_cout << "info string classical evaluation enabled" << sync_endl;
}
/// In training we override eval file so this is useful.
void verify_any_net_loaded() {
if (!Options["SkipLoadingEval"] && useNNUE != UseNNUEMode::False && eval_file_loaded.empty())
{
UCI::OptionsMap defaults;
UCI::init(defaults);
std::string msg1 = "If the UCI option \"Use NNUE\" is set to true, network evaluation parameters compatible with the engine must be available.";
std::string msg2 = "The option is set to true, but the network file was not loaded successfully.";
std::string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file.";
std::string msg5 = "The engine will be terminated now.";
sync_cout << "info string ERROR: " << msg1 << sync_endl;
sync_cout << "info string ERROR: " << msg2 << sync_endl;
sync_cout << "info string ERROR: " << msg3 << sync_endl;
sync_cout << "info string ERROR: " << msg5 << sync_endl;
std::exit(EXIT_FAILURE);
}
if (useNNUE != UseNNUEMode::False)
sync_cout << "info string NNUE evaluation using " << eval_file_loaded << " enabled" << sync_endl;
else
sync_cout << "info string classical evaluation enabled" << sync_endl;
}
} // namespace Eval::NNUE
} // namespace Stockfish::Eval::NNUE
+3 -45
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -27,7 +27,7 @@
#include <memory>
namespace Eval::NNUE {
namespace Stockfish::Eval::NNUE {
enum struct UseNNUEMode
{
@@ -63,48 +63,6 @@ namespace Eval::NNUE {
template <typename T>
using LargePagePtr = std::unique_ptr<T, LargePageDeleter<T>>;
// Input feature converter
extern LargePagePtr<FeatureTransformer> feature_transformer;
// Evaluation function
extern AlignedPtr<Network> network;
// Evaluation function file name
extern std::string fileName;
// Saved evaluation function file name
extern std::string savedfileName;
extern UseNNUEMode useNNUE;
extern std::string eval_file_loaded;
// Get a string that represents the structure of the evaluation function
std::string get_architecture_string();
std::string get_layers_info();
// read the header
bool read_header(std::istream& stream,
std::uint32_t* hash_value, std::string* architecture);
// write the header
bool write_header(std::ostream& stream,
std::uint32_t hash_value, const std::string& architecture);
// read evaluation function parameters
bool ReadParameters(std::istream& stream);
// write evaluation function parameters
bool WriteParameters(std::ostream& stream);
Value evaluate(const Position& pos);
bool load_eval(std::string name, std::istream& stream);
void init();
void verify_eval_file_loaded();
void verify_any_net_loaded();
} // namespace Eval::NNUE
} // namespace Stockfish::Eval::NNUE
#endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED
+3 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,7 +24,7 @@
#include "features_common.h"
#include <array>
namespace Eval::NNUE::Features {
namespace Stockfish::Eval::NNUE::Features {
// Class template that represents a list of values
template <typename T, T... Values>
@@ -297,6 +297,6 @@ namespace Eval::NNUE::Features {
friend class FeatureSet;
};
} // namespace Eval::NNUE::Features
} // namespace Stockfish::Eval::NNUE::Features
#endif // #ifndef NNUE_FEATURE_SET_H_INCLUDED
+3 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,7 +24,7 @@
#include "../../evaluate.h"
#include "../nnue_common.h"
namespace Eval::NNUE::Features {
namespace Stockfish::Eval::NNUE::Features {
class IndexList;
@@ -45,6 +45,6 @@ namespace Eval::NNUE::Features {
kEnemy, // opponent
};
} // namespace Eval::NNUE::Features
} // namespace Stockfish::Eval::NNUE::Features
#endif // #ifndef NNUE_FEATURES_COMMON_H_INCLUDED
+61 -69
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,74 +21,66 @@
#include "half_kp.h"
#include "index_list.h"
namespace Eval::NNUE::Features {
namespace Stockfish::Eval::NNUE::Features {
// Orient a square according to perspective (rotate the board 180° for black)
// this has to stay until we find a better arch that works with "flip".
// allows us to use current master net for gensfen (primarily needed for higher quality data)
inline Square orient(Color perspective, Square s) {
return Square(int(s) ^ (bool(perspective) * 63));
// Orient a square according to perspective (rotates by 180 for black)
inline Square orient(Color perspective, Square s) {
return Square(int(s) ^ (bool(perspective) * 63));
}
// Index of a feature for a given king position and another piece on some square
inline IndexType make_index(Color perspective, Square s, Piece pc, Square ksq) {
return IndexType(orient(perspective, s) + kpp_board_index[perspective][pc] + PS_END * ksq);
}
// Get a list of indices for active features
template <Side AssociatedKing>
void HalfKP<AssociatedKing>::AppendActiveIndices(
const Position& pos, Color perspective, IndexList* active) {
Square ksq = orient(perspective, pos.square<KING>(perspective));
Bitboard bb = pos.pieces() & ~pos.pieces(KING);
while (bb)
{
Square s = pop_lsb(bb);
active->push_back(make_index(perspective, s, pos.piece_on(s), ksq));
}
}
// AppendChangedIndices() : get a list of indices for recently changed features
// IMPORTANT: The `pos` in this function is pretty much useless as it
// is not always the position the features are updated to. The feature
// transformer code right now can update multiple accumulators per move,
// but since Stockfish only keeps the full state of the current leaf
// search position it is not possible to always pass here the position for
// which the accumulator is being updated. Therefore the only thing that
// can be reliably extracted from `pos` is the king square for the king
// of the `perspective` color (note: not even the other king's square will
// match reality in all cases, this is also the reason why `dp` is passed
// as a parameter and not extracted from pos.state()). This is of particular
// problem for future nets with other feature sets, where updating the active
// feature might require more information from the intermediate positions. In
// this case the only easy solution is to remove the multiple updates from
// the feature transformer update code and only update the accumulator for
// the current leaf position (the position after the move).
template <Side AssociatedKing>
void HalfKP<AssociatedKing>::AppendChangedIndices(
const Position& pos, const DirtyPiece& dp, Color perspective,
IndexList* removed, IndexList* added) {
Square ksq = orient(perspective, pos.square<KING>(perspective));
for (int i = 0; i < dp.dirty_num; ++i) {
Piece pc = dp.piece[i];
if (type_of(pc) == KING) continue;
if (dp.from[i] != SQ_NONE)
removed->push_back(make_index(perspective, dp.from[i], pc, ksq));
if (dp.to[i] != SQ_NONE)
added->push_back(make_index(perspective, dp.to[i], pc, ksq));
}
// Find the index of the feature quantity from the king position and PieceSquare
template <Side AssociatedKing>
inline IndexType HalfKP<AssociatedKing>::make_index(
Color perspective,
Square s,
Piece pc,
Square ksq) {
template class HalfKP<Side::kFriend>;
return IndexType(orient(perspective, s) + kpp_board_index[pc][perspective] + PS_END * ksq);
}
// Get a list of indices for active features
template <Side AssociatedKing>
void HalfKP<AssociatedKing>::append_active_indices(
const Position& pos,
Color perspective,
IndexList* active) {
Square ksq = orient(
perspective,
pos.square<KING>(
AssociatedKing == Side::kFriend ? perspective : ~perspective));
Bitboard bb = pos.pieces() & ~pos.pieces(KING);
while (bb) {
Square s = pop_lsb(&bb);
active->push_back(make_index(perspective, s, pos.piece_on(s), ksq));
}
}
// Get a list of indices for recently changed features
template <Side AssociatedKing>
void HalfKP<AssociatedKing>::append_changed_indices(
const Position& pos,
Color perspective,
IndexList* removed,
IndexList* added) {
Square ksq = orient(
perspective,
pos.square<KING>(
AssociatedKing == Side::kFriend ? perspective : ~perspective));
const auto& dp = pos.state()->dirtyPiece;
for (int i = 0; i < dp.dirty_num; ++i) {
Piece pc = dp.piece[i];
if (type_of(pc) == KING)
continue;
if (dp.from[i] != SQ_NONE)
removed->push_back(make_index(perspective, dp.from[i], pc, ksq));
if (dp.to[i] != SQ_NONE)
added->push_back(make_index(perspective, dp.to[i], pc, ksq));
}
}
template class HalfKP<Side::kFriend>;
template class HalfKP<Side::kEnemy>;
} // namespace Eval::NNUE::Features
} // namespace Stockfish::Eval::NNUE::Features
+28 -47
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,55 +21,36 @@
#include "features_common.h"
#include "evaluate.h"
namespace Stockfish::Eval::NNUE::Features {
//Definition of input features HalfKP of NNUE evaluation function
namespace Eval::NNUE::Features {
// Feature HalfKP: Combination of the position of own king
// and the position of pieces other than kings
template <Side AssociatedKing>
class HalfKP {
// Feature HalfKP: Combination of the position of own king
// and the position of pieces other than kings
template <Side AssociatedKing>
class HalfKP {
public:
// Feature name
static constexpr const char* kName = "HalfKP(Friend)";
// Hash value embedded in the evaluation file
static constexpr std::uint32_t kHashValue =
0x5D69D5B9u ^ (AssociatedKing == Side::kFriend);
// Number of feature dimensions
static constexpr IndexType kDimensions =
static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_END);
// Maximum number of simultaneously active features
static constexpr IndexType kMaxActiveDimensions = 30; // Kings don't count
// Trigger for full calculation instead of difference calculation
static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved;
public:
// Feature name
static constexpr const char* kName = (AssociatedKing == Side::kFriend) ?
"HalfKP(Friend)" : "HalfKP(Enemy)";
// Get a list of indices for active features
static void AppendActiveIndices(const Position& pos, Color perspective,
IndexList* active);
// Hash value embedded in the evaluation file
static constexpr std::uint32_t kHashValue =
0x5D69D5B9u ^ (AssociatedKing == Side::kFriend);
// Get a list of indices for recently changed features
static void AppendChangedIndices(const Position& pos, const DirtyPiece& dp, Color perspective,
IndexList* removed, IndexList* added);
};
// Number of feature dimensions
static constexpr IndexType kDimensions =
static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_END);
// Maximum number of simultaneously active features
static constexpr IndexType kMaxActiveDimensions = 30; // Kings don't count
// Trigger for full calculation instead of difference calculation
static constexpr TriggerEvent kRefreshTrigger =
(AssociatedKing == Side::kFriend) ?
TriggerEvent::kFriendKingMoved : TriggerEvent::kEnemyKingMoved;
// Get a list of indices for active features
static void append_active_indices(
const Position& pos,
Color perspective,
IndexList* active);
// Get a list of indices for recently changed features
static void append_changed_indices(
const Position& pos,
Color perspective,
IndexList* removed,
IndexList* added);
private:
// Index of a feature for a given king position and another piece on some square
static IndexType make_index(Color perspective, Square s, Piece pc, Square sq_k);
};
} // namespace Eval::NNUE::Features
} // namespace Stockfish::Eval::NNUE::Features
#endif // #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED
+3 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,7 +24,7 @@
#include "../../position.h"
#include "../nnue_architecture.h"
namespace Eval::NNUE::Features {
namespace Stockfish::Eval::NNUE::Features {
// Class template used for feature index list
template <typename T, std::size_t MaxSize>
@@ -59,6 +59,6 @@ namespace Eval::NNUE::Features {
: public ValueList<IndexType, RawFeatures::kMaxActiveDimensions> {
};
} // namespace Eval::NNUE::Features
} // namespace Stockfish::Eval::NNUE::Features
#endif // NNUE_FEATURES_INDEX_LIST_H_INCLUDED
+213 -515
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,11 +24,7 @@
#include <iostream>
#include "../nnue_common.h"
#include <string>
#include <type_traits>
#include <cstdint>
namespace Eval::NNUE::Layers {
namespace Stockfish::Eval::NNUE::Layers {
// Affine transformation layer
template <typename PreviousLayer, IndexType OutputDimensions>
@@ -45,6 +41,11 @@ namespace Eval::NNUE::Layers {
static constexpr IndexType kOutputDimensions = OutputDimensions;
static constexpr IndexType kPaddedInputDimensions =
CeilToMultiple<IndexType>(kInputDimensions, kMaxSimdWidth);
#if defined (USE_AVX512)
static constexpr const IndexType kOutputSimdWidth = kSimdWidth / 2;
#elif defined (USE_SSSE3)
static constexpr const IndexType kOutputSimdWidth = kSimdWidth / 4;
#endif
// Size of forward propagation buffer used in this layer
static constexpr std::size_t kSelfBufferSize =
@@ -92,7 +93,58 @@ namespace Eval::NNUE::Layers {
for (std::size_t i = 0; i < kOutputDimensions; ++i)
biases_[i] = read_little_endian<BiasType>(stream);
for (std::size_t i = 0; i < kOutputDimensions * kPaddedInputDimensions; ++i)
#if !defined (USE_SSSE3)
weights_[i] = read_little_endian<WeightType>(stream);
#else
weights_[
(i / 4) % (kPaddedInputDimensions / 4) * kOutputDimensions * 4 +
i / kPaddedInputDimensions * 4 +
i % 4
] = read_little_endian<WeightType>(stream);
// Determine if eights of weight and input products can be summed using 16bits
// without saturation. We assume worst case combinations of 0 and 127 for all inputs.
if (kOutputDimensions > 1 && !stream.fail())
{
canSaturate16.count = 0;
#if !defined(USE_VNNI)
for (IndexType i = 0; i < kPaddedInputDimensions; i += 16)
for (IndexType j = 0; j < kOutputDimensions; ++j)
for (int x = 0; x < 2; ++x)
{
WeightType* w = &weights_[i * kOutputDimensions + j * 4 + x * 2];
int sum[2] = {0, 0};
for (int k = 0; k < 8; ++k)
{
IndexType idx = k / 2 * kOutputDimensions * 4 + k % 2;
sum[w[idx] < 0] += w[idx];
}
for (int sign : { -1, 1 })
while (sign * sum[sign == -1] > 258)
{
int maxK = 0, maxW = 0;
for (int k = 0; k < 8; ++k)
{
IndexType idx = k / 2 * kOutputDimensions * 4 + k % 2;
if (maxW < sign * w[idx])
maxK = k, maxW = sign * w[idx];
}
IndexType idx = maxK / 2 * kOutputDimensions * 4 + maxK % 2;
sum[sign == -1] -= w[idx];
canSaturate16.add(j, i + maxK / 2 * 4 + maxK % 2 + x * 2, w[idx]);
w[idx] = 0;
}
}
// Non functional optimization for faster more linear access
std::sort(canSaturate16.ids, canSaturate16.ids + canSaturate16.count,
[](const typename CanSaturate::Entry& e1, const typename CanSaturate::Entry& e2)
{ return e1.in == e2.in ? e1.out < e2.out : e1.in < e2.in; });
#endif
}
#endif
return !stream.fail();
}
@@ -125,111 +177,33 @@ namespace Eval::NNUE::Layers {
return _mm512_reduce_add_epi32(sum) + bias;
};
// This function takes
// sum0 = [xmm0a, xmm0b, xmm0c, xmm0d]
// sum1 = [xmm1a, xmm1b, xmm1c, xmm1d]
// sum2 = [xmm2a, xmm2b, xmm2c, xmm2d]
// sum3 = [xmm3a, xmm3b, xmm3c, xmm3d]
// and returns
// ret = [
// reduce_add_epi32(xmm0a), reduce_add_epi32(xmm1a), reduce_add_epi32(xmm2a), reduce_add_epi32(xmm3a),
// reduce_add_epi32(xmm0b), reduce_add_epi32(xmm1b), reduce_add_epi32(xmm2b), reduce_add_epi32(xmm3b),
// reduce_add_epi32(xmm0c), reduce_add_epi32(xmm1c), reduce_add_epi32(xmm2c), reduce_add_epi32(xmm3c),
// reduce_add_epi32(xmm0d), reduce_add_epi32(xmm1d), reduce_add_epi32(xmm2d), reduce_add_epi32(xmm3d)
// ]
[[maybe_unused]] auto m512_hadd128x16_interleave = [](
__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) -> __m512i {
__m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1);
__m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1);
__m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3);
__m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3);
__m512i sum01 = _mm512_add_epi32(sum01a, sum01b);
__m512i sum23 = _mm512_add_epi32(sum23a, sum23b);
__m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23);
__m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23);
return _mm512_add_epi32(sum0123a, sum0123b);
};
[[maybe_unused]] auto m512_haddx4 = [m512_hadd128x16_interleave](
__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, __m128i bias) -> __m128i {
__m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3);
__m256i sum256lo = _mm512_castsi512_si256(sum);
__m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1);
sum256lo = _mm256_add_epi32(sum256lo, sum256hi);
__m128i sum128lo = _mm256_castsi256_si128(sum256lo);
__m128i sum128hi = _mm256_extracti128_si256(sum256lo, 1);
return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias);
};
[[maybe_unused]] auto m512_haddx8 = [m512_hadd128x16_interleave](
__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3,
__m512i sum4, __m512i sum5, __m512i sum6, __m512i sum7, __m256i bias) -> __m256i {
__m512i suma = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3);
__m512i sumb = m512_hadd128x16_interleave(sum4, sum5, sum6, sum7);
__m512i indices0 = _mm512_setr_epi64(0, 1, 8, 9, 4, 5, 12, 13);
__m512i indices1 = _mm512_setr_epi64(2, 3, 10, 11, 6, 7, 14, 15);
__m512i x = _mm512_add_epi32(
_mm512_permutex2var_epi64(suma, indices0, sumb),
_mm512_permutex2var_epi64(suma, indices1, sumb));
__m256i sum256lo = _mm512_castsi512_si256(x);
__m256i sum256hi = _mm512_extracti64x4_epi64(x, 1);
return _mm256_add_epi32(_mm256_add_epi32(sum256lo, sum256hi), bias);
};
[[maybe_unused]] auto m512_hadd256x8 =[m512_hadd128x16_interleave](
__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, __m256i bias) -> __m256i {
__m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3);
__m512i indices = _mm512_setr_epi32(
0, 4, 8, 12, 2, 6, 10, 14,
1, 5, 9, 13, 3, 7, 11, 15);
sum = _mm512_permutexvar_epi32(indices, sum);
__m256i sum256lo = _mm512_castsi512_si256(sum);
__m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1);
return _mm256_add_epi32(_mm256_hadd_epi32(sum256lo, sum256hi), bias);
};
[[maybe_unused]] auto m512_hadd256x16 = [m512_hadd128x16_interleave](
__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3,
__m512i sum4, __m512i sum5, __m512i sum6, __m512i sum7, __m512i bias) -> __m512i {
__m512i suma = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3);
__m512i sumb = m512_hadd128x16_interleave(sum4, sum5, sum6, sum7);
__m512i indices0 = _mm512_setr_epi64(0, 1, 8, 9, 4, 5, 12, 13);
__m512i indices1 = _mm512_setr_epi64(2, 3, 10, 11, 6, 7, 14, 15);
__m512i x = _mm512_add_epi32(
_mm512_permutex2var_epi64(suma, indices0, sumb),
_mm512_permutex2var_epi64(suma, indices1, sumb));
__m512i indices = _mm512_setr_epi32(0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15);
return _mm512_add_epi32(_mm512_permutexvar_epi32(indices, x), bias);
};
#if defined (USE_VNNI)
[[maybe_unused]] auto m512_add_dpbusd_epi32 = [=](__m512i& acc, __m512i a, __m512i b) {
#if defined (USE_VNNI)
acc = _mm512_dpbusd_epi32(acc, a, b);
#else
[[maybe_unused]] auto m512_dpbusd_epi32 = [=](__m512i a, __m512i b) -> __m512i {
__m512i product0 = _mm512_maddubs_epi16(a, b);
return _mm512_madd_epi16(product0, kOnes512);
product0 = _mm512_madd_epi16(product0, kOnes512);
acc = _mm512_add_epi32(acc, product0);
#endif
};
[[maybe_unused]] auto m512_add_dpbusd_epi32x4 = [=](__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512i b1,
__m512i a2, __m512i b2, __m512i a3, __m512i b3) {
#if defined (USE_VNNI)
acc = _mm512_dpbusd_epi32(acc, a0, b0);
acc = _mm512_dpbusd_epi32(acc, a1, b1);
acc = _mm512_dpbusd_epi32(acc, a2, b2);
acc = _mm512_dpbusd_epi32(acc, a3, b3);
#else
__m512i product0 = _mm512_maddubs_epi16(a0, b0);
__m512i product1 = _mm512_maddubs_epi16(a1, b1);
__m512i product2 = _mm512_maddubs_epi16(a2, b2);
__m512i product3 = _mm512_maddubs_epi16(a3, b3);
product0 = _mm512_add_epi16(product0, product1);
product2 = _mm512_add_epi16(product2, product3);
product0 = _mm512_add_epi16(product0, product2);
product0 = _mm512_madd_epi16(product0, kOnes512);
acc = _mm512_add_epi32(acc, product0);
#endif
};
@@ -245,29 +219,37 @@ namespace Eval::NNUE::Layers {
return _mm_cvtsi128_si32(sum128) + bias;
};
[[maybe_unused]] auto m256_haddx4 = [](__m256i sum0, __m256i sum1, __m256i sum2, __m256i sum3, __m128i bias) -> __m128i {
sum0 = _mm256_hadd_epi32(sum0, sum1);
sum2 = _mm256_hadd_epi32(sum2, sum3);
sum0 = _mm256_hadd_epi32(sum0, sum2);
__m128i sum128lo = _mm256_castsi256_si128(sum0);
__m128i sum128hi = _mm256_extracti128_si256(sum0, 1);
return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias);
};
#if defined (USE_VNNI)
[[maybe_unused]] auto m256_add_dpbusd_epi32 = [=](__m256i& acc, __m256i a, __m256i b) {
#if defined (USE_VNNI)
acc = _mm256_dpbusd_epi32(acc, a, b);
#else
[[maybe_unused]] auto m256_dpbusd_epi32 = [=](__m256i a, __m256i b) -> __m256i {
__m256i product0 = _mm256_maddubs_epi16(a, b);
return _mm256_madd_epi16(product0, kOnes256);
product0 = _mm256_madd_epi16(product0, kOnes256);
acc = _mm256_add_epi32(acc, product0);
#endif
};
[[maybe_unused]] auto m256_add_dpbusd_epi32x4 = [=](__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256i b1,
__m256i a2, __m256i b2, __m256i a3, __m256i b3) {
#if defined (USE_VNNI)
acc = _mm256_dpbusd_epi32(acc, a0, b0);
acc = _mm256_dpbusd_epi32(acc, a1, b1);
acc = _mm256_dpbusd_epi32(acc, a2, b2);
acc = _mm256_dpbusd_epi32(acc, a3, b3);
#else
__m256i product0 = _mm256_maddubs_epi16(a0, b0);
__m256i product1 = _mm256_maddubs_epi16(a1, b1);
__m256i product2 = _mm256_maddubs_epi16(a2, b2);
__m256i product3 = _mm256_maddubs_epi16(a3, b3);
product0 = _mm256_add_epi16(product0, product1);
product2 = _mm256_add_epi16(product2, product3);
product0 = _mm256_add_epi16(product0, product2);
product0 = _mm256_madd_epi16(product0, kOnes256);
acc = _mm256_add_epi32(acc, product0);
#endif
};
#endif
#if defined (USE_SSSE3)
[[maybe_unused]] const __m128i kOnes128 = _mm_set1_epi16(1);
@@ -278,413 +260,119 @@ namespace Eval::NNUE::Layers {
return _mm_cvtsi128_si32(sum) + bias;
};
[[maybe_unused]] auto m128_haddx4 = [](__m128i sum0, __m128i sum1, __m128i sum2, __m128i sum3, __m128i bias) -> __m128i {
sum0 = _mm_hadd_epi32(sum0, sum1);
sum2 = _mm_hadd_epi32(sum2, sum3);
sum0 = _mm_hadd_epi32(sum0, sum2);
return _mm_add_epi32(sum0, bias);
[[maybe_unused]] auto m128_add_dpbusd_epi32 = [=](__m128i& acc, __m128i a, __m128i b) {
__m128i product0 = _mm_maddubs_epi16(a, b);
product0 = _mm_madd_epi16(product0, kOnes128);
acc = _mm_add_epi32(acc, product0);
};
[[maybe_unused]] auto m128_dpbusd_epi32 = [=](__m128i a, __m128i b) -> __m128i {
__m128i product0 = _mm_maddubs_epi16(a, b);
return _mm_madd_epi16(product0, kOnes128);
[[maybe_unused]] auto m128_add_dpbusd_epi32x4 = [=](__m128i& acc, __m128i a0, __m128i b0, __m128i a1, __m128i b1,
__m128i a2, __m128i b2, __m128i a3, __m128i b3) {
__m128i product0 = _mm_maddubs_epi16(a0, b0);
__m128i product1 = _mm_maddubs_epi16(a1, b1);
__m128i product2 = _mm_maddubs_epi16(a2, b2);
__m128i product3 = _mm_maddubs_epi16(a3, b3);
product0 = _mm_add_epi16(product0, product1);
product2 = _mm_add_epi16(product2, product3);
product0 = _mm_add_epi16(product0, product2);
product0 = _mm_madd_epi16(product0, kOnes128);
acc = _mm_add_epi32(acc, product0);
};
#endif
#if defined (USE_AVX512)
using vec_t = __m512i;
#define vec_setzero _mm512_setzero_si512
#define vec_set_32 _mm512_set1_epi32
auto& vec_add_dpbusd_32 = m512_add_dpbusd_epi32;
auto& vec_add_dpbusd_32x4 = m512_add_dpbusd_epi32x4;
auto& vec_hadd = m512_hadd;
#elif defined (USE_AVX2)
using vec_t = __m256i;
#define vec_setzero _mm256_setzero_si256
#define vec_set_32 _mm256_set1_epi32
auto& vec_add_dpbusd_32 = m256_add_dpbusd_epi32;
auto& vec_add_dpbusd_32x4 = m256_add_dpbusd_epi32x4;
auto& vec_hadd = m256_hadd;
#elif defined (USE_SSSE3)
using vec_t = __m128i;
#define vec_setzero _mm_setzero_si128
#define vec_set_32 _mm_set1_epi32
auto& vec_add_dpbusd_32 = m128_add_dpbusd_epi32;
auto& vec_add_dpbusd_32x4 = m128_add_dpbusd_epi32x4;
auto& vec_hadd = m128_hadd;
#endif
constexpr IndexType kNumChunks512 = kPaddedInputDimensions / (kSimdWidth * 2);
constexpr IndexType kNumChunks256 = kPaddedInputDimensions / kSimdWidth;
#if defined (USE_SSSE3)
const auto output = reinterpret_cast<OutputType*>(buffer);
const auto input_vector = reinterpret_cast<const vec_t*>(input);
// Since to saturate a zmm register it takes 64 bytes we
// cannot use AVX512 for the smaller affine transforms.
// Instead we fallback to a AVX2 implementation if the
// kInputDimensions isn't a multiple of 64.
// Note that this means that for example for
// kInputDimensions of 96 we fallback to AVX2 even though
// the first 64 elements could be processed with AVX512.
// This is caused by mixing the __m256 and __m512 variables
// required to better handle that case and it would
// require handling more cases statically not to lose performance.
// This should be revisited if such input dimensions are to be considered.
[[maybe_unused]] const auto input_vector512 = reinterpret_cast<const __m512i*>(input);
[[maybe_unused]] const auto input_vector256 = reinterpret_cast<const __m256i*>(input);
static_assert(kOutputDimensions % kOutputSimdWidth == 0 || kOutputDimensions == 1);
// kOutputDimensions is either 1 or a multiple of kSimdWidth
// because then it is also an input dimension.
if constexpr (kOutputDimensions % 16 == 0 && kNumChunks256 == 1)
if constexpr (kOutputDimensions % kOutputSimdWidth == 0)
{
for (IndexType i = 0; i < kOutputDimensions; i += 16)
{
const IndexType offset01a = (i + 0) * kPaddedInputDimensions;
const IndexType offset23a = (i + 2) * kPaddedInputDimensions;
const IndexType offset45a = (i + 4) * kPaddedInputDimensions;
const IndexType offset67a = (i + 6) * kPaddedInputDimensions;
const IndexType offset01b = (i + 8) * kPaddedInputDimensions;
const IndexType offset23b = (i + 10) * kPaddedInputDimensions;
const IndexType offset45b = (i + 12) * kPaddedInputDimensions;
const IndexType offset67b = (i + 14) * kPaddedInputDimensions;
constexpr IndexType kNumChunks = kPaddedInputDimensions / 4;
const __m512i bias = *reinterpret_cast<const __m512i*>(&biases_[i]);
__m512i* outptr = reinterpret_cast<__m512i*>(&output[i]);
const auto input32 = reinterpret_cast<const std::int32_t*>(input);
vec_t* outptr = reinterpret_cast<vec_t*>(output);
std::memcpy(output, biases_, kOutputDimensions * sizeof(OutputType));
const auto row01a = *reinterpret_cast<const __m512i*>(&weights_[offset01a]);
const auto row23a = *reinterpret_cast<const __m512i*>(&weights_[offset23a]);
const auto row45a = *reinterpret_cast<const __m512i*>(&weights_[offset45a]);
const auto row67a = *reinterpret_cast<const __m512i*>(&weights_[offset67a]);
const auto row01b = *reinterpret_cast<const __m512i*>(&weights_[offset01b]);
const auto row23b = *reinterpret_cast<const __m512i*>(&weights_[offset23b]);
const auto row45b = *reinterpret_cast<const __m512i*>(&weights_[offset45b]);
const auto row67b = *reinterpret_cast<const __m512i*>(&weights_[offset67b]);
const __m256i in256 = input_vector256[0];
const __m512i in = _mm512_inserti64x4(_mm512_castsi256_si512(in256), in256, 1);
#if defined (USE_VNNI)
__m512i sum01a = _mm512_setzero_si512();
__m512i sum23a = _mm512_setzero_si512();
__m512i sum45a = _mm512_setzero_si512();
__m512i sum67a = _mm512_setzero_si512();
__m512i sum01b = _mm512_setzero_si512();
__m512i sum23b = _mm512_setzero_si512();
__m512i sum45b = _mm512_setzero_si512();
__m512i sum67b = _mm512_setzero_si512();
m512_add_dpbusd_epi32(sum01a, in, row01a);
m512_add_dpbusd_epi32(sum23a, in, row23a);
m512_add_dpbusd_epi32(sum45a, in, row45a);
m512_add_dpbusd_epi32(sum67a, in, row67a);
m512_add_dpbusd_epi32(sum01b, in, row01b);
m512_add_dpbusd_epi32(sum23b, in, row23b);
m512_add_dpbusd_epi32(sum45b, in, row45b);
m512_add_dpbusd_epi32(sum67b, in, row67b);
#else
__m512i sum01a = m512_dpbusd_epi32(in, row01a);
__m512i sum23a = m512_dpbusd_epi32(in, row23a);
__m512i sum45a = m512_dpbusd_epi32(in, row45a);
__m512i sum67a = m512_dpbusd_epi32(in, row67a);
__m512i sum01b = m512_dpbusd_epi32(in, row01b);
__m512i sum23b = m512_dpbusd_epi32(in, row23b);
__m512i sum45b = m512_dpbusd_epi32(in, row45b);
__m512i sum67b = m512_dpbusd_epi32(in, row67b);
#endif
*outptr = m512_hadd256x16(
sum01a, sum23a, sum45a, sum67a,
sum01b, sum23b, sum45b, sum67b, bias);
}
}
else if constexpr (kOutputDimensions % 4 == 0)
{
for (IndexType i = 0; i < kOutputDimensions; i += 4)
{
const IndexType offset0 = (i + 0) * kPaddedInputDimensions;
const IndexType offset1 = (i + 1) * kPaddedInputDimensions;
const IndexType offset2 = (i + 2) * kPaddedInputDimensions;
const IndexType offset3 = (i + 3) * kPaddedInputDimensions;
const __m128i bias = *reinterpret_cast<const __m128i*>(&biases_[i]);
__m128i* outptr = reinterpret_cast<__m128i*>(&output[i]);
if constexpr (kPaddedInputDimensions % (kSimdWidth * 2) == 0)
for (int i = 0; i < (int)kNumChunks - 3; i += 4)
{
const auto row0 = reinterpret_cast<const __m512i*>(&weights_[offset0]);
const auto row1 = reinterpret_cast<const __m512i*>(&weights_[offset1]);
const auto row2 = reinterpret_cast<const __m512i*>(&weights_[offset2]);
const auto row3 = reinterpret_cast<const __m512i*>(&weights_[offset3]);
const vec_t in0 = vec_set_32(input32[i + 0]);
const vec_t in1 = vec_set_32(input32[i + 1]);
const vec_t in2 = vec_set_32(input32[i + 2]);
const vec_t in3 = vec_set_32(input32[i + 3]);
const auto col0 = reinterpret_cast<const vec_t*>(&weights_[(i + 0) * kOutputDimensions * 4]);
const auto col1 = reinterpret_cast<const vec_t*>(&weights_[(i + 1) * kOutputDimensions * 4]);
const auto col2 = reinterpret_cast<const vec_t*>(&weights_[(i + 2) * kOutputDimensions * 4]);
const auto col3 = reinterpret_cast<const vec_t*>(&weights_[(i + 3) * kOutputDimensions * 4]);
for (int j = 0; j * kOutputSimdWidth < kOutputDimensions; ++j)
vec_add_dpbusd_32x4(outptr[j], in0, col0[j], in1, col1[j], in2, col2[j], in3, col3[j]);
}
for (int i = 0; i < canSaturate16.count; ++i)
output[canSaturate16.ids[i].out] += input[canSaturate16.ids[i].in] * canSaturate16.ids[i].w;
}
else if constexpr (kOutputDimensions == 1)
{
#if defined (USE_AVX512)
if constexpr (kPaddedInputDimensions % (kSimdWidth * 2) != 0)
{
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
const auto input_vector256 = reinterpret_cast<const __m256i*>(input);
#if defined (USE_VNNI)
__m512i sum0 = _mm512_setzero_si512();
__m512i sum1 = _mm512_setzero_si512();
__m512i sum2 = _mm512_setzero_si512();
__m512i sum3 = _mm512_setzero_si512();
const IndexType kStart = 0;
#else
__m512i sum0 = m512_dpbusd_epi32(input_vector512[0], row0[0]);
__m512i sum1 = m512_dpbusd_epi32(input_vector512[0], row1[0]);
__m512i sum2 = m512_dpbusd_epi32(input_vector512[0], row2[0]);
__m512i sum3 = m512_dpbusd_epi32(input_vector512[0], row3[0]);
const IndexType kStart = 1;
#endif
__m256i sum0 = _mm256_setzero_si256();
const auto row0 = reinterpret_cast<const __m256i*>(&weights_[0]);
for (IndexType j = kStart; j < kNumChunks512; ++j)
{
const __m512i in = input_vector512[j];
#if defined (USE_VNNI)
m512_add_dpbusd_epi32(sum0, in, row0[j]);
m512_add_dpbusd_epi32(sum1, in, row1[j]);
m512_add_dpbusd_epi32(sum2, in, row2[j]);
m512_add_dpbusd_epi32(sum3, in, row3[j]);
#else
sum0 = _mm512_add_epi32(sum0, m512_dpbusd_epi32(in, row0[j]));
sum1 = _mm512_add_epi32(sum1, m512_dpbusd_epi32(in, row1[j]));
sum2 = _mm512_add_epi32(sum2, m512_dpbusd_epi32(in, row2[j]));
sum3 = _mm512_add_epi32(sum3, m512_dpbusd_epi32(in, row3[j]));
#endif
}
*outptr = m512_haddx4(sum0, sum1, sum2, sum3, bias);
for (int j = 0; j < (int)kNumChunks; ++j)
{
const __m256i in = input_vector256[j];
m256_add_dpbusd_epi32(sum0, in, row0[j]);
}
output[0] = m256_hadd(sum0, biases_[0]);
}
else
#endif
{
const auto row0 = reinterpret_cast<const __m256i*>(&weights_[offset0]);
const auto row1 = reinterpret_cast<const __m256i*>(&weights_[offset1]);
const auto row2 = reinterpret_cast<const __m256i*>(&weights_[offset2]);
const auto row3 = reinterpret_cast<const __m256i*>(&weights_[offset3]);
#if defined (USE_VNNI)
__m256i sum0 = _mm256_setzero_si256();
__m256i sum1 = _mm256_setzero_si256();
__m256i sum2 = _mm256_setzero_si256();
__m256i sum3 = _mm256_setzero_si256();
const IndexType kStart = 0;
#if defined (USE_AVX512)
constexpr IndexType kNumChunks = kPaddedInputDimensions / (kSimdWidth * 2);
#else
__m256i sum0 = m256_dpbusd_epi32(input_vector256[0], row0[0]);
__m256i sum1 = m256_dpbusd_epi32(input_vector256[0], row1[0]);
__m256i sum2 = m256_dpbusd_epi32(input_vector256[0], row2[0]);
__m256i sum3 = m256_dpbusd_epi32(input_vector256[0], row3[0]);
const IndexType kStart = 1;
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
#endif
vec_t sum0 = vec_setzero();
const auto row0 = reinterpret_cast<const vec_t*>(&weights_[0]);
for (IndexType j = kStart; j < kNumChunks256; ++j)
{
const __m256i in = input_vector256[j];
#if defined (USE_VNNI)
m256_add_dpbusd_epi32(sum0, in, row0[j]);
m256_add_dpbusd_epi32(sum1, in, row1[j]);
m256_add_dpbusd_epi32(sum2, in, row2[j]);
m256_add_dpbusd_epi32(sum3, in, row3[j]);
#else
sum0 = _mm256_add_epi32(sum0, m256_dpbusd_epi32(in, row0[j]));
sum1 = _mm256_add_epi32(sum1, m256_dpbusd_epi32(in, row1[j]));
sum2 = _mm256_add_epi32(sum2, m256_dpbusd_epi32(in, row2[j]));
sum3 = _mm256_add_epi32(sum3, m256_dpbusd_epi32(in, row3[j]));
#endif
}
*outptr = m256_haddx4(sum0, sum1, sum2, sum3, bias);
for (int j = 0; j < (int)kNumChunks; ++j)
{
const vec_t in = input_vector[j];
vec_add_dpbusd_32(sum0, in, row0[j]);
}
output[0] = vec_hadd(sum0, biases_[0]);
}
}
}
else if constexpr (kOutputDimensions == 1)
{
if constexpr (kPaddedInputDimensions % (kSimdWidth * 2) == 0)
{
const auto row0 = reinterpret_cast<const __m512i*>(&weights_[0]);
#if defined (USE_VNNI)
__m512i sum0 = _mm512_setzero_si512();
const IndexType kStart = 0;
#else
__m512i sum0 = m512_dpbusd_epi32(input_vector512[0], row0[0]);
const IndexType kStart = 1;
#endif
for (IndexType j = kStart; j < kNumChunks512; ++j)
{
const __m512i in = input_vector512[j];
#if defined (USE_VNNI)
m512_add_dpbusd_epi32(sum0, in, row0[j]);
#else
sum0 = _mm512_add_epi32(sum0, m512_dpbusd_epi32(in, row0[j]));
#endif
}
output[0] = m512_hadd(sum0, biases_[0]);
}
else
{
const auto row0 = reinterpret_cast<const __m256i*>(&weights_[0]);
#if defined (USE_VNNI)
__m256i sum0 = _mm256_setzero_si256();
const IndexType kStart = 0;
#else
__m256i sum0 = m256_dpbusd_epi32(input_vector256[0], row0[0]);
const IndexType kStart = 1;
#endif
for (IndexType j = kStart; j < kNumChunks256; ++j)
{
const __m256i in = input_vector256[j];
#if defined (USE_VNNI)
m256_add_dpbusd_epi32(sum0, in, row0[j]);
#else
sum0 = _mm256_add_epi32(sum0, m256_dpbusd_epi32(in, row0[j]));
#endif
}
output[0] = m256_hadd(sum0, biases_[0]);
}
}
else
{
// This case can never happen because kOutputDimensions
// is always 1 or a multiple of kSimdWidth.
assert(false);
}
#elif defined (USE_AVX2)
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
const auto output = reinterpret_cast<OutputType*>(buffer);
const auto input_vector = reinterpret_cast<const __m256i*>(input);
// kOutputDimensions is either 1 or a multiple of kSimdWidth
// because then it is also an input dimension.
if constexpr (kOutputDimensions % 4 == 0)
{
for (IndexType i = 0; i < kOutputDimensions; i += 4)
{
const IndexType offset0 = (i + 0) * kPaddedInputDimensions;
const IndexType offset1 = (i + 1) * kPaddedInputDimensions;
const IndexType offset2 = (i + 2) * kPaddedInputDimensions;
const IndexType offset3 = (i + 3) * kPaddedInputDimensions;
const __m128i bias = *reinterpret_cast<const __m128i*>(&biases_[i]);
__m128i* outptr = reinterpret_cast<__m128i*>(&output[i]);
const auto row0 = reinterpret_cast<const __m256i*>(&weights_[offset0]);
const auto row1 = reinterpret_cast<const __m256i*>(&weights_[offset1]);
const auto row2 = reinterpret_cast<const __m256i*>(&weights_[offset2]);
const auto row3 = reinterpret_cast<const __m256i*>(&weights_[offset3]);
#if defined (USE_VNNI)
__m256i sum0 = _mm256_setzero_si256();
__m256i sum1 = _mm256_setzero_si256();
__m256i sum2 = _mm256_setzero_si256();
__m256i sum3 = _mm256_setzero_si256();
const IndexType kStart = 0;
#else
__m256i sum0 = m256_dpbusd_epi32(input_vector[0], row0[0]);
__m256i sum1 = m256_dpbusd_epi32(input_vector[0], row1[0]);
__m256i sum2 = m256_dpbusd_epi32(input_vector[0], row2[0]);
__m256i sum3 = m256_dpbusd_epi32(input_vector[0], row3[0]);
const IndexType kStart = 1;
#endif
for (IndexType j = kStart; j < kNumChunks; ++j)
{
const __m256i in = input_vector[j];
#if defined (USE_VNNI)
m256_add_dpbusd_epi32(sum0, in, row0[j]);
m256_add_dpbusd_epi32(sum1, in, row1[j]);
m256_add_dpbusd_epi32(sum2, in, row2[j]);
m256_add_dpbusd_epi32(sum3, in, row3[j]);
#else
sum0 = _mm256_add_epi32(sum0, m256_dpbusd_epi32(in, row0[j]));
sum1 = _mm256_add_epi32(sum1, m256_dpbusd_epi32(in, row1[j]));
sum2 = _mm256_add_epi32(sum2, m256_dpbusd_epi32(in, row2[j]));
sum3 = _mm256_add_epi32(sum3, m256_dpbusd_epi32(in, row3[j]));
#endif
}
*outptr = m256_haddx4(sum0, sum1, sum2, sum3, bias);
}
}
else if constexpr (kOutputDimensions == 1)
{
const auto row0 = reinterpret_cast<const __m256i*>(&weights_[0]);
#if defined (USE_VNNI)
__m256i sum0 = _mm256_setzero_si256();
const IndexType kStart = 0;
#else
__m256i sum0 = m256_dpbusd_epi32(input_vector[0], row0[0]);
const IndexType kStart = 1;
#endif
for (IndexType j = kStart; j < kNumChunks; ++j)
{
const __m256i in = input_vector[j];
#if defined (USE_VNNI)
m256_add_dpbusd_epi32(sum0, in, row0[j]);
#else
sum0 = _mm256_add_epi32(sum0, m256_dpbusd_epi32(in, row0[j]));
#endif
}
output[0] = m256_hadd(sum0, biases_[0]);
}
else
{
// This case can never happen because kOutputDimensions
// is always 1 or a multiple of kSimdWidth.
assert(false);
}
#elif defined (USE_SSSE3)
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
auto output = reinterpret_cast<OutputType*>(buffer);
const auto input_vector = reinterpret_cast<const __m128i*>(input);
// kOutputDimensions is either 1 or a multiple of kSimdWidth
// because then it is also an input dimension.
if constexpr (kOutputDimensions % 4 == 0)
{
for (IndexType i = 0; i < kOutputDimensions; i += 4)
{
const IndexType offset0 = (i + 0) * kPaddedInputDimensions;
const IndexType offset1 = (i + 1) * kPaddedInputDimensions;
const IndexType offset2 = (i + 2) * kPaddedInputDimensions;
const IndexType offset3 = (i + 3) * kPaddedInputDimensions;
const __m128i bias = *reinterpret_cast<const __m128i*>(&biases_[i]);
__m128i* outptr = reinterpret_cast<__m128i*>(&output[i]);
const auto row0 = reinterpret_cast<const __m128i*>(&weights_[offset0]);
const auto row1 = reinterpret_cast<const __m128i*>(&weights_[offset1]);
const auto row2 = reinterpret_cast<const __m128i*>(&weights_[offset2]);
const auto row3 = reinterpret_cast<const __m128i*>(&weights_[offset3]);
__m128i sum0 = m128_dpbusd_epi32(input_vector[0], row0[0]);
__m128i sum1 = m128_dpbusd_epi32(input_vector[0], row1[0]);
__m128i sum2 = m128_dpbusd_epi32(input_vector[0], row2[0]);
__m128i sum3 = m128_dpbusd_epi32(input_vector[0], row3[0]);
for (int j = 1; j < (int)kNumChunks; ++j)
{
const __m128i in = input_vector[j];
sum0 = _mm_add_epi32(sum0, m128_dpbusd_epi32(in, row0[j]));
sum1 = _mm_add_epi32(sum1, m128_dpbusd_epi32(in, row1[j]));
sum2 = _mm_add_epi32(sum2, m128_dpbusd_epi32(in, row2[j]));
sum3 = _mm_add_epi32(sum3, m128_dpbusd_epi32(in, row3[j]));
}
*outptr = m128_haddx4(sum0, sum1, sum2, sum3, bias);
}
}
else if constexpr (kOutputDimensions == 1)
{
const auto row0 = reinterpret_cast<const __m128i*>(&weights_[0]);
__m128i sum0 = m128_dpbusd_epi32(input_vector[0], row0[0]);
for (int j = 1; j < (int)kNumChunks; ++j)
sum0 = _mm_add_epi32(sum0, m128_dpbusd_epi32(input_vector[j], row0[j]));
output[0] = m128_hadd(sum0, biases_[0]);
}
else
{
// This case can never happen because kOutputDimensions
// is always 1 or a multiple of kSimdWidth.
assert(false);
}
#else
@@ -695,11 +383,7 @@ namespace Eval::NNUE::Layers {
#if defined(USE_SSE2)
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
#ifndef USE_SSSE3
const __m128i kZeros = _mm_setzero_si128();
#else
const __m128i kOnes = _mm_set1_epi16(1);
#endif
const auto input_vector = reinterpret_cast<const __m128i*>(input);
#elif defined(USE_MMX)
@@ -722,9 +406,8 @@ namespace Eval::NNUE::Layers {
for (IndexType j = 0; j < kNumChunks; ++j) {
__m128i row_j = _mm_load_si128(&row[j]);
__m128i input_j = _mm_load_si128(&input_vector[j]);
__m128i row_signs = _mm_cmpgt_epi8(kZeros, row_j);
__m128i extended_row_lo = _mm_unpacklo_epi8(row_j, row_signs);
__m128i extended_row_hi = _mm_unpackhi_epi8(row_j, row_signs);
__m128i extended_row_lo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8);
__m128i extended_row_hi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8);
__m128i extended_input_lo = _mm_unpacklo_epi8(input_j, kZeros);
__m128i extended_input_hi = _mm_unpackhi_epi8(input_j, kZeros);
__m128i product_lo = _mm_madd_epi16(extended_row_lo, extended_input_lo);
@@ -746,9 +429,8 @@ namespace Eval::NNUE::Layers {
for (IndexType j = 0; j < kNumChunks; ++j) {
__m64 row_j = row[j];
__m64 input_j = input_vector[j];
__m64 row_signs = _mm_cmpgt_pi8(kZeros, row_j);
__m64 extended_row_lo = _mm_unpacklo_pi8(row_j, row_signs);
__m64 extended_row_hi = _mm_unpackhi_pi8(row_j, row_signs);
__m64 extended_row_lo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8);
__m64 extended_row_hi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8);
__m64 extended_input_lo = _mm_unpacklo_pi8(input_j, kZeros);
__m64 extended_input_hi = _mm_unpackhi_pi8(input_j, kZeros);
__m64 product_lo = _mm_madd_pi16(extended_row_lo, extended_input_lo);
@@ -798,10 +480,26 @@ namespace Eval::NNUE::Layers {
PreviousLayer previous_layer_;
alignas(kCacheLineSize) BiasType biases_[kOutputDimensions];
alignas(kCacheLineSize)
WeightType weights_[kOutputDimensions * kPaddedInputDimensions];
alignas(kCacheLineSize) WeightType weights_[kOutputDimensions * kPaddedInputDimensions];
#if defined (USE_SSSE3)
struct CanSaturate {
int count;
struct Entry {
uint16_t out;
uint16_t in;
int8_t w;
} ids[kPaddedInputDimensions * kOutputDimensions * 3 / 4];
void add(int i, int j, int8_t w) {
ids[count].out = i;
ids[count].in = j;
ids[count].w = w;
++count;
}
} canSaturate16;
#endif
};
} // namespace Eval::NNUE::Layers
} // namespace Stockfish::Eval::NNUE::Layers
#endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED
+3 -7
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,11 +23,7 @@
#include "../nnue_common.h"
#include <string>
#include <cstdint>
#include <type_traits>
namespace Eval::NNUE::Layers {
namespace Stockfish::Eval::NNUE::Layers {
// Clipped ReLU
template <typename PreviousLayer>
@@ -195,6 +191,6 @@ namespace Eval::NNUE::Layers {
PreviousLayer previous_layer_;
};
} // namespace Eval::NNUE::Layers
} // namespace Stockfish::Eval::NNUE::Layers
#endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED
+3 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@
#include "../nnue_common.h"
namespace Eval::NNUE::Layers {
namespace Stockfish::Eval::NNUE::Layers {
// Input layer
template <IndexType OutputDimensions, IndexType Offset = 0>
@@ -89,6 +89,6 @@ class InputSlice {
private:
};
} // namespace Layers
} // namespace Stockfish::Eval::NNUE::Layers
#endif // #ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED
+3 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@
#include "nnue_architecture.h"
namespace Eval::NNUE {
namespace Stockfish::Eval::NNUE {
// Class that holds the result of affine transformation of input features
struct alignas(kCacheLineSize) Accumulator {
@@ -31,6 +31,6 @@ namespace Eval::NNUE {
bool computed_accumulation;
};
} // namespace Eval::NNUE
} // namespace Stockfish::Eval::NNUE
#endif // NNUE_ACCUMULATOR_H_INCLUDED
+3 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,7 +24,7 @@
// Defines the network structure
#include "architectures/halfkp_256x2-32-32.h"
namespace Eval::NNUE {
namespace Stockfish::Eval::NNUE {
static_assert(kTransformedFeatureDimensions % kMaxSimdWidth == 0, "");
static_assert(Network::kOutputDimensions == 1, "");
@@ -33,6 +33,6 @@ namespace Eval::NNUE {
// Trigger for full calculation instead of difference calculation
constexpr auto kRefreshTriggers = RawFeatures::kRefreshTriggers;
} // namespace Eval::NNUE
} // namespace Stockfish::Eval::NNUE
#endif // #ifndef NNUE_ARCHITECTURE_H_INCLUDED
+11 -4
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -52,7 +52,7 @@ namespace sys = std::filesystem;
#include <arm_neon.h>
#endif
namespace Eval::NNUE {
namespace Stockfish::Eval::NNUE {
// Version of the evaluation file
constexpr std::uint32_t kVersion = 0x7AF32F16u;
@@ -99,7 +99,14 @@ namespace Eval::NNUE {
PS_END2 = 12 * SQUARE_NB + 1
};
extern const uint32_t kpp_board_index[PIECE_NB][COLOR_NB];
constexpr uint32_t kpp_board_index[COLOR_NB][PIECE_NB] = {
// convention: W - us, B - them
// viewed from other side, W and B are reversed
{ PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_KING, PS_NONE,
PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_KING, PS_NONE },
{ PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_KING, PS_NONE,
PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_KING, PS_NONE }
};
// Type of input feature after conversion
using TransformedFeatureType = std::uint8_t;
@@ -133,6 +140,6 @@ namespace Eval::NNUE {
return result;
}
} // namespace Eval::NNUE
} // namespace Stockfish::Eval::NNUE
#endif // #ifndef NNUE_COMMON_H_INCLUDED
+3 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,7 +28,7 @@
#include <cstring>
#include <string>
namespace Eval::NNUE {
namespace Stockfish::Eval::NNUE {
// If vector instructions are enabled, we update and refresh the
// accumulator tile by tile such that each tile fits in the CPU's
@@ -510,6 +510,6 @@ namespace Eval::NNUE {
WeightType weights_[kHalfDimensions * kInputDimensions];
};
} // namespace Eval::NNUE
} // namespace Stockfish::Eval::NNUE
#endif // #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED
+30 -14
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,27 +24,30 @@
#include "position.h"
#include "thread.h"
namespace Stockfish {
namespace {
#define V Value
#define S(mg, eg) make_score(mg, eg)
// Pawn penalties
constexpr Score Backward = S( 8, 25);
constexpr Score Doubled = S(10, 55);
constexpr Score Backward = S( 9, 22);
constexpr Score Doubled = S(13, 51);
constexpr Score DoubledEarly = S(20, 7);
constexpr Score Isolated = S( 3, 15);
constexpr Score WeakLever = S( 3, 55);
constexpr Score WeakUnopposed = S(13, 25);
constexpr Score WeakLever = S( 4, 58);
constexpr Score WeakUnopposed = S(13, 24);
// Bonus for blocked pawns at 5th or 6th rank
constexpr Score BlockedPawn[2] = { S(-13, -4), S(-5, 2) };
constexpr Score BlockedPawn[2] = { S(-17, -6), S(-9, 2) };
constexpr Score BlockedStorm[RANK_NB] = {
S(0, 0), S(0, 0), S(76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2)
S(0, 0), S(0, 0), S(75, 78), S(-8, 16), S(-6, 10), S(-6, 6), S(0, 2)
};
// Connected pawn bonus
constexpr int Connected[RANK_NB] = { 0, 5, 7, 11, 24, 48, 86 };
constexpr int Connected[RANK_NB] = { 0, 5, 7, 11, 23, 48, 87 };
// 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.
@@ -66,10 +69,11 @@ namespace {
{ V(-17), V( -13), V( 100), V( 4), V( 9), V(-16), V(-31) }
};
// KingOnFile[semi-open Us][semi-open Them] contains bonuses/penalties
// for king when the king is on a semi-open or open file.
constexpr Score KingOnFile[2][2] = {{ S(-19,12), S(-6, 7) },
{ S( 0, 2), S( 6,-5) }};
constexpr Score KingOnFile[2][2] = {{ S(-21,10), S(-7, 1) },
{ S( 0,-3), S( 9,-4) }};
#undef S
#undef V
@@ -85,13 +89,14 @@ namespace {
constexpr Color Them = ~Us;
constexpr Direction Up = pawn_push(Us);
constexpr Direction Down = -Up;
Bitboard neighbours, stoppers, support, phalanx, opposed;
Bitboard lever, leverPush, blocked;
Square s;
bool backward, passed, doubled;
Score score = SCORE_ZERO;
const Square* pl = pos.squares<PAWN>(Us);
Bitboard b = pos.pieces(Us, PAWN);
Bitboard ourPawns = pos.pieces( Us, PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN);
@@ -104,8 +109,10 @@ namespace {
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)
while (b)
{
s = pop_lsb(b);
assert(pos.piece_on(s) == make_piece(Us, PAWN));
Rank r = relative_rank(Us, s);
@@ -121,6 +128,13 @@ namespace {
phalanx = neighbours & rank_bb(s);
support = neighbours & rank_bb(s - Up);
if (doubled)
{
// Additional doubled penalty if none of their pawns is fixed
if (!(ourPawns & shift<Down>(theirPawns | pawn_attacks_bb<Them>(theirPawns))))
score -= DoubledEarly;
}
// A pawn is backward when it is behind all pawns of the same color on
// the adjacent files and cannot safely advance.
backward = !(neighbours & forward_ranks_bb(Them, s + Up))
@@ -170,7 +184,7 @@ namespace {
else if (backward)
score -= Backward
+ WeakUnopposed * !opposed;
+ WeakUnopposed * !opposed * bool(~(FileABB | FileHBB) & s);
if (!support)
score -= Doubled * doubled
@@ -277,7 +291,7 @@ Score Entry::do_king_safety(const Position& pos) {
if (pawns & attacks_bb<KING>(ksq))
minPawnDist = 1;
else while (pawns)
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns)));
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(pawns)));
return shelter - make_score(0, 16 * minPawnDist);
}
@@ -287,3 +301,5 @@ template Score Entry::do_king_safety<WHITE>(const Position& pos);
template Score Entry::do_king_safety<BLACK>(const Position& pos);
} // namespace Pawns
} // namespace Stockfish
+3 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@
#include "position.h"
#include "types.h"
namespace Pawns {
namespace Stockfish::Pawns {
/// Pawns::Entry contains various information about a pawn structure. A lookup
/// to the pawn hash table (performed by calling the probe function) returns a
@@ -65,6 +65,6 @@ typedef HashTable<Entry, 131072> Table;
Entry* probe(const Position& pos);
} // namespace Pawns
} // namespace Stockfish::Pawns
#endif // #ifndef PAWNS_H_INCLUDED
+71 -78
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -39,6 +39,8 @@
using std::string;
namespace Stockfish {
namespace Zobrist {
Key psq[PIECE_NB][SQUARE_NB];
@@ -76,7 +78,7 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) {
<< std::setfill(' ') << std::dec << "\nCheckers: ";
for (Bitboard b = pos.checkers(); b; )
os << UCI::square(pop_lsb(&b)) << " ";
os << UCI::square(pop_lsb(b)) << " ";
if ( int(Tablebases::MaxCardinality) >= popcount(pos.pieces())
&& !pos.can_castle(ANY_CASTLING))
@@ -202,7 +204,6 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
std::memset(this, 0, sizeof(Position));
std::memset(si, 0, sizeof(StateInfo));
std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE);
st = si;
ss >> std::noskipws;
@@ -255,6 +256,8 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
set_castling_right(c, rsq);
}
set_state(st);
// 4. En passant square.
// Ignore if square is invalid or not on side to move relative rank 6.
bool enpassant = false;
@@ -268,12 +271,24 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
// a) side to move have a pawn threatening epSquare
// b) there is an enemy pawn in front of epSquare
// c) there is no piece on epSquare or behind epSquare
// d) enemy pawn didn't block a check of its own color by moving forward
enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN)
&& (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))
&& !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))));
&& !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))))
&& ( file_of(square<KING>(sideToMove)) == file_of(st->epSquare)
|| !(blockers_for_king(sideToMove) & (st->epSquare + pawn_push(~sideToMove))));
}
if (!enpassant)
// It's necessary for st->previous to be intialized in this way because legality check relies on its existence
if (enpassant) {
st->previous = new StateInfo();
remove_piece(st->epSquare - pawn_push(sideToMove));
st->previous->checkersBB = attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove);
st->previous->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square<KING>(WHITE), st->previous->pinners[BLACK]);
st->previous->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square<KING>(BLACK), st->previous->pinners[WHITE]);
put_piece(make_piece(~sideToMove, PAWN), st->epSquare - pawn_push(sideToMove));
}
else
st->epSquare = SQ_NONE;
// 5-6. Halfmove clock and fullmove number
@@ -285,7 +300,8 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
chess960 = isChess960;
thisThread = th;
set_state(st);
st->accumulator.state[WHITE] = Eval::NNUE::INIT;
st->accumulator.state[BLACK] = Eval::NNUE::INIT;
assert(pos_is_ok());
@@ -309,7 +325,7 @@ void Position::set_castling_right(Color c, Square rfrom) {
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)
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto))
& ~(kfrom | rfrom);
}
@@ -348,7 +364,7 @@ void Position::set_state(StateInfo* si) const {
for (Bitboard b = pieces(); b; )
{
Square s = pop_lsb(&b);
Square s = pop_lsb(b);
Piece pc = piece_on(s);
si->key ^= Zobrist::psq[pc][s];
@@ -399,7 +415,7 @@ Position& Position::set(const string& code, Color c, StateInfo* si) {
/// Position::fen() returns a FEN representation of the position. In case of
/// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function.
const string Position::fen() const {
string Position::fen() const {
int emptyCnt;
std::ostringstream ss;
@@ -465,7 +481,7 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners
while (snipers)
{
Square sniperSq = pop_lsb(&snipers);
Square sniperSq = pop_lsb(snipers);
Bitboard b = between_bb(s, sniperSq) & occupancy;
if (b && !more_than_one(b))
@@ -506,23 +522,11 @@ bool Position::legal(Move m) const {
assert(color_of(moved_piece(m)) == us);
assert(piece_on(square<KING>(us)) == make_piece(us, KING));
// En passant captures are a tricky special case. Because they are rather
// uncommon, we do it simply by testing whether the king is attacked after
// the move is made.
if (type_of(m) == ENPASSANT)
{
Square ksq = square<KING>(us);
Square capsq = to - pawn_push(us);
Bitboard occupied = (pieces() ^ from ^ capsq) | to;
assert(to == ep_square());
assert(moved_piece(m) == make_piece(us, PAWN));
assert(piece_on(capsq) == make_piece(~us, PAWN));
assert(piece_on(to) == NO_PIECE);
return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK))
&& !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
}
// st->previous->blockersForKing consider capsq as empty.
// If pinned, it has to move along the king ray.
if (type_of(m) == EN_PASSANT)
return !(st->previous->blockersForKing[sideToMove] & from)
|| aligned(from, to, square<KING>(us));
// Castling moves generation does not check if the castling path is clear of
// enemy attacks, it is delayed at a later time: now!
@@ -537,22 +541,20 @@ bool Position::legal(Move m) const {
if (attackers_to(s) & pieces(~us))
return false;
// In case of Chess960, verify that when moving the castling rook we do
// not discover some hidden checker.
// In case of Chess960, verify if the Rook blocks some checks
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
return !chess960
|| !(attacks_bb<ROOK>(to, pieces() ^ to_sq(m)) & pieces(~us, ROOK, QUEEN));
return !chess960 || !(blockers_for_king(us) & to_sq(m));
}
// If the moving piece is a king, check whether the destination square is
// attacked by the opponent.
if (type_of(piece_on(from)) == KING)
return !(attackers_to(to) & pieces(~us));
return !(attackers_to(to, pieces() ^ from) & pieces(~us));
// A non-king move is legal if and only if it is not pinned or it
// is moving along the ray towards or away from the king.
return !(blockers_for_king(us) & from)
|| aligned(from, to, square<KING>(us));
return !(blockers_for_king(us) & from)
|| aligned(from, to, square<KING>(us));
}
@@ -568,8 +570,10 @@ bool Position::pseudo_legal(const Move m) const {
Piece pc = moved_piece(m);
// Use a slower but simpler function for uncommon cases
// yet we skip the legality check of MoveList<LEGAL>().
if (type_of(m) != NORMAL)
return MoveList<LEGAL>(*this).contains(m);
return checkers() ? MoveList< EVASIONS>(*this).contains(m)
: MoveList<NON_EVASIONS>(*this).contains(m);
// Is not a promotion, so promotion piece must be empty
if (promotion_type(m) - KNIGHT != NO_PIECE_TYPE)
@@ -614,8 +618,8 @@ bool Position::pseudo_legal(const Move m) const {
if (more_than_one(checkers()))
return false;
// Our move must be a blocking evasion or a capture of the checking piece
if (!((between_bb(lsb(checkers()), square<KING>(us)) | checkers()) & to))
// Our move must be a blocking interposition or a capture of the checking piece
if (!(between_bb(square<KING>(us), lsb(checkers())) & to))
return false;
}
// In case of king moves under check we have to remove king so as to catch
@@ -655,31 +659,24 @@ bool Position::gives_check(Move m) const {
case PROMOTION:
return attacks_bb(promotion_type(m), to, pieces() ^ from) & square<KING>(~sideToMove);
// En passant capture with check? We have already handled the case
// of direct checks and ordinary discovered check, so the only case we
// need to handle is the unusual case of a discovered check through
// the captured pawn.
case ENPASSANT:
{
Square capsq = make_square(file_of(to), rank_of(from));
Bitboard b = (pieces() ^ from ^ capsq) | to;
// The double-pushed pawn blocked a check? En Passant will remove the blocker.
// The only discovery check that wasn't handle is through capsq and fromsq
// So the King must be in the same rank as fromsq to consider this possibility.
// st->previous->blockersForKing consider capsq as empty.
case EN_PASSANT:
return st->previous->checkersBB
|| ( rank_of(square<KING>(~sideToMove)) == rank_of(from)
&& st->previous->blockersForKing[~sideToMove] & from);
return (attacks_bb< ROOK>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK))
| (attacks_bb<BISHOP>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP));
}
case CASTLING:
default: //CASTLING
{
Square kfrom = from;
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);
// Castling is encoded as 'king captures the rook'
Square ksq = square<KING>(~sideToMove);
Square rto = relative_square(sideToMove, to > from ? SQ_F1 : SQ_D1);
return (attacks_bb<ROOK>(rto) & square<KING>(~sideToMove))
&& (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
return (attacks_bb<ROOK>(rto) & ksq)
&& (attacks_bb<ROOK>(rto, pieces() ^ from ^ to) & ksq);
}
default:
assert(false);
return false;
}
}
@@ -719,7 +716,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
Square from = from_sq(m);
Square to = to_sq(m);
Piece pc = piece_on(from);
Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to);
Piece captured = type_of(m) == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to);
assert(color_of(pc) == us);
assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us));
@@ -745,7 +742,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// update non-pawn material.
if (type_of(captured) == PAWN)
{
if (type_of(m) == ENPASSANT)
if (type_of(m) == EN_PASSANT)
{
capsq -= pawn_push(us);
@@ -772,7 +769,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// Update board and piece lists
remove_piece(capsq);
if (type_of(m) == ENPASSANT)
if (type_of(m) == EN_PASSANT)
board[capsq] = NO_PIECE;
// Update material hash key and prefetch access to materialTable
@@ -818,7 +815,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// If the moving piece is a pawn do some special extra work
if (type_of(pc) == PAWN)
{
// Set en-passant square if the moved pawn can be captured
// Set en passant square if the moved pawn can be captured
if ( (int(to) ^ int(from)) == 16
&& (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN)))
{
@@ -941,7 +938,7 @@ void Position::undo_move(Move m) {
{
Square capsq = to;
if (type_of(m) == ENPASSANT)
if (type_of(m) == EN_PASSANT)
{
capsq -= pawn_push(us);
@@ -1020,7 +1017,7 @@ void Position::do_null_move(StateInfo& newSt) {
}
st->key ^= Zobrist::side;
prefetch(TT.first_entry(st->key));
prefetch(TT.first_entry(key()));
++st->rule50;
st->pliesFromNull = 0;
@@ -1045,7 +1042,7 @@ void Position::undo_null_move() {
/// Position::key_after() computes the new hash key after the given move. Needed
/// for speculative prefetch. It doesn't recognize special moves like castling,
/// en-passant and promotions.
/// en passant and promotions.
Key Position::key_after(Move m) const {
@@ -1070,7 +1067,7 @@ bool Position::see_ge(Move m, Value threshold) const {
assert(is_ok(m));
// Only deal with normal moves, assume others pass a simple see
// Only deal with normal moves, assume others pass a simple SEE
if (type_of(m) != NORMAL)
return VALUE_ZERO >= threshold;
@@ -1116,7 +1113,7 @@ bool Position::see_ge(Move m, Value threshold) const {
if ((swap = PawnValueMg - swap) < res)
break;
occupied ^= lsb(bb);
occupied ^= least_significant_square_bb(bb);
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
}
@@ -1125,7 +1122,7 @@ bool Position::see_ge(Move m, Value threshold) const {
if ((swap = KnightValueMg - swap) < res)
break;
occupied ^= lsb(bb);
occupied ^= least_significant_square_bb(bb);
}
else if ((bb = stmAttackers & pieces(BISHOP)))
@@ -1133,7 +1130,7 @@ bool Position::see_ge(Move m, Value threshold) const {
if ((swap = BishopValueMg - swap) < res)
break;
occupied ^= lsb(bb);
occupied ^= least_significant_square_bb(bb);
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
}
@@ -1142,7 +1139,7 @@ bool Position::see_ge(Move m, Value threshold) const {
if ((swap = RookValueMg - swap) < res)
break;
occupied ^= lsb(bb);
occupied ^= least_significant_square_bb(bb);
attackers |= attacks_bb<ROOK>(to, occupied) & pieces(ROOK, QUEEN);
}
@@ -1151,7 +1148,7 @@ bool Position::see_ge(Move m, Value threshold) const {
if ((swap = QueenValueMg - swap) < res)
break;
occupied ^= lsb(bb);
occupied ^= least_significant_square_bb(bb);
attackers |= (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN))
| (attacks_bb<ROOK >(to, occupied) & pieces(ROOK , QUEEN));
}
@@ -1241,7 +1238,7 @@ bool Position::has_game_cycle(int ply) const {
Square s1 = from_sq(move);
Square s2 = to_sq(move);
if (!(between_bb(s1, s2) & pieces()))
if (!((between_bb(s1, s2) ^ s2) & pieces()))
{
if (ply > i)
return true;
@@ -1345,16 +1342,10 @@ bool Position::pos_is_ok() const {
assert(0 && "pos_is_ok: State");
for (Piece pc : Pieces)
{
if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))
|| pieceCount[pc] != std::count(board, board + SQUARE_NB, pc))
assert(0 && "pos_is_ok: Pieces");
for (int i = 0; i < pieceCount[pc]; ++i)
if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i)
assert(0 && "pos_is_ok: Index");
}
for (Color c : { WHITE, BLACK })
for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE})
{
@@ -1383,3 +1374,5 @@ void Position::sfen_pack(Tools::PackedSfen& sfen)
{
sfen = Tools::sfen_pack(*this);
}
} // namespace Stockfish
+15 -39
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
#include "bitboard.h"
#include "evaluate.h"
#include "psqt.h"
#include "types.h"
#include "nnue/nnue_accumulator.h"
@@ -33,6 +34,7 @@
#include "tools/packed_sfen.h"
#include "tools/sfen_packer.h"
namespace Stockfish {
/// StateInfo struct stores information needed to restore a Position object to
/// its previous state when we retract a move. Whenever a move is made on the
@@ -89,7 +91,7 @@ public:
// FEN string input/output
Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th);
Position& set(const std::string& code, Color c, StateInfo* si);
const std::string fen() const;
std::string fen() const;
// Position representation
Bitboard pieces(PieceType pt) const;
@@ -102,7 +104,6 @@ public:
bool empty(Square s) const;
template<PieceType Pt> int count(Color c) const;
template<PieceType Pt> int count() const;
template<PieceType Pt> const Square* squares(Color c) const;
template<PieceType Pt> Square square(Color c) const;
bool is_on_semiopen_file(Color c, Square s) const;
@@ -117,7 +118,7 @@ public:
Bitboard blockers_for_king(Color c) const;
Bitboard check_squares(PieceType pt) const;
Bitboard pinners(Color c) const;
bool is_discovery_check_on_king(Color c, Move m) const;
bool is_discovered_check_on_king(Color c, Move m) const;
// Attacks to/from a given square
Bitboard attackers_to(Square s) const;
@@ -130,7 +131,6 @@ public:
bool capture(Move m) const;
bool capture_or_promotion(Move m) const;
bool gives_check(Move m) const;
bool advanced_pawn_push(Move m) const;
Piece moved_piece(Move m) const;
Piece captured_piece() const;
@@ -217,8 +217,6 @@ private:
Bitboard byTypeBB[PIECE_TYPE_NB];
Bitboard byColorBB[COLOR_NB];
int pieceCount[PIECE_NB];
Square pieceList[PIECE_NB][16];
int index[SQUARE_NB];
int castlingRightsMask[SQUARE_NB];
Square castlingRookSquare[CASTLING_RIGHT_NB];
Bitboard castlingPath[CASTLING_RIGHT_NB];
@@ -230,10 +228,6 @@ private:
bool chess960;
};
namespace PSQT {
extern Score psq[PIECE_NB][SQUARE_NB];
}
extern std::ostream& operator<<(std::ostream& os, const Position& pos);
inline Color Position::side_to_move() const {
@@ -281,13 +275,9 @@ template<PieceType Pt> inline int Position::count() const {
return count<Pt>(WHITE) + count<Pt>(BLACK);
}
template<PieceType Pt> inline const Square* Position::squares(Color c) const {
return pieceList[make_piece(c, Pt)];
}
template<PieceType Pt> inline Square Position::square(Color c) const {
assert(pieceCount[make_piece(c, Pt)] == 1);
return squares<Pt>(c)[0];
assert(count<Pt>(c) == 1);
return lsb(pieces(c, Pt));
}
inline Square Position::ep_square() const {
@@ -338,7 +328,7 @@ 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 {
inline bool Position::is_discovered_check_on_king(Color c, Move m) const {
return st->blockersForKing[c] & from_sq(m);
}
@@ -346,17 +336,13 @@ inline bool Position::pawn_passed(Color c, Square s) const {
return !(pieces(~c, PAWN) & passed_pawn_span(c, s));
}
inline bool Position::advanced_pawn_push(Move m) const {
return type_of(moved_piece(m)) == PAWN
&& relative_rank(sideToMove, to_sq(m)) > RANK_5;
}
inline int Position::pawns_on_same_color_squares(Color c, Square s) const {
return popcount(pieces(c, PAWN) & ((DarkSquares & s) ? DarkSquares : ~DarkSquares));
}
inline Key Position::key() const {
return st->key;
return st->rule50 < 14 ? st->key
: st->key ^ make_key((st->rule50 - 14) / 8);
}
inline Key Position::pawn_key() const {
@@ -405,7 +391,7 @@ inline bool Position::capture_or_promotion(Move m) const {
inline bool Position::capture(Move m) const {
assert(is_ok(m));
// Castling is encoded as "king captures rook"
return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT;
return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == EN_PASSANT;
}
inline Piece Position::captured_piece() const {
@@ -421,35 +407,25 @@ inline void Position::put_piece(Piece pc, Square s) {
board[s] = pc;
byTypeBB[ALL_PIECES] |= byTypeBB[type_of(pc)] |= s;
byColorBB[color_of(pc)] |= s;
index[s] = pieceCount[pc]++;
pieceList[pc][index[s]] = s;
pieceCount[pc]++;
pieceCount[make_piece(color_of(pc), ALL_PIECES)]++;
psq += PSQT::psq[pc][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;
/* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */
Square lastSquare = pieceList[pc][--pieceCount[pc]];
index[lastSquare] = index[s];
pieceList[pc][index[lastSquare]] = lastSquare;
pieceList[pc][pieceCount[pc]] = SQ_NONE;
pieceCount[pc]--;
pieceCount[make_piece(color_of(pc), ALL_PIECES)]--;
psq -= PSQT::psq[pc][s];
}
inline void Position::move_piece(Square from, Square to) {
// index[from] is not updated and becomes stale. This works as long as index[]
// is accessed just by known occupied squares.
Piece pc = board[from];
Bitboard fromTo = from | to;
byTypeBB[ALL_PIECES] ^= fromTo;
@@ -457,8 +433,6 @@ inline void Position::move_piece(Square from, Square to) {
byColorBB[color_of(pc)] ^= fromTo;
board[from] = NO_PIECE;
board[to] = pc;
index[to] = index[from];
pieceList[pc][index[to]] = to;
psq += PSQT::psq[pc][to] - PSQT::psq[pc][from];
}
@@ -471,4 +445,6 @@ inline StateInfo* Position::state() const {
return st;
}
} // namespace Stockfish
#endif // #ifndef POSITION_H_INCLUDED
+44 -35
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,19 +16,23 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "psqt.h"
#include <algorithm>
#include "types.h"
#include "bitboard.h"
#include "types.h"
namespace PSQT {
namespace Stockfish {
#define S(mg, eg) make_score(mg, eg)
namespace
{
// Bonus[PieceType][Square / 2] contains Piece-Square scores. For each piece
// type on a given square a (middlegame, endgame) score pair is assigned. Table
// is defined for files A..D and white side: it is symmetric for black side and
// second half of the files.
auto constexpr S = make_score;
// 'Bonus' contains Piece-Square parameters.
// Scores are explicit for files A to D, implicitly mirrored for E to H.
constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
{ },
{ },
@@ -43,14 +47,14 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
{ S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) }
},
{ // Bishop
{ 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) }
{ S(-37,-40), S(-4 ,-21), S( -6,-26), S(-16, -8) },
{ S(-11,-26), S( 6, -9), S( 13,-12), S( 3, 1) },
{ S(-5 ,-11), S( 15, -1), S( -4, -1), S( 12, 7) },
{ S(-4 ,-14), S( 8, -4), S( 18, 0), S( 27, 12) },
{ S(-8 ,-12), S( 20, -1), S( 15,-10), S( 22, 11) },
{ S(-11,-21), S( 4, 4), S( 1, 3), S( 8, 4) },
{ S(-12,-22), S(-10,-14), S( 4, -1), S( 0, 1) },
{ S(-34,-32), S( 1,-29), S(-10,-26), S(-16,-17) }
},
{ // Rook
{ S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) },
@@ -64,13 +68,13 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
},
{ // Queen
{ S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) },
{ S(-3,-55), S( 5,-31), S( 8,-22), S(12, -4) },
{ S(-3,-54), S( 5,-31), S( 8,-22), S(12, -4) },
{ S(-3,-39), S( 6,-18), S(13, -9), S( 7, 3) },
{ S( 4,-23), S( 5, -3), S( 9, 13), S( 8, 24) },
{ S( 0,-29), S(14, -6), S(12, 9), S( 5, 21) },
{ S(-4,-38), S(10,-18), S( 6,-12), S( 8, 1) },
{ S(-4,-38), S(10,-18), S( 6,-11), S( 8, 1) },
{ S(-5,-50), S( 6,-27), S(10,-24), S( 8, -8) },
{ S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) }
{ S(-2,-74), S(-2,-52), S( 1,-43), S(-2,-34) }
},
{ // King
{ S(271, 1), S(327, 45), S(271, 85), S(198, 76) },
@@ -87,19 +91,22 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
constexpr Score PBonus[RANK_NB][FILE_NB] =
{ // Pawn (asymmetric distribution)
{ },
{ S( 3,-10), S( 3, -6), S( 10, 10), S( 19, 0), S( 16, 14), S( 19, 7), S( 7, -5), S( -5,-19) },
{ S( -9,-10), S(-15,-10), S( 11,-10), S( 15, 4), S( 32, 4), S( 22, 3), S( 5, -6), S(-22, -4) },
{ S( -4, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S( -8, -9) },
{ S( 13, 10), S( 0, 5), S(-13, 4), S( 1, -5), S( 11, -5), S( -2, -5), S(-13, 14), S( 5, 9) },
{ S( 5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S( -8, 13) },
{ S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) }
{ S( 2, -8), S( 4, -6), S( 11, 9), S( 18, 5), S( 16, 16), S( 21, 6), S( 9, -6), S( -3,-18) },
{ S( -9, -9), S(-15, -7), S( 11,-10), S( 15, 5), S( 31, 2), S( 23, 3), S( 6, -8), S(-20, -5) },
{ S( -3, 7), S(-20, 1), S( 8, -8), S( 19, -2), S( 39,-14), S( 17,-13), S( 2,-11), S( -5, -6) },
{ S( 11, 12), S( -4, 6), S(-11, 2), S( 2, -6), S( 11, -5), S( 0, -4), S(-12, 14), S( 5, 9) },
{ S( 3, 27), S(-11, 18), S( -6, 19), S( 22, 29), S( -8, 30), S( -5, 9), S(-14, 8), S(-11, 14) },
{ S( -7, -1), S( 6,-14), S( -2, 13), S(-11, 22), S( 4, 24), S(-14, 17), S( 10, 7), S( -9, 7) }
};
#undef S
} // namespace
namespace PSQT
{
Score psq[PIECE_NB][SQUARE_NB];
// PSQT::init() initializes piece-square tables: the white halves of the tables are
// copied from Bonus[] and PBonus[], adding the piece value, then the black halves of
// the tables are initialized by flipping and changing the sign of the white scores.
@@ -107,16 +114,18 @@ void init() {
for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING})
{
Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
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][flip_rank(s)] = -psq[pc][s];
}
for (Square s = SQ_A1; s <= SQ_H8; ++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][flip_rank(s)] = -psq[pc][s];
}
}
}
} // namespace PSQT
} // namespace Stockfish
+38
View File
@@ -0,0 +1,38 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PSQT_H_INCLUDED
#define PSQT_H_INCLUDED
#include "types.h"
namespace Stockfish::PSQT
{
extern Score psq[PIECE_NB][SQUARE_NB];
// Fill psqt array from a set of internally linked parameters
extern void init();
} // namespace Stockfish::PSQT
#endif // PSQT_H_INCLUDED
+153 -99
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -37,6 +37,8 @@
#include "uci.h"
#include "syzygy/tbprobe.h"
namespace Stockfish {
namespace Search {
LimitsType Limits;
@@ -56,8 +58,7 @@ namespace {
constexpr uint64_t TtHitAverageWindow = 4096;
constexpr uint64_t TtHitAverageResolution = 1024;
// Razor and futility margins
constexpr int RazorMargin = 510;
// Futility margin
Value futility_margin(Depth d, bool improving) {
return Value(234 * (d - improving));
}
@@ -76,10 +77,10 @@ namespace {
// History and stats update bonus, based on depth
int stat_bonus(Depth d) {
return d > 13 ? 29 : 17 * d * d + 134 * d - 134;
return d > 14 ? 66 : 6 * d * d + 231 * d - 206;
}
// Add a small random component to draw evaluations to avoid 3fold-blindness
// Add a small random component to draw evaluations to avoid 3-fold blindness
Value value_draw(Thread* thisThread) {
return VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1);
}
@@ -420,7 +421,7 @@ void Thread::search() {
while (true)
{
Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - searchAgainCounter);
bestValue = ::search<PV>(rootPos, ss, alpha, beta, adjustedDepth, false);
bestValue = Stockfish::search<PV>(rootPos, ss, alpha, beta, adjustedDepth, false);
// Bring the best move to the front. It is critical that sorting
// is done with a stable algorithm because all the values but the
@@ -608,12 +609,12 @@ namespace {
// Step 1. Initialize node
Thread* thisThread = pos.this_thread();
ss->inCheck = pos.checkers();
priorCapture = pos.captured_piece();
Color us = pos.side_to_move();
moveCount = captureCount = quietCount = ss->moveCount = 0;
bestValue = -VALUE_INFINITE;
maxValue = VALUE_INFINITE;
ss->inCheck = pos.checkers();
priorCapture = pos.captured_piece();
Color us = pos.side_to_move();
moveCount = captureCount = quietCount = ss->moveCount = 0;
bestValue = -VALUE_INFINITE;
maxValue = VALUE_INFINITE;
// Check for the available remaining time
if (thisThread == Threads.main())
@@ -673,6 +674,7 @@ namespace {
ss->ttPv = PvNode || (ss->ttHit && tte->is_pv());
formerPv = ss->ttPv && !PvNode;
// Update low ply history for previous move if we are near root and position is or has been in PV
if ( ss->ttPv
&& depth > 12
&& ss->ply - 1 < MAX_LPH
@@ -697,6 +699,7 @@ namespace {
{
if (ttValue >= beta)
{
// Bonus for a quiet ttMove that fails high
if (!pos.capture_or_promotion(ttMove))
update_quiet_stats(pos, ss, ttMove, stat_bonus(depth), depth);
@@ -713,6 +716,8 @@ namespace {
}
}
// Partial workaround for the graph history interaction problem
// For high rule50 counts don't produce transposition table cutoffs.
if (pos.rule50_count() < 90)
return ttValue;
}
@@ -786,6 +791,7 @@ namespace {
if (eval == VALUE_NONE)
ss->staticEval = eval = evaluate(pos);
// Randomize draw evaluation
if (eval == VALUE_DRAW)
eval = value_draw(thisThread);
@@ -796,38 +802,46 @@ namespace {
}
else
{
// In case of null move search use previous static eval with a different sign
// and addition of two tempos
if ((ss-1)->currentMove != MOVE_NULL)
ss->staticEval = eval = evaluate(pos);
else
ss->staticEval = eval = -(ss-1)->staticEval + 2 * Tempo;
// Save static evaluation into transposition table
tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval);
}
// Step 7. Razoring (~1 Elo)
if ( !rootNode // The required rootNode PV handling is not available in qsearch
&& depth == 1
&& eval <= alpha - RazorMargin)
return qsearch<NT>(pos, ss, alpha, beta);
// Use static evaluation difference to improve quiet move ordering
if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture)
{
int bonus = std::clamp(-depth * 4 * int((ss-1)->staticEval + ss->staticEval - 2 * Tempo), -1000, 1000);
thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus;
}
// Set up improving flag that is used in various pruning heuristics
// We define position as improving if static evaluation of position is better
// Than the previous static evaluation at our turn
// In case of us being in check at our previous move we look at move prior to it
improving = (ss-2)->staticEval == VALUE_NONE
? ss->staticEval > (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE
: ss->staticEval > (ss-2)->staticEval;
// Step 8. Futility pruning: child node (~50 Elo)
// Step 7. Futility pruning: child node (~50 Elo)
if ( !PvNode
&& depth < 8
&& depth < 9
&& eval - futility_margin(depth, improving) >= beta
&& eval < VALUE_KNOWN_WIN) // Do not return unproven wins
return eval;
// Step 9. Null move search with verification search (~40 Elo)
// Step 8. Null move search with verification search (~40 Elo)
if ( !PvNode
&& (ss-1)->currentMove != MOVE_NULL
&& (ss-1)->statScore < 22977
&& (ss-1)->statScore < 24185
&& eval >= beta
&& eval >= ss->staticEval
&& ss->staticEval >= beta - 30 * depth - 28 * improving + 84 * ss->ttPv + 168
&& ss->staticEval >= beta - 24 * depth - 34 * improving + 162 * ss->ttPv + 159
&& !excludedMove
&& pos.non_pawn_material(us)
&& (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
@@ -835,7 +849,7 @@ namespace {
assert(eval - beta >= 0);
// Null move dynamic reduction based on depth and value
Depth R = (1015 + 85 * depth) / 256 + std::min(int(eval - beta) / 191, 3);
Depth R = (1062 + 68 * depth) / 256 + std::min(int(eval - beta) / 190, 3);
ss->currentMove = MOVE_NULL;
ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0];
@@ -871,9 +885,9 @@ namespace {
}
}
probCutBeta = beta + 183 - 49 * improving;
probCutBeta = beta + 209 - 44 * improving;
// Step 10. ProbCut (~10 Elo)
// Step 9. ProbCut (~10 Elo)
// If we have a good enough capture and a reduced search returns a value
// much above beta, we can (almost) safely prune the previous move.
if ( !PvNode
@@ -899,6 +913,7 @@ namespace {
return probCutBeta;
assert(probCutBeta < VALUE_INFINITE);
MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory);
int probCutCount = 0;
bool ttPv = ss->ttPv;
@@ -946,7 +961,7 @@ namespace {
ss->ttPv = ttPv;
}
// Step 11. If the position is not in TT, decrease depth by 2
// Step 10. If the position is not in TT, decrease depth by 2
if ( PvNode
&& depth >= 6
&& !ttMove)
@@ -954,6 +969,23 @@ namespace {
moves_loop: // When in check, search starts from here
ttCapture = ttMove && pos.capture_or_promotion(ttMove);
// Step 11. A small Probcut idea, when we are in check
probCutBeta = beta + 400;
if ( ss->inCheck
&& !PvNode
&& depth >= 4
&& ttCapture
&& (tte->bound() & BOUND_LOWER)
&& tte->depth() >= depth - 3
&& ttValue >= probCutBeta
&& abs(ttValue) <= VALUE_KNOWN_WIN
&& abs(beta) <= VALUE_KNOWN_WIN
)
return probCutBeta;
const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory,
nullptr , (ss-4)->continuationHistory,
nullptr , (ss-6)->continuationHistory };
@@ -970,7 +1002,6 @@ moves_loop: // When in check, search starts from here
value = bestValue;
singularQuietLMR = moveCountPruning = false;
ttCapture = ttMove && pos.capture_or_promotion(ttMove);
// Mark this node as being searched
ThreadHolding th(thisThread, posKey, ss->ply);
@@ -1012,6 +1043,14 @@ moves_loop: // When in check, search starts from here
movedPiece = pos.moved_piece(move);
givesCheck = pos.gives_check(move);
// Indicate PvNodes that will probably fail low if node was searched with non-PV search
// at depth equal or greater to current depth and result of this search was far below alpha
bool likelyFailLow = PvNode
&& ttMove
&& (tte->bound() & BOUND_UPPER)
&& ttValue < alpha + 200 + 100 * depth
&& tte->depth() >= depth;
// Calculate new depth for this move
newDepth = depth - 1;
@@ -1027,8 +1066,20 @@ moves_loop: // When in check, search starts from here
// Reduced depth of the next LMR search
int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0);
if ( !captureOrPromotion
&& !givesCheck)
if ( captureOrPromotion
|| givesCheck)
{
// Capture history based pruning when the move doesn't give check
if ( !givesCheck
&& lmrDepth < 1
&& captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0)
continue;
// SEE based pruning
if (!pos.see_ge(move, Value(-218) * depth)) // (~25 Elo)
continue;
}
else
{
// Countermoves based pruning (~20 Elo)
if ( lmrDepth < 4 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1)
@@ -1039,29 +1090,17 @@ moves_loop: // When in check, search starts from here
// Futility pruning: parent node (~5 Elo)
if ( lmrDepth < 7
&& !ss->inCheck
&& ss->staticEval + 266 + 170 * lmrDepth <= alpha
&& ss->staticEval + 174 + 157 * lmrDepth <= alpha
&& (*contHist[0])[movedPiece][to_sq(move)]
+ (*contHist[1])[movedPiece][to_sq(move)]
+ (*contHist[3])[movedPiece][to_sq(move)]
+ (*contHist[5])[movedPiece][to_sq(move)] / 2 < 27376)
+ (*contHist[5])[movedPiece][to_sq(move)] / 3 < 28255)
continue;
// Prune moves with negative SEE (~20 Elo)
if (!pos.see_ge(move, Value(-(30 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth)))
continue;
}
else
{
// Capture history based pruning when the move doesn't give check
if ( !givesCheck
&& lmrDepth < 1
&& captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0)
continue;
// SEE based pruning
if (!pos.see_ge(move, Value(-213) * depth)) // (~25 Elo)
continue;
}
}
// Step 14. Extensions (~75 Elo)
@@ -1082,6 +1121,7 @@ moves_loop: // When in check, search starts from here
{
Value singularBeta = ttValue - ((formerPv + 4) * depth) / 2;
Depth singularDepth = (depth - 1 + 3 * formerPv) / 2;
ss->excludedMove = move;
value = search<NonPV>(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode);
ss->excludedMove = MOVE_NONE;
@@ -1115,20 +1155,9 @@ moves_loop: // When in check, search starts from here
// Check extension (~2 Elo)
else if ( givesCheck
&& (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move)))
&& (pos.is_discovered_check_on_king(~us, move) || pos.see_ge(move)))
extension = 1;
// Last captures extension
else if ( PieceValue[EG][pos.captured_piece()] > PawnValueEg
&& pos.non_pawn_material() <= 2 * RookValueMg)
extension = 1;
// Late irreversible move extension
if ( move == ttMove
&& pos.rule50_count() > 80
&& (captureOrPromotion || type_of(movedPiece) == PAWN))
extension = 2;
// Add extension to new depth
newDepth += extension;
@@ -1145,14 +1174,17 @@ moves_loop: // When in check, search starts from here
// Step 15. Make the move
pos.do_move(move, st, givesCheck);
// Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be
// re-searched at full depth.
// Step 16. Late moves reduction / extension (LMR, ~200 Elo)
// We use various heuristics for the sons of a node after the first son has
// been searched. In general we would like to reduce them, but there are many
// cases where we extend a son if it has good chances to be "interesting".
if ( depth >= 3
&& moveCount > 1 + 2 * rootNode
&& ( !captureOrPromotion
|| moveCountPruning
|| ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha
|| cutNode
|| (!PvNode && !formerPv && captureHistory[movedPiece][to_sq(move)][type_of(pos.captured_piece())] < 3678)
|| thisThread->ttHitAverage < 432 * TtHitAverageResolution * TtHitAverageWindow / 1024))
{
Depth r = reduction(improving, depth, moveCount);
@@ -1165,15 +1197,21 @@ moves_loop: // When in check, search starts from here
if (th.marked())
r++;
// Decrease reduction if position is or has been on the PV (~10 Elo)
if (ss->ttPv)
// Decrease reduction if position is or has been on the PV
// and node is not likely to fail low. (~10 Elo)
if ( ss->ttPv
&& !likelyFailLow)
r -= 2;
// Increase reduction at root and non-PV nodes when the best move does not change frequently
if ((rootNode || !PvNode) && depth > 10 && thisThread->bestMoveChanges <= 2)
if ( (rootNode || !PvNode)
&& thisThread->rootDepth > 10
&& thisThread->bestMoveChanges <= 2)
r++;
if (moveCountPruning && !formerPv)
// More reductions for late moves if position was not in previous PV
if ( moveCountPruning
&& !formerPv)
r++;
// Decrease reduction if opponent's move count is high (~5 Elo)
@@ -1184,7 +1222,14 @@ moves_loop: // When in check, search starts from here
if (singularQuietLMR)
r--;
if (!captureOrPromotion)
if (captureOrPromotion)
{
// Increase reduction for non-checking captures likely to be bad
if ( !givesCheck
&& ss->staticEval + PieceValue[EG][pos.captured_piece()] + 210 * depth <= alpha)
r++;
}
else
{
// Increase reduction if ttMove is a capture (~5 Elo)
if (ttCapture)
@@ -1199,7 +1244,7 @@ moves_loop: // When in check, search starts from here
// Decrease reduction for moves that escape a capture. Filter out
// castling moves, because they are coded as "king captures rook" and
// hence break make_move(). (~2 Elo)
// hence break reverse_move() (~2 Elo)
else if ( type_of(move) == NORMAL
&& !pos.see_ge(reverse_move(move)))
r -= 2 + ss->ttPv - (type_of(movedPiece) == PAWN);
@@ -1208,42 +1253,39 @@ moves_loop: // When in check, search starts from here
+ (*contHist[0])[movedPiece][to_sq(move)]
+ (*contHist[1])[movedPiece][to_sq(move)]
+ (*contHist[3])[movedPiece][to_sq(move)]
- 5287;
- 4741;
// Decrease/increase reduction by comparing opponent's stat score (~10 Elo)
if (ss->statScore >= -105 && (ss-1)->statScore < -103)
if (ss->statScore >= -89 && (ss-1)->statScore < -116)
r--;
else if ((ss-1)->statScore >= -122 && ss->statScore < -129)
else if ((ss-1)->statScore >= -112 && ss->statScore < -100)
r++;
// Decrease/increase reduction for moves with a good/bad history (~30 Elo)
r -= ss->statScore / 14884;
}
else
{
// Increase reduction for captures/promotions if late move and at low depth
if (depth < 8 && moveCount > 2)
r++;
// Unless giving check, this capture is likely bad
if ( !givesCheck
&& ss->staticEval + PieceValue[EG][pos.captured_piece()] + 210 * depth <= alpha)
r++;
// If we are not in check use statScore, but if we are in check we use
// the sum of main history and first continuation history with an offset.
if (ss->inCheck)
r -= (thisThread->mainHistory[us][from_to(move)]
+ (*contHist[0])[movedPiece][to_sq(move)] - 3833) / 16384;
else
r -= ss->statScore / 14790;
}
Depth d = std::clamp(newDepth - r, 1, newDepth);
// In general we want to cap the LMR depth search at newDepth. But if
// reductions are really negative and movecount is low, we allow this move
// to be searched deeper than the first move.
Depth d = std::clamp(newDepth - r, 1, newDepth + (r < -1 && moveCount <= 5));
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, d, true);
doFullDepthSearch = value > alpha && d != newDepth;
// If the son is reduced and fails high it will be re-searched at full depth
doFullDepthSearch = value > alpha && d < newDepth;
didLMR = true;
}
else
{
doFullDepthSearch = !PvNode || moveCount > 1;
didLMR = false;
}
@@ -1252,14 +1294,12 @@ moves_loop: // When in check, search starts from here
{
value = -search<NonPV>(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode);
// If the move passed LMR update its stats
if (didLMR && !captureOrPromotion)
{
int bonus = value > alpha ? stat_bonus(newDepth)
: -stat_bonus(newDepth);
if (move == ss->killers[0])
bonus += bonus / 4;
update_continuation_histories(ss, movedPiece, to_sq(move), bonus);
}
}
@@ -1306,8 +1346,7 @@ moves_loop: // When in check, search starts from here
rm.pv.push_back(*m);
// We record how often the best move has been changed in each
// iteration. This information is used for time management: when
// the best move changes frequently, we allocate some more time.
// iteration. This information is used for time management and LMR
if (moveCount > 1)
++thisThread->bestMoveChanges;
}
@@ -1340,6 +1379,7 @@ moves_loop: // When in check, search starts from here
}
}
// If the move is worse than some previously searched move, remember it to update its stats later
if (move != bestMove)
{
if (captureOrPromotion && captureCount < 32)
@@ -1366,9 +1406,11 @@ moves_loop: // When in check, search starts from here
assert(moveCount || !ss->inCheck || excludedMove || !MoveList<LEGAL>(pos).size());
if (!moveCount)
bestValue = excludedMove ? alpha
: ss->inCheck ? mated_in(ss->ply) : VALUE_DRAW;
bestValue = excludedMove ? alpha :
ss->inCheck ? mated_in(ss->ply)
: VALUE_DRAW;
// If there is a move which produces search value greater than alpha we update stats of searched moves
else if (bestMove)
update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq,
quietsSearched, quietCount, capturesSearched, captureCount, depth);
@@ -1390,6 +1432,7 @@ moves_loop: // When in check, search starts from here
else if (depth > 3)
ss->ttPv = ss->ttPv && (ss+1)->ttPv;
// Write gathered information in transposition table
if (!excludedMove && !(rootNode && thisThread->pvIdx))
tte->save(posKey, value_to_tt(bestValue, ss->ply), ss->ttPv,
bestValue >= beta ? BOUND_LOWER :
@@ -1485,6 +1528,8 @@ moves_loop: // When in check, search starts from here
bestValue = ttValue;
}
else
// In case of null move search use previous static eval with a different sign
// and addition of two tempos
ss->staticEval = bestValue =
(ss-1)->currentMove != MOVE_NULL ? evaluate(pos)
: -(ss-1)->staticEval + 2 * Tempo;
@@ -1492,6 +1537,7 @@ moves_loop: // When in check, search starts from here
// Stand pat. Return immediately if static value is at least beta
if (bestValue >= beta)
{
// Save gathered info in transposition table
if (!ss->ttHit)
tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER,
DEPTH_NONE, MOVE_NONE, ss->staticEval);
@@ -1528,15 +1574,13 @@ moves_loop: // When in check, search starts from here
moveCount++;
// Futility pruning
// Futility pruning and moveCount pruning
if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY
&& !givesCheck
&& futilityBase > -VALUE_KNOWN_WIN
&& !pos.advanced_pawn_push(move))
&& type_of(move) != PROMOTION)
{
assert(type_of(move) != ENPASSANT); // Due to !pos.advanced_pawn_push
// moveCount pruning
if (moveCount > 2)
continue;
@@ -1557,7 +1601,6 @@ moves_loop: // When in check, search starts from here
// Do not search moves with negative SEE values
if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY
&& !(givesCheck && pos.is_discovery_check_on_king(~pos.side_to_move(), move))
&& !pos.see_ge(move))
continue;
@@ -1620,6 +1663,7 @@ moves_loop: // When in check, search starts from here
return mated_in(ss->ply); // Plies to mate from the root
}
// Save gathered info in transposition table
tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit,
bestValue >= beta ? BOUND_LOWER :
PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER,
@@ -1698,14 +1742,15 @@ moves_loop: // When in check, search starts from here
PieceType captured = type_of(pos.piece_on(to_sq(bestMove)));
bonus1 = stat_bonus(depth + 1);
bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus
: stat_bonus(depth); // smaller bonus
bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus
: std::min(bonus1, stat_bonus(depth)); // smaller bonus
if (!pos.capture_or_promotion(bestMove))
{
// Increase stats for the best move in case it was a quiet move
update_quiet_stats(pos, ss, bestMove, bonus2, depth);
// Decrease all the non-best quiet moves
// Decrease stats for all non-best quiet moves
for (int i = 0; i < quietCount; ++i)
{
thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bonus2;
@@ -1713,14 +1758,16 @@ moves_loop: // When in check, search starts from here
}
}
else
// Increase stats for the best move in case it was a capture move
captureHistory[moved_piece][to_sq(bestMove)][captured] << bonus1;
// Extra penalty for a quiet early move that was not a TT move or main killer move in previous ply when it gets refuted
// Extra penalty for a quiet early move that was not a TT move or
// main killer move in previous ply when it gets refuted.
if ( ((ss-1)->moveCount == 1 + (ss-1)->ttHit || ((ss-1)->currentMove == (ss-1)->killers[0]))
&& !pos.captured_piece())
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -bonus1);
// Decrease all the non-best capture moves
// Decrease stats for all non-best capture moves
for (int i = 0; i < captureCount; ++i)
{
moved_piece = pos.moved_piece(capturesSearched[i]);
@@ -1737,6 +1784,7 @@ moves_loop: // When in check, search starts from here
for (int i : {1, 2, 4, 6})
{
// Only update first 2 continuation histories if we are in check
if (ss->inCheck && i > 2)
break;
if (is_ok((ss-i)->currentMove))
@@ -1749,6 +1797,7 @@ moves_loop: // When in check, search starts from here
void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus, int depth) {
// Update killers
if (ss->killers[0] != move)
{
ss->killers[1] = ss->killers[0];
@@ -1760,15 +1809,18 @@ moves_loop: // When in check, search starts from here
thisThread->mainHistory[us][from_to(move)] << bonus;
update_continuation_histories(ss, pos.moved_piece(move), to_sq(move), bonus);
// Penalty for reversed move in case of moved piece not being a pawn
if (type_of(pos.moved_piece(move)) != PAWN)
thisThread->mainHistory[us][from_to(reverse_move(move))] << -bonus;
// Update countermove history
if (is_ok((ss-1)->currentMove))
{
Square prevSq = to_sq((ss-1)->currentMove);
thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move;
}
// Update low ply history
if (depth > 11 && ss->ply < MAX_LPH)
thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 7);
}
@@ -2249,3 +2301,5 @@ namespace Search
}
}
} // namespace Stockfish
+5 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,6 +26,8 @@
#include "types.h"
#include "uci.h"
namespace Stockfish {
class Position;
namespace Search {
@@ -118,4 +120,6 @@ ValueAndPV search(Position& pos, int depth_, size_t multiPV = 1, uint64_t nodesL
}
} // namespace Stockfish
#endif // #ifndef SEARCH_H_INCLUDED
+18 -12
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -50,9 +50,11 @@
#include <windows.h>
#endif
using namespace Tablebases;
using namespace Stockfish::Tablebases;
int Tablebases::MaxCardinality = 0;
int Stockfish::Tablebases::MaxCardinality = 0;
namespace Stockfish {
namespace {
@@ -190,7 +192,8 @@ public:
std::stringstream ss(Paths);
std::string path;
while (std::getline(ss, path, SepChar)) {
while (std::getline(ss, path, SepChar))
{
fname = path + "/" + f;
std::ifstream::open(fname);
if (is_open())
@@ -565,7 +568,8 @@ int decompress_pairs(PairsData* d, uint64_t idx) {
int buf64Size = 64;
Sym sym;
while (true) {
while (true)
{
int len = 0; // This is the symbol length - d->min_sym_len
// Now get the symbol length. For any symbol s64 of length l right-padded
@@ -603,8 +607,8 @@ int decompress_pairs(PairsData* d, uint64_t idx) {
// We binary-search for our value recursively expanding into the left and
// right child symbols until we reach a leaf node where symlen[sym] + 1 == 1
// that will store the value we need.
while (d->symlen[sym]) {
while (d->symlen[sym])
{
Sym left = d->btree[sym].get<LR::Left>();
// If a symbol contains 36 sub-symbols (d->symlen[sym] + 1 = 36) and
@@ -709,7 +713,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
leadPawns = b = pos.pieces(color_of(pc), PAWN);
do
squares[size++] = pop_lsb(&b) ^ flipSquares;
squares[size++] = pop_lsb(b) ^ flipSquares;
while (b);
leadPawnsCnt = size;
@@ -729,7 +733,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
// directly map them to the correct color and square.
b = pos.pieces() ^ leadPawns;
do {
Square s = pop_lsb(&b);
Square s = pop_lsb(b);
squares[size] = s ^ flipSquares;
pieces[size++] = Piece(pos.piece_on(s) ^ flipColor);
} while (b);
@@ -1000,7 +1004,7 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) {
// so that d->lowestSym[i] >= d->lowestSym[i+1] (when read as LittleEndian).
// Starting from this we compute a base64[] table indexed by symbol length
// and containing 64 bit values so that d->base64[i] >= d->base64[i+1].
// See http://www.eecs.harvard.edu/~michaelm/E210/huffman.pdf
// See https://en.wikipedia.org/wiki/Huffman_coding
for (int i = d->base64.size() - 2; i >= 0; --i) {
d->base64[i] = (d->base64[i + 1] + number<Sym, LittleEndian>(&d->lowestSym[i])
- number<Sym, LittleEndian>(&d->lowestSym[i + 1])) / 2;
@@ -1141,7 +1145,7 @@ void* mapped(TBTable<Type>& e, const Position& pos) {
if (e.ready.load(std::memory_order_acquire))
return e.baseAddress; // Could be nullptr if file does not exist
std::unique_lock<std::mutex> lk(mutex);
std::scoped_lock<std::mutex> lk(mutex);
if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock
return e.baseAddress;
@@ -1440,7 +1444,7 @@ WDLScore Tablebases::probe_wdl(Position& pos, ProbeState* result) {
// If n = 100 immediately after a capture or pawn move, then the position
// is also certainly a win, and during the whole phase until the next
// capture or pawn move, the inequality to be preserved is
// dtz + 50-movecounter <= 100.
// dtz + 50-move-counter <= 100.
//
// In short, if a move is available resulting in dtz + 50-move-counter <= 99,
// then do not accept moves leading to dtz + 50-move-counter == 100.
@@ -1610,3 +1614,5 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) {
return true;
}
} // namespace Stockfish
+3 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@
#include "../search.h"
namespace Tablebases {
namespace Stockfish::Tablebases {
enum WDLScore {
WDLLoss = -2, // Loss
@@ -73,6 +73,6 @@ inline std::ostream& operator<<(std::ostream& os, const ProbeState v) {
return os;
}
}
} // namespace Stockfish::Tablebases
#endif
+9 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,6 +26,8 @@
#include "syzygy/tbprobe.h"
#include "tt.h"
namespace Stockfish {
ThreadPool Threads; // Global object
@@ -151,14 +153,16 @@ void Thread::idle_loop() {
void ThreadPool::set(size_t requested) {
if (size() > 0) { // destroy any existing thread(s)
if (size() > 0) // destroy any existing thread(s)
{
main()->wait_for_search_finished();
while (size() > 0)
delete back(), pop_back();
}
if (requested > 0) { // create new thread(s)
if (requested > 0) // create new thread(s)
{
push_back(new MainThread(0));
while (size() < requested)
@@ -294,3 +298,5 @@ void ThreadPool::wait_for_workers_finished() const {
for (Thread* th : *this)
th->wait_for_worker_finished();
}
} // namespace Stockfish
+4 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,6 +33,7 @@
#include "search.h"
#include "thread_win32_osx.h"
namespace Stockfish {
/// Thread class keeps together all the thread-related stuff. We use
/// per-thread pawn and material hash tables so that once we get a
@@ -216,4 +217,6 @@ private:
extern ThreadPool Threads;
} // namespace Stockfish
#endif // #ifndef THREAD_H_INCLUDED
+9 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -31,6 +31,8 @@
#include <pthread.h>
namespace Stockfish {
static const size_t TH_STACK_SIZE = 8 * 1024 * 1024;
template <class T, class P = std::pair<T*, void(T::*)()>>
@@ -57,10 +59,16 @@ public:
void join() { pthread_join(thread, NULL); }
};
} // namespace Stockfish
#else // Default case: use STL classes
namespace Stockfish {
typedef std::thread NativeThread;
} // namespace Stockfish
#endif
#endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED
+5 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,6 +24,8 @@
#include "timeman.h"
#include "uci.h"
namespace Stockfish {
TimeManagement Time; // Our global time management object
@@ -95,3 +97,5 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
if (Options["Ponder"])
optimumTime += optimumTime / 4;
}
} // namespace Stockfish
+5 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,6 +23,8 @@
#include "search.h"
#include "thread.h"
namespace Stockfish {
/// The TimeManagement class computes the optimal time to think depending on
/// the maximum available time, the game move number and other parameters.
@@ -44,4 +46,6 @@ private:
extern TimeManagement Time;
} // namespace Stockfish
#endif // #ifndef TIMEMAN_H_INCLUDED
+13 -8
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,6 +26,8 @@
#include "tt.h"
#include "uci.h"
namespace Stockfish {
TranspositionTable TT; // Our global transposition table
bool TranspositionTable::enable_transposition_table = true;
@@ -133,7 +135,7 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
for (int i = 0; i < ClusterSize; ++i)
if (tte[i].key16 == key16 || !tte[i].depth8)
{
tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & 0x7)); // Refresh
tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & (GENERATION_DELTA - 1))); // Refresh
return found = (bool)tte[i].depth8, &tte[i];
}
@@ -142,11 +144,12 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
TTEntry* replace = tte;
for (int i = 1; i < ClusterSize; ++i)
// Due to our packed storage format for generation and its cyclic
// nature we add 263 (256 is the modulus plus 7 to keep the unrelated
// lowest three bits from affecting the result) to calculate the entry
// age correctly even after generation8 overflows into the next cycle.
if ( replace->depth8 - ((263 + generation8 - replace->genBound8) & 0xF8)
> tte[i].depth8 - ((263 + generation8 - tte[i].genBound8) & 0xF8))
// nature we add GENERATION_CYCLE (256 is the modulus, plus what
// is needed to keep the unrelated lowest n bits from affecting
// the result) to calculate the entry age correctly even after
// generation8 overflows into the next cycle.
if ( replace->depth8 - ((GENERATION_CYCLE + generation8 - replace->genBound8) & GENERATION_MASK)
> tte[i].depth8 - ((GENERATION_CYCLE + generation8 - tte[i].genBound8) & GENERATION_MASK))
replace = &tte[i];
return found = false, replace;
@@ -161,7 +164,9 @@ int TranspositionTable::hashfull() const {
int cnt = 0;
for (int i = 0; i < 1000; ++i)
for (int j = 0; j < ClusterSize; ++j)
cnt += table[i].entry[j].depth8 && (table[i].entry[j].genBound8 & 0xF8) == generation8;
cnt += table[i].entry[j].depth8 && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8;
return cnt / ClusterSize;
}
} // namespace Stockfish
+12 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -22,6 +22,8 @@
#include "misc.h"
#include "types.h"
namespace Stockfish {
/// TTEntry struct is the 10 bytes transposition table entry, defined as below:
///
/// key 16 bit
@@ -72,9 +74,15 @@ class TranspositionTable {
static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size");
// Constants used to refresh the hash table periodically
static constexpr unsigned GENERATION_BITS = 3; // nb of bits reserved for other things
static constexpr int GENERATION_DELTA = (1 << GENERATION_BITS); // increment for generation field
static constexpr int GENERATION_CYCLE = 255 + (1 << GENERATION_BITS); // cycle length
static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF; // mask to pull out generation number
public:
~TranspositionTable() { aligned_large_pages_free(table); }
void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound
void new_search() { generation8 += GENERATION_DELTA; } // Lower bits are used for other things
TTEntry* probe(const Key key, bool& found) const;
int hashfull() const;
void resize(size_t mbSize);
@@ -96,4 +104,6 @@ private:
extern TranspositionTable TT;
} // namespace Stockfish
#endif // #ifndef TT_H_INCLUDED
+9 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,6 +26,8 @@
using std::string;
namespace Stockfish {
bool Tune::update_on_last;
const UCI::Option* LastOption = nullptr;
BoolConditions Conditions;
@@ -126,6 +128,8 @@ void BoolConditions::set() {
sync_cout << binary[i] << sync_endl;
}
} // namespace Stockfish
// 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.
@@ -138,7 +142,11 @@ void BoolConditions::set() {
#include <cmath>
namespace Stockfish {
void Tune::read_results() {
/* ...insert your values here... */
}
} // namespace Stockfish
+8 -4
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,6 +24,8 @@
#include <type_traits>
#include <vector>
namespace Stockfish {
typedef std::pair<int, int> Range; // Option's min-max values
typedef Range (RangeFun) (int);
@@ -130,9 +132,9 @@ class Tune {
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.
// Our facility 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; }
@@ -190,4 +192,6 @@ public:
#define TUNE_CONDITIONS() int UNIQUE(c, __LINE__) = (Conditions.init(__COUNTER__), 0); \
TUNE(Conditions, set_conditions)
} // namespace Stockfish
#endif // #ifndef TUNE_H_INCLUDED
+7 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -83,6 +83,8 @@
# define pext(b, m) 0
#endif
namespace Stockfish {
#ifdef USE_POPCNT
constexpr bool HasPopCnt = true;
#else
@@ -113,7 +115,7 @@ constexpr int MAX_PLY = 246;
/// bit 6-11: origin square (from 0 to 63)
/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
/// bit 14-15: special move flag: promotion (1), en passant (2), castling (3)
/// NOTE: EN-PASSANT bit is set only when a pawn can be captured
/// NOTE: en passant bit is set only when a pawn can be captured
///
/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in
/// any normal move destination square is always different from origin square
@@ -127,7 +129,7 @@ enum Move : int {
enum MoveType {
NORMAL,
PROMOTION = 1 << 14,
ENPASSANT = 2 << 14,
EN_PASSANT = 2 << 14,
CASTLING = 3 << 14
};
@@ -489,6 +491,8 @@ constexpr Key make_key(uint64_t seed) {
return seed * 6364136223846793005ULL + 1442695040888963407ULL;
}
} // namespace Stockfish
#endif // #ifndef TYPES_H_INCLUDED
#include "tune.h" // Global visibility to tuning setup
+6 -3
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -41,6 +41,8 @@
using namespace std;
namespace Stockfish {
extern vector<string> setup_bench(const Position&, istream&);
// FEN string of the initial position, normal chess
@@ -346,8 +348,7 @@ void UCI::loop(int argc, char* argv[]) {
std::cout << th.thread_idx() << '\n';
});
}
else
else if (!token.empty() && token[0] != '#')
sync_cout << "Unknown command: " << cmd << sync_endl;
} while (token != "quit" && argc == 1); // Command line args are one-shot
@@ -441,3 +442,5 @@ Move UCI::to_move(const Position& pos, string& str) {
return MOVE_NONE;
}
} // namespace Stockfish
+4 -2
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,6 +24,8 @@
#include "types.h"
namespace Stockfish {
class Position;
namespace UCI {
@@ -81,6 +83,6 @@ void setoption(const std::string& name, const std::string& value);
extern UCI::OptionsMap Options;
extern const char* StartFEN;
} // namespace Stockfish
#endif // #ifndef UCI_H_INCLUDED
+5 -1
View File
@@ -1,6 +1,6 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -32,6 +32,8 @@
using std::string;
namespace Stockfish {
UCI::OptionsMap Options; // Global object
namespace UCI {
@@ -211,3 +213,5 @@ Option& Option::operator=(const string& v) {
}
} // namespace UCI
} // namespace Stockfish
+9 -9
View File
@@ -51,16 +51,16 @@ case $1 in
tt_size=128
cat << EOF > tsan.supp
race:TTEntry::move
race:TTEntry::depth
race:TTEntry::bound
race:TTEntry::save
race:TTEntry::value
race:TTEntry::eval
race:TTEntry::is_pv
race:Stockfish::TTEntry::move
race:Stockfish::TTEntry::depth
race:Stockfish::TTEntry::bound
race:Stockfish::TTEntry::save
race:Stockfish::TTEntry::value
race:Stockfish::TTEntry::eval
race:Stockfish::TTEntry::is_pv
race:TranspositionTable::probe
race:TranspositionTable::hashfull
race:Stockfish::TranspositionTable::probe
race:Stockfish::TranspositionTable::hashfull
EOF