diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..8981efca
--- /dev/null
+++ b/.gitignore
@@ -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
+
diff --git a/AUTHORS b/AUTHORS
index b31a36e9..c12b98a0 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -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)
diff --git a/README.md b/README.md
index fe6ce27c..10d43595 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,32 @@
-
-
-
-
-Stockfish NNUE
-
## Overview
+[](https://travis-ci.org/official-stockfish/Stockfish)
+[](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master)
+
+[Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine
+derived from Glaurung 2.1. 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.
diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt
index 482e9000..f5347ea1 100644
--- a/Top CPU Contributors.txt
+++ b/Top CPU Contributors.txt
@@ -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
diff --git a/src/Makefile b/src/Makefile
index 33a2434d..86d00d95 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -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
diff --git a/src/benchmark.cpp b/src/benchmark.cpp
index ffb631a2..7945a453 100644
--- a/src/benchmark.cpp
+++ b/src/benchmark.cpp
@@ -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 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 setup_bench(const Position& current, istream& is) {
return list;
}
+
+} // namespace Stockfish
diff --git a/src/bitbase.cpp b/src/bitbase.cpp
index bbe8e9a7..27bf4095 100644
--- a/src/bitbase.cpp
+++ b/src/bitbase.cpp
@@ -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(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
diff --git a/src/bitboard.cpp b/src/bitboard.cpp
index 80206b58..6b84b51e 100644
--- a/src/bitboard.cpp
+++ b/src/bitboard.cpp
@@ -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
diff --git a/src/bitboard.h b/src/bitboard.h
index 29d8f66d..b29f3e24 100644
--- a/src/bitboard.h
+++ b/src/bitboard.h
@@ -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
diff --git a/src/endgame.cpp b/src/endgame.cpp
index c8be2198..a44d3a1c 100644
--- a/src/endgame.cpp
+++ b/src/endgame.cpp
@@ -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::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 2));
assert(verify_material(pos, weakSide, RookValueMg, 1));
- Square strongPawn1 = pos.squares(strongSide)[0];
- Square strongPawn2 = pos.squares(strongSide)[1];
+ Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN));
+ Square strongPawn2 = msb(pos.pieces(strongSide, PAWN));
Square weakKing = pos.square(weakSide);
// Does the stronger side have a passed pawn?
@@ -638,8 +640,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const {
return SCALE_FACTOR_NONE;
Square weakKing = pos.square(weakSide);
- Square strongPawn1 = pos.squares(strongSide)[0];
- Square strongPawn2 = pos.squares(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::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
diff --git a/src/endgame.h b/src/endgame.h
index 1351d88a..146111b9 100644
--- a/src/endgame.h
+++ b/src/endgame.h
@@ -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
diff --git a/src/evaluate.cpp b/src/evaluate.cpp
index 709a50ff..e4558730 100644
--- a/src/evaluate.cpp
+++ b/src/evaluate.cpp
@@ -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(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(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(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(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(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(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(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(behind);
behind |= shift(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(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::winnable(Score score) const {
int outflanking = distance(pos.square(WHITE), pos.square(BLACK))
- - distance(pos.square(WHITE), pos.square(BLACK));
+ + int(rank_of(pos.square(WHITE)) - rank_of(pos.square(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(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(strongSide) - pos.count(~strongSide) <= 1
&& bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN))
&& (attacks_bb(pos.square(~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() == 1)
sf = 37 + 3 * (pos.count(WHITE) == 1 ? pos.count(BLACK) + pos.count(BLACK)
: pos.count(WHITE) + pos.count(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(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();
- return NNUE::evaluate(pos) * (720 + mat / 32) / 1024 + Tempo;
+ auto adjusted_NNUE = [&]()
+ {
+ int material = pos.non_pawn_material() + 4 * PawnValueMg * pos.count();
+ 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() < 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() < 2);
- v = classical || strongClassical ? Evaluation(pos).value() : adjusted_NNUE();
+ v = classical || lowPieceEndgame ? Evaluation(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
diff --git a/src/evaluate.h b/src/evaluate.h
index f5d3efa7..f3766f45 100644
--- a/src/evaluate.h
+++ b/src/evaluate.h
@@ -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
diff --git a/src/main.cpp b/src/main.cpp
index 1a13dc62..ff6564a6 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -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[]) {
diff --git a/src/material.cpp b/src/material.cpp
index 870a5e11..9d17af20 100644
--- a/src/material.cpp
+++ b/src/material.cpp
@@ -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 EvaluateKXK[] = { Endgame(WHITE), Endgame(BLACK) };
@@ -67,7 +74,7 @@ namespace {
bool is_KBPsK(const Position& pos, Color us) {
return pos.non_pawn_material(us) == BishopValueMg
- && pos.count(us) >= 1;
+ && pos.count(us) >= 1;
}
bool is_KQKRPs(const Position& pos, Color us) {
@@ -82,11 +89,11 @@ namespace {
/// piece type for both colors.
template
- 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(BLACK) > 1, pos.count(BLACK), pos.count(BLACK),
pos.count(BLACK) , pos.count(BLACK), pos.count(BLACK) } };
- e->value = int16_t((imbalance(pieceCount) - imbalance(pieceCount)) / 16);
+ e->score = (imbalance(pieceCount) - imbalance(pieceCount)) / 16;
return e;
}
} // namespace Material
+
+} // namespace Stockfish
diff --git a/src/material.h b/src/material.h
index 80d01655..26535a53 100644
--- a/src/material.h
+++ b/src/material.h
@@ -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* evaluationFunction;
const EndgameBase* 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 Table;
Entry* probe(const Position& pos);
-} // namespace Material
+} // namespace Stockfish::Material
#endif // #ifndef MATERIAL_H_INCLUDED
diff --git a/src/misc.cpp b/src/misc.cpp
index eb68e842..e981136b 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -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 ", 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
diff --git a/src/misc.h b/src/misc.h
index c7cf3265..6eb8b1ae 100644
--- a/src/misc.h
+++ b/src/misc.h
@@ -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
diff --git a/src/movegen.cpp b/src/movegen.cpp
index 3340f65c..50496136 100644
--- a/src/movegen.cpp
+++ b/src/movegen.cpp
@@ -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
@@ -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(pawnsOn7) & emptySquares;
while (b1)
- moveList = make_promotions(moveList, pop_lsb(&b1), ksq);
+ moveList = make_promotions(moveList, pop_lsb(b1), ksq);
while (b2)
- moveList = make_promotions(moveList, pop_lsb(&b2), ksq);
+ moveList = make_promotions(moveList, pop_lsb(b2), ksq);
while (b3)
- moveList = make_promotions(moveList, pop_lsb(&b3), ksq);
+ moveList = make_promotions(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(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(pop_lsb(&b1), pos.ep_square());
+ *moveList++ = make(pop_lsb(b1), pos.ep_square());
}
}
@@ -175,32 +175,23 @@ namespace {
}
- template
- ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
+ template
+ 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(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(from) & target & pos.check_squares(Pt)))
- continue;
-
- if (pos.blockers_for_king(~Us) & from)
- continue;
- }
+ Square from = pop_lsb(bb);
Bitboard b = attacks_bb(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
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(Us), checksq) | checksq;
+ target = between_bb(pos.square(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(pos, moveList, target);
- moveList = generate_moves(pos, moveList, target);
- moveList = generate_moves(pos, moveList, target);
- moveList = generate_moves(pos, moveList, target);
- moveList = generate_moves(pos, moveList, target);
+ moveList = generate_moves(pos, moveList, piecesToMove, target);
+ moveList = generate_moves(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(Us);
Bitboard b = attacks_bb(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 {
/// Generates all pseudo-legal captures plus queen and checking knight promotions
-/// Generates all pseudo-legal non-captures and underpromotions(except checking knight)
+/// Generates all pseudo-legal non-captures and underpromotions (except checking knight)
/// 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(const Position&, ExtMove*);
template ExtMove* generate(const Position&, ExtMove*);
-/// generate generates all pseudo-legal non-captures.
-/// Returns a pointer to the end of the move list.
+/// generate generates all pseudo-legal non-captures giving check,
+/// except castling. Returns a pointer to the end of the move list.
template<>
ExtMove* generate(const Position& pos, ExtMove* moveList) {
@@ -295,7 +287,7 @@ ExtMove* generate(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(const Position& pos, ExtMove* moveList) {
b &= ~attacks_bb(pos.square(~us));
while (b)
- *moveList++ = make_move(from, pop_lsb(&b));
+ *moveList++ = make_move(from, pop_lsb(b));
}
return us == WHITE ? generate_all(pos, moveList)
@@ -321,24 +313,16 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) {
Color us = pos.side_to_move();
Square ksq = pos.square(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(ksq) & ~pos.pieces(us) & ~sliderAttacks;
+ // Generate evasions for king
+ Bitboard b = attacks_bb(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(pos, moveList)
: generate_all(pos, moveList);
}
@@ -357,7 +341,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) {
moveList = pos.checkers() ? generate(pos, moveList)
: generate(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(const Position& pos, ExtMove* moveList) {
return moveList;
}
+
+} // namespace Stockfish
diff --git a/src/movegen.h b/src/movegen.h
index 675b7698..704cd3e2 100644
--- a/src/movegen.h
+++ b/src/movegen.h
@@ -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
diff --git a/src/movepick.cpp b/src/movepick.cpp
index f5e02385..4ff4cff4 100644
--- a/src/movepick.cpp
+++ b/src/movepick.cpp
@@ -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
diff --git a/src/movepick.h b/src/movepick.h
index 4c0ad551..c76d4957 100644
--- a/src/movepick.h
+++ b/src/movepick.h
@@ -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 ButterflyHistory;
+typedef Stats 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 PieceToHistory;
typedef Stats 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
diff --git a/src/nnue/architectures/halfkp_256x2-32-32.h b/src/nnue/architectures/halfkp_256x2-32-32.h
index 333feb83..21308368 100644
--- a/src/nnue/architectures/halfkp_256x2-32-32.h
+++ b/src/nnue/architectures/halfkp_256x2-32-32.h
@@ -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
diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp
index 569df292..bbc6ebc2 100644
--- a/src/nnue/evaluate_nnue.cpp
+++ b/src/nnue/evaluate_nnue.cpp
@@ -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 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
diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h
index a1051abe..b6070fae 100644
--- a/src/nnue/evaluate_nnue.h
+++ b/src/nnue/evaluate_nnue.h
@@ -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
-namespace Eval::NNUE {
+namespace Stockfish::Eval::NNUE {
enum struct UseNNUEMode
{
@@ -63,48 +63,6 @@ namespace Eval::NNUE {
template
using LargePagePtr = std::unique_ptr>;
- // Input feature converter
- extern LargePagePtr feature_transformer;
-
- // Evaluation function
- extern AlignedPtr 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
diff --git a/src/nnue/features/feature_set.h b/src/nnue/features/feature_set.h
index 6602eae7..c293661b 100644
--- a/src/nnue/features/feature_set.h
+++ b/src/nnue/features/feature_set.h
@@ -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
-namespace Eval::NNUE::Features {
+namespace Stockfish::Eval::NNUE::Features {
// Class template that represents a list of values
template
@@ -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
diff --git a/src/nnue/features/features_common.h b/src/nnue/features/features_common.h
index 656502a3..0e2c0a84 100644
--- a/src/nnue/features/features_common.h
+++ b/src/nnue/features/features_common.h
@@ -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
diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp
index 743a6378..612b42bd 100644
--- a/src/nnue/features/half_kp.cpp
+++ b/src/nnue/features/half_kp.cpp
@@ -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
+ void HalfKP::AppendActiveIndices(
+ const Position& pos, Color perspective, IndexList* active) {
+
+ Square ksq = orient(perspective, pos.square(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
+ void HalfKP::AppendChangedIndices(
+ const Position& pos, const DirtyPiece& dp, Color perspective,
+ IndexList* removed, IndexList* added) {
+
+ Square ksq = orient(perspective, pos.square(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
- inline IndexType HalfKP::make_index(
- Color perspective,
- Square s,
- Piece pc,
- Square ksq) {
+ template class HalfKP;
- return IndexType(orient(perspective, s) + kpp_board_index[pc][perspective] + PS_END * ksq);
- }
-
- // Get a list of indices for active features
- template
- void HalfKP::append_active_indices(
- const Position& pos,
- Color perspective,
- IndexList* active) {
-
- Square ksq = orient(
- perspective,
- pos.square(
- 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
- void HalfKP::append_changed_indices(
- const Position& pos,
- Color perspective,
- IndexList* removed,
- IndexList* added) {
-
- Square ksq = orient(
- perspective,
- pos.square(
- 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;
- template class HalfKP;
-
-} // namespace Eval::NNUE::Features
+} // namespace Stockfish::Eval::NNUE::Features
diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_kp.h
index 4a4329e8..3c94778d 100644
--- a/src/nnue/features/half_kp.h
+++ b/src/nnue/features/half_kp.h
@@ -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
+ class HalfKP {
- // Feature HalfKP: Combination of the position of own king
- // and the position of pieces other than kings
- template
- 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(SQUARE_NB) * static_cast(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(SQUARE_NB) * static_cast(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
diff --git a/src/nnue/features/index_list.h b/src/nnue/features/index_list.h
index d9ad680a..9f03993b 100644
--- a/src/nnue/features/index_list.h
+++ b/src/nnue/features/index_list.h
@@ -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
@@ -59,6 +59,6 @@ namespace Eval::NNUE::Features {
: public ValueList {
};
-} // namespace Eval::NNUE::Features
+} // namespace Stockfish::Eval::NNUE::Features
#endif // NNUE_FEATURES_INDEX_LIST_H_INCLUDED
diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h
index 1f2ff7c5..b6fb5928 100644
--- a/src/nnue/layers/affine_transform.h
+++ b/src/nnue/layers/affine_transform.h
@@ -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
#include "../nnue_common.h"
-#include
-#include
-#include
-
-namespace Eval::NNUE::Layers {
+namespace Stockfish::Eval::NNUE::Layers {
// Affine transformation layer
template
@@ -45,6 +41,11 @@ namespace Eval::NNUE::Layers {
static constexpr IndexType kOutputDimensions = OutputDimensions;
static constexpr IndexType kPaddedInputDimensions =
CeilToMultiple(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(stream);
for (std::size_t i = 0; i < kOutputDimensions * kPaddedInputDimensions; ++i)
+#if !defined (USE_SSSE3)
weights_[i] = read_little_endian(stream);
+#else
+ weights_[
+ (i / 4) % (kPaddedInputDimensions / 4) * kOutputDimensions * 4 +
+ i / kPaddedInputDimensions * 4 +
+ i % 4
+ ] = read_little_endian(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(buffer);
+ const auto input_vector = reinterpret_cast(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(input);
- [[maybe_unused]] const auto input_vector256 = reinterpret_cast(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(&biases_[i]);
- __m512i* outptr = reinterpret_cast<__m512i*>(&output[i]);
+ const auto input32 = reinterpret_cast(input);
+ vec_t* outptr = reinterpret_cast(output);
+ std::memcpy(output, biases_, kOutputDimensions * sizeof(OutputType));
- const auto row01a = *reinterpret_cast(&weights_[offset01a]);
- const auto row23a = *reinterpret_cast(&weights_[offset23a]);
- const auto row45a = *reinterpret_cast(&weights_[offset45a]);
- const auto row67a = *reinterpret_cast(&weights_[offset67a]);
- const auto row01b = *reinterpret_cast(&weights_[offset01b]);
- const auto row23b = *reinterpret_cast(&weights_[offset23b]);
- const auto row45b = *reinterpret_cast(&weights_[offset45b]);
- const auto row67b = *reinterpret_cast(&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(&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(&weights_[offset0]);
- const auto row1 = reinterpret_cast(&weights_[offset1]);
- const auto row2 = reinterpret_cast(&weights_[offset2]);
- const auto row3 = reinterpret_cast(&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(&weights_[(i + 0) * kOutputDimensions * 4]);
+ const auto col1 = reinterpret_cast(&weights_[(i + 1) * kOutputDimensions * 4]);
+ const auto col2 = reinterpret_cast(&weights_[(i + 2) * kOutputDimensions * 4]);
+ const auto col3 = reinterpret_cast(&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(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(&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(&weights_[offset0]);
- const auto row1 = reinterpret_cast(&weights_[offset1]);
- const auto row2 = reinterpret_cast(&weights_[offset2]);
- const auto row3 = reinterpret_cast(&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(&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(&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(&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(buffer);
- const auto input_vector = reinterpret_cast(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(&biases_[i]);
- __m128i* outptr = reinterpret_cast<__m128i*>(&output[i]);
-
- const auto row0 = reinterpret_cast(&weights_[offset0]);
- const auto row1 = reinterpret_cast(&weights_[offset1]);
- const auto row2 = reinterpret_cast(&weights_[offset2]);
- const auto row3 = reinterpret_cast(&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(&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(buffer);
- const auto input_vector = reinterpret_cast(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(&biases_[i]);
- __m128i* outptr = reinterpret_cast<__m128i*>(&output[i]);
-
- const auto row0 = reinterpret_cast(&weights_[offset0]);
- const auto row1 = reinterpret_cast(&weights_[offset1]);
- const auto row2 = reinterpret_cast(&weights_[offset2]);
- const auto row3 = reinterpret_cast(&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(&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(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
diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h
index 3e9ce655..7efb0a0b 100644
--- a/src/nnue/layers/clipped_relu.h
+++ b/src/nnue/layers/clipped_relu.h
@@ -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
-#include
-#include
-
-namespace Eval::NNUE::Layers {
+namespace Stockfish::Eval::NNUE::Layers {
// Clipped ReLU
template
@@ -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
diff --git a/src/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h
index 7a4ef045..f7dab19e 100644
--- a/src/nnue/layers/input_slice.h
+++ b/src/nnue/layers/input_slice.h
@@ -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
@@ -89,6 +89,6 @@ class InputSlice {
private:
};
-} // namespace Layers
+} // namespace Stockfish::Eval::NNUE::Layers
#endif // #ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED
diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h
index 3d2f5bb4..e84e26f1 100644
--- a/src/nnue/nnue_accumulator.h
+++ b/src/nnue/nnue_accumulator.h
@@ -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
diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h
index 91cdc4bd..1680368e 100644
--- a/src/nnue/nnue_architecture.h
+++ b/src/nnue/nnue_architecture.h
@@ -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
diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h
index e72168f8..1479d6b3 100644
--- a/src/nnue/nnue_common.h
+++ b/src/nnue/nnue_common.h
@@ -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
#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
diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h
index 8c17c959..3082c1df 100644
--- a/src/nnue/nnue_feature_transformer.h
+++ b/src/nnue/nnue_feature_transformer.h
@@ -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
#include
-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
diff --git a/src/pawns.cpp b/src/pawns.cpp
index 68aaf331..70fb6f23 100644
--- a/src/pawns.cpp
+++ b/src/pawns.cpp
@@ -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(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(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(theirPawns | pawn_attacks_bb(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(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(const Position& pos);
template Score Entry::do_king_safety(const Position& pos);
} // namespace Pawns
+
+} // namespace Stockfish
diff --git a/src/pawns.h b/src/pawns.h
index 5499826e..124619d6 100644
--- a/src/pawns.h
+++ b/src/pawns.h
@@ -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 Table;
Entry* probe(const Position& pos);
-} // namespace Pawns
+} // namespace Stockfish::Pawns
#endif // #ifndef PAWNS_H_INCLUDED
diff --git a/src/position.cpp b/src/position.cpp
index 2fb5f4f7..57bc7c7b 100644
--- a/src/position.cpp
+++ b/src/position.cpp
@@ -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(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(~sideToMove)) & pieces(sideToMove);
+ st->previous->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square(WHITE), st->previous->pinners[BLACK]);
+ st->previous->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(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(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(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(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(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(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(us));
+ return !(blockers_for_king(us) & from)
+ || aligned(from, to, square(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().
if (type_of(m) != NORMAL)
- return MoveList(*this).contains(m);
+ return checkers() ? MoveList< EVASIONS>(*this).contains(m)
+ : MoveList(*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(us)) | checkers()) & to))
+ // Our move must be a blocking interposition or a capture of the checking piece
+ if (!(between_bb(square(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(~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(~sideToMove)) == rank_of(from)
+ && st->previous->blockersForKing[~sideToMove] & from);
- return (attacks_bb< ROOK>(square(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK))
- | (attacks_bb(square