Compare commits

...

381 Commits

Author SHA1 Message Date
Joost VandeVondele b4ac3d6b96 Merge pull request #5161 from Disservin/cluster
Merge SF master in the cluster branch
2024-04-10 22:18:16 +02:00
Disservin 6c19bec86e Merge branch 'master' into cluster 2024-04-10 18:46:26 +02:00
Viren6 0716b845fd Update NNUE architecture to SFNNv9 and net nn-ae6a388e4a1a.nnue
Part 1: PyTorch Training, linrock

Trained with a 10-stage sequence from scratch, starting in May 2023:
https://github.com/linrock/nnue-tools/blob/master/exp-sequences/3072-10stage-SFNNv9.yml

While the training methods were similar to the L1-2560 training sequence,
the last two stages introduced min-v2 binpacks,
where bestmove capture and in-check position scores were not zeroed during minimization,
for compatibility with skipping SEE >= 0 positions and future research.

Training data can be found at:
https://robotmoon.com/nnue-training-data

This net was tested at epoch 679 of the 10th training stage:
https://tests.stockfishchess.org/tests/view/65f32e460ec64f0526c48dbc

Part 2: SPSA Training, Viren6

The net was then SPSA tuned.
This consisted of the output weights (32 * 8) and biases (8)
as well as the L3 biases (32 * 8) and L2 biases (16 * 8), totalling 648 params in total.

The SPSA tune can be found here:
https://tests.stockfishchess.org/tests/view/65fc33ba0ec64f0526c512e3

With the help of Disservin , the initial weights were extracted with:
https://github.com/Viren6/Stockfish/tree/new228

The net was saved with the tuned weights using:
https://github.com/Viren6/Stockfish/tree/new241

Earlier nets of the SPSA failed STC compared to the base 3072 net of part 1:
https://tests.stockfishchess.org/tests/view/65ff356e0ec64f0526c53c98
Therefore it is suspected that the SPSA at VVLTC has
added extra scaling on top of the scaling of increasing the L1 size.

Passed VVLTC 1:
https://tests.stockfishchess.org/tests/view/6604a9020ec64f0526c583da
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 53042 W: 13554 L: 13256 D: 26232
Ptnml(0-2): 12, 5147, 15903, 5449, 10

Passed VVLTC 2:
https://tests.stockfishchess.org/tests/view/660ad1b60ec64f0526c5dd23
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 17506 W: 4574 L: 4315 D: 8617
Ptnml(0-2): 1, 1567, 5362, 1818, 5

STC Elo estimate:
https://tests.stockfishchess.org/tests/view/660b834d01aaec5069f87cb0
Elo: -7.66 ± 3.8 (95%) LOS: 0.0%
Total: 9618 W: 2440 L: 2652 D: 4526
Ptnml(0-2): 80, 1281, 2261, 1145, 42
nElo: -13.94 ± 6.9 (95%) PairsRatio: 0.87

closes https://tests.stockfishchess.org/tests/view/660b834d01aaec5069f87cb0

bench 1823302

Co-Authored-By: Linmiao Xu <lin@robotmoon.com>
2024-04-02 08:49:48 +02:00
Joost VandeVondele c964942da2 Avoid a note related to an ABI change
current master triggers a gcc note:
parameter passing for argument of type 'std::pair<double, double>' when C++17 is enabled changed to match C++14 in GCC 10.1

while this is inconsequential, and just informative  https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111516 we can easily avoid it.

closes https://github.com/official-stockfish/Stockfish/pull/5145

No functional change
2024-03-30 12:38:02 +01:00
Disservin ec598b380d Improve prerelease creation workflow
In the last couple of months we sometimes saw duplicated prereleases uploaded to GitHub, possibly due to some racy behavior when concurrent jobs create a prerelease. This now creates an empty prerelease at the beginning of the CI and the binaries are later just attached to this one.

closes https://github.com/official-stockfish/Stockfish/pull/5144

No functional change
2024-03-29 13:13:07 +01:00
Joost VandeVondele 68d58d94da Fix usage of abs vs std::abs
close https://github.com/official-stockfish/Stockfish/pull/5143

No functional change
2024-03-29 13:12:20 +01:00
Shawn Xu e13e4cfb83 Simplify NMP Condition
Remove eval >= ss->staticEval condition for Null Move Pruning.

Passed non-regression STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 44000 W: 11420 L: 11202 D: 21378
Ptnml(0-2): 174, 5243, 10978, 5401, 204
https://tests.stockfishchess.org/tests/live_elo/6603ee490ec64f0526c57984

Passed non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 82956 W: 20978 L: 20818 D: 41160
Ptnml(0-2): 54, 9353, 22499, 9523, 49
https://tests.stockfishchess.org/tests/live_elo/660464b50ec64f0526c5804d

closes https://github.com/official-stockfish/Stockfish/pull/5142

Bench 1759189
2024-03-29 10:18:40 +01:00
Michael Chaly 0ef5d05102 Adjust best value after a pruned quiet move
Logic somewhat similar to how we adjust best value after pruned captures
in qsearch, but in search this patch does it after pruned quiet moves
and also to not full scale of futility value but to smth in between
best value and futility value.

Passed STC:
https://tests.stockfishchess.org/tests/view/6601cf900ec64f0526c55c30
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 59936 W: 15722 L: 15369 D: 28845
Ptnml(0-2): 182, 7097, 15112, 7340, 237

Passed LTC:
https://tests.stockfishchess.org/tests/view/66029b2d0ec64f0526c566f1
LLR: 2.96 (-2.94,2.94) <0.50,2.50>
Total: 118362 W: 29953 L: 29460 D: 58949
Ptnml(0-2): 68, 13159, 32249, 13622, 83

closes https://github.com/official-stockfish/Stockfish/pull/5141

bench 1772608
2024-03-29 10:15:17 +01:00
xoto10 e636f73ab8 Vary time use with eval
Adjust time use depending on the current eval.

Passed STC :
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 101696 W: 26651 L: 26238 D: 48807
Ptnml(0-2): 400, 11602, 26459, 11959, 428
https://tests.stockfishchess.org/tests/live_elo/660187a50ec64f0526c557f6

Passed LTC :
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 60648 W: 15550 L: 15187 D: 29911
Ptnml(0-2): 40, 6356, 17171, 6715, 42
https://tests.stockfishchess.org/tests/live_elo/660298ed0ec64f0526c566d0

Values were found using two tunes with the final values taken from the ltc tune after 62k games :
stc - https://tests.stockfishchess.org/tests/view/65fb526b0ec64f0526c50694
ltc - https://tests.stockfishchess.org/tests/view/65fd36e60ec64f0526c5214b

Ideas for future work;

  * tune these values with the other TM adjustments
  * try narrower bands
  * calculate adjustment for exact eval by interpolation

closes https://github.com/official-stockfish/Stockfish/pull/5138

No functional change
2024-03-29 10:09:42 +01:00
Gahtan Nahdi ed24e3a0a6 Remove material imbalance from nnue eval
Passed non-reg STC:
https://tests.stockfishchess.org/tests/view/65fdf11f0ec64f0526c52b57
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 76480 W: 19893 L: 19712 D: 36875
Ptnml(0-2): 339, 9107, 19157, 9308, 329

Passed non-reg LTC:
https://tests.stockfishchess.org/tests/view/65fee22e0ec64f0526c53885
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 150948 W: 38078 L: 37988 D: 74882
Ptnml(0-2): 111, 16997, 41148, 17127, 91

closes https://github.com/official-stockfish/Stockfish/pull/5135

Bench: 2103324
2024-03-26 18:17:00 +01:00
FauziAkram d49b3738bc Tweak the stats bonus and malus
For depth 1 we don't have a negative score anymore.

Passed STC:
https://tests.stockfishchess.org/tests/view/65fb055c0ec64f0526c5024f
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 117120 W: 30468 L: 30023 D: 56629
Ptnml(0-2): 526, 13759, 29539, 14216, 520

Passed LTC:
https://tests.stockfishchess.org/tests/view/65fdca4b0ec64f0526c5293f
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 54816 W: 13955 L: 13595 D: 27266
Ptnml(0-2): 30, 6046, 14897, 6404, 31

closes https://github.com/official-stockfish/Stockfish/pull/5134

Bench: 1876428
2024-03-26 18:17:00 +01:00
Disservin 7998570414 Add functionality to export small net
Usage
```
export_net <filenameBigNet> <filenameSmallNet>
```

closes https://github.com/official-stockfish/Stockfish/pull/5133

No functional change
2024-03-26 18:17:00 +01:00
mstembera 5001d49f42 Update nnue_feature_transformer.h
Unroll update_accumulator_refresh to process two
active indices simultaneously.

The compiler might not unroll effectively because
the number of active indices isn't known at
compile time.

STC https://tests.stockfishchess.org/tests/view/65faa8850ec64f0526c4fca9
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 130464 W: 33882 L: 33431 D: 63151
Ptnml(0-2): 539, 14591, 34501, 15082, 519

closes https://github.com/official-stockfish/Stockfish/pull/5125

No functional change
2024-03-26 18:06:49 +01:00
Muzhen Gaming d99f89506b VVLTC search tune
This set of parameters was derived from 3 tuning attempts:

    https://tests.stockfishchess.org/tests/view/65d19ab61d8e83c78bfd8436 (80+0.8 x8, ~40k games)
    Then tuned with one of linrock's early L1-3072 nets:
    https://tests.stockfishchess.org/tests/view/65def7b04b19edc854ebdec8 (VVLTC, ~36k games)
    Starting from the result of this tuning, the parameters were then tuned with the current master net:
    https://tests.stockfishchess.org/tests/view/65f11c420ec64f0526c46fc4 (VVLTC, ~45k games)

Additionally, at the start of the third tuning phase, 2 parameters were manually changed:

    Notably, the triple extension margin was decreased from 78 to 22. This idea was given by Vizvezdenec:
    https://tests.stockfishchess.org/tests/view/65f0a2360ec64f0526c46752.
    The PvNode extension margin was also adjusted from 50 to 40.

This tune also differs from previous tuning attempts by tuning the evaluation thresholds for smallnet and psqt-only.
The former was increased through the tuning, and this is hypothesized to scale better at VVLTC,
although there is not much evidence of it.

Passed VVLTC 1st sprt: https://tests.stockfishchess.org/tests/view/65f6761d0ec64f0526c4be88
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 44688 W: 11421 L: 11140 D: 22127
Ptnml(0-2): 1, 4170, 13722, 4449, 2

Passed VVLTC 2nd sprt: https://tests.stockfishchess.org/tests/view/65fa31a30ec64f0526c4f611
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 27450 W: 7057 L: 6778 D: 13615
Ptnml(0-2): 4, 2545, 8346, 2828, 2

STC Elo estimate: https://tests.stockfishchess.org/tests/view/65fd3e540ec64f0526c521ae
Elo: -7.84 ± 1.8 (95%) LOS: 0.0%
Total: 40000 W: 9899 L: 10802 D: 19299
Ptnml(0-2): 203, 5221, 10025, 4378, 173
nElo: -14.91 ± 3.4 (95%) PairsRatio: 0.84

closes https://github.com/official-stockfish/Stockfish/pull/5130

Bench: 1876107
2024-03-22 16:44:06 +01:00
Disservin 7e427639ce Add cmath header to movepick.h
No functional change
2024-03-20 16:36:18 +01:00
Gahtan Nahdi 8e61d70499 Remove reduction increase on repetition
Passed non-reg STC:
https://tests.stockfishchess.org/tests/view/65f89ae30ec64f0526c4e0ff
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 173568 W: 45005 L: 44936 D: 83627
Ptnml(0-2): 684, 19878, 45628, 19873, 721

Passed non-reg LTC:
https://tests.stockfishchess.org/tests/view/65fa0f370ec64f0526c4f42d
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 21138 W: 5432 L: 5216 D: 10490
Ptnml(0-2): 13, 2107, 6112, 2325, 12

closes https://github.com/official-stockfish/Stockfish/pull/5123

Bench: 2109005
2024-03-20 16:29:35 +01:00
Gahtan Nahdi 1a6c22c511 Evaluation adjustment for different eval types
Gives different eval scaling parameters for the three different types
of evaluation (bignet, smallnet, psqtOnly).

Passed STC:
https://tests.stockfishchess.org/tests/view/65f4b0020ec64f0526c4a3bd
LLR: 2.96 (-2.94,2.94) <0.00,2.00>
Total: 168064 W: 43507 L: 42987 D: 81570
Ptnml(0-2): 662, 19871, 42445, 20393, 661

Passed LTC:
https://tests.stockfishchess.org/tests/view/65f6be1a0ec64f0526c4c361
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 162564 W: 41188 L: 40604 D: 80772
Ptnml(0-2): 120, 18112, 44216, 18732, 102

closes https://github.com/official-stockfish/Stockfish/pull/5122

Bench: 2113576
2024-03-20 16:29:35 +01:00
Robert Nurnberg 9b92ada935 Base WDL model on material count and normalize evals dynamically
This PR proposes to change the parameter dependence of Stockfish's
internal WDL model from full move counter to material count. In addition
it ensures that an evaluation of 100 centipawns always corresponds to a
50% win probability at fishtest LTC, whereas for master this holds only
at move number 32. See also
https://github.com/official-stockfish/Stockfish/pull/4920 and the
discussion therein.

The new model was fitted based on about 340M positions extracted from
5.6M fishtest LTC games from the last three weeks, involving SF versions
from e67cc979fd (SF 16.1) to current
master.

The involved commands are for
[WDL_model](https://github.com/official-stockfish/WDL_model) are:
```
./updateWDL.sh --firstrev e67cc979fd
python scoreWDL.py updateWDL.json --plot save --pgnName update_material.png --momType "material" --momTarget 58 --materialMin 10 --modelFitting optimizeProbability
```

The anchor `58` for the material count value was chosen to be as close
as possible to the observed average material count of fishtest LTC games
at move 32 (`43`), while not changing the value of
`NormalizeToPawnValue` compared to the move-based WDL model by more than
1.

The patch only affects the displayed cp and wdl values.

closes https://github.com/official-stockfish/Stockfish/pull/5121

No functional change
2024-03-20 16:29:35 +01:00
Disservin 117e08c264 Fix header name in Makefile
No functional change
2024-03-20 16:29:35 +01:00
Disservin 134e6d7bb4 Consistent use of anonymous namespace
Also change `bindThisThread` to match the current code style for function naming.

closes https://github.com/official-stockfish/Stockfish/pull/5118

No functional change
2024-03-20 16:15:37 +01:00
Michael Chaly ed60460004 Clamp history bonus to stats range
Before, one always had to keep track of the bonus one assigns to a history to stop
the stats from overflowing. This is a quality of life improvement. Since this would often go unnoticed during benching.

Passed non-regression bounds:
https://tests.stockfishchess.org/tests/view/65ef2af40ec64f0526c44cbc
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 179232 W: 46513 L: 46450 D: 86269
Ptnml(0-2): 716, 20323, 47452, 20432, 693

closes https://github.com/official-stockfish/Stockfish/pull/5116

No functional change
2024-03-20 16:14:00 +01:00
Disservin fb07281f55 Fix false positives from ThreadSanitizer
Since Linux Kernel 6.5 we are getting false positives from the ci,
lower the ALSR entropy to disable ALSR, which works as a temporary
workaround.

https://github.com/google/sanitizers/issues/1716
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2056762

closes https://github.com/official-stockfish/Stockfish/pull/5115

No functional change
2024-03-14 22:14:11 +01:00
cj5716 abd82396a1 Make effort part of RootMove struct
Also includes several small cleanups.

Passed STC:
https://tests.stockfishchess.org/tests/view/65f15cfe0ec64f0526c473a0
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 71136 W: 18456 L: 18273 D: 34407
Ptnml(0-2): 311, 8014, 18708, 8251, 284

closes https://github.com/official-stockfish/Stockfish/pull/5114

No functional change
2024-03-14 22:13:14 +01:00
Lemmy 23493de082 Sudden Death - Improve TM
Due to the 50 estimated move horizon, once a sudden death game got below
1 second, the time allocation for optimumTime would go into the negative
and SF would start instamoving.

To counter this, once limits.time is below 1 second, the move horizon
will start decreasing, at a rate of 1 move per 20ms. This was just what
seemed a reasonable rate of decay.

Fishtest sudden death TC 5+0
https://tests.stockfishchess.org/tests/live_elo/65ee2cdf0ec64f0526c43bbb
LLR: 2.99 (-2.94,2.94) <0.00,2.00>
Total: 3348 W: 1070 L: 727 D:1551
Ptnml(0-2): 46, 277, 738, 514, 99

Fishtest SD TC 10+0
https://tests.stockfishchess.org/tests/live_elo/65ee401e0ec64f0526c43cf7
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 3780 W: 1097 L: 808 D: 1875
Ptnml(0-2): 11, 353, 919, 550, 57

Neutral Non-Regression STC 10+0.1
https://tests.stockfishchess.org/tests/live_elo/65ee45ff0ec64f0526c43d68
LLR: 2.95 (-2.94,2.94) <-1.75, 0.25>
Total: 123616 W: 32054 L: 31927 D:59635
Ptnml(0-2): 493, 14323, 32105, 14338, 549

Neutral Non-Regression LTC 60+0.6
https://tests.stockfishchess.org/tests/live_elo/65ef1eec0ec64f0526c44bc4
LLR: 2.95 (-2.94,2.94) <-1.75, 0.25>
Total: 130482 W: 32961 L: 32855 D:64666
Ptnml(0-2): 88, 13412, 38123, 13542, 76

closes https://github.com/official-stockfish/Stockfish/pull/5112

Bench: 2002517
2024-03-14 22:12:53 +01:00
Michael Chaly ee2ee6bdc4 Give more bonuses to quiet move that caused a fail low
Give extra bonus if search result is far below static evaluation of
position.

Passed STC:
https://tests.stockfishchess.org/tests/view/65edf1250ec64f0526c43787
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 90816 W: 23713 L: 23307 D: 43796
Ptnml(0-2): 401, 10725, 22742, 11147, 393

Passed LTC:
https://tests.stockfishchess.org/tests/view/65ef5ed70ec64f0526c450af
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 66618 W: 16950 L: 16565 D: 33103
Ptnml(0-2): 35, 7372, 18139, 7699, 64

closes https://github.com/official-stockfish/Stockfish/pull/5111

Bench: 2002517
2024-03-14 22:08:48 +01:00
Disservin 55df0ee009 Fix Raspberry Pi Compilation
Reported by @Torom over discord.

> dev build fails on Raspberry Pi 5 with clang

```
clang++ -o stockfish benchmark.o bitboard.o evaluate.o main.o misc.o movegen.o movepick.o position.o search.o thread.o timeman.o tt.o uci.o ucioption.o tune.o tbprobe.o nnue_misc.o half_ka_v2_hm.o network.o  -fprofile-instr-generate -latomic -lpthread  -Wall -Wcast-qual -fno-exceptions -std=c++17 -fprofile-instr-generate  -pedantic -Wextra -Wshadow -Wmissing-prototypes -Wconditional-uninitialized -DUSE_PTHREADS -DNDEBUG -O3 -funroll-loops -DIS_64BIT -DUSE_POPCNT -DUSE_NEON=8 -march=armv8.2-a+dotprod -DUSE_NEON_DOTPROD -DGIT_SHA=627974c9 -DGIT_DATE=20240312 -DARCH=armv8-dotprod -flto=full
/tmp/lto-llvm-e9300e.o: in function `_GLOBAL__sub_I_network.cpp':
ld-temp.o:(.text.startup+0x704c): relocation truncated to fit: R_AARCH64_LDST64_ABS_LO12_NC against symbol `gEmbeddedNNUEBigEnd' defined in .rodata section in /tmp/lto-llvm-e9300e.o
/usr/bin/ld: ld-temp.o:(.text.startup+0x704c): warning: one possible cause of this error is that the symbol is being referenced in the indicated code as if it had a larger alignment than was declared where it was defined
ld-temp.o:(.text.startup+0x7068): relocation truncated to fit: R_AARCH64_LDST64_ABS_LO12_NC against symbol `gEmbeddedNNUESmallEnd' defined in .rodata section in /tmp/lto-llvm-e9300e.o
/usr/bin/ld: ld-temp.o:(.text.startup+0x7068): warning: one possible cause of this error is that the symbol is being referenced in the indicated code as if it had a larger alignment than was declared where it was defined
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [Makefile:1051: stockfish] Error 1
make[2]: Leaving directory '/home/torsten/chess/Stockfish_master/src'
make[1]: *** [Makefile:1058: clang-profile-make] Error 2
make[1]: Leaving directory '/home/torsten/chess/Stockfish_master/src'
make: *** [Makefile:886: profile-build] Error 2
```

closes https://github.com/official-stockfish/Stockfish/pull/5106

No functional change
2024-03-12 19:09:50 +01:00
FauziAkram 627974c99f Search + Eval + Movepick Tune
Passed STC:
https://tests.stockfishchess.org/tests/view/65ef15220ec64f0526c44b04
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 24480 W: 6459 L: 6153 D: 11868
Ptnml(0-2): 101, 2798, 6184, 3008, 149

Passed LTC:
https://tests.stockfishchess.org/tests/view/65ef4bac0ec64f0526c44f50
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 53316 W: 13561 L: 13203 D: 26552
Ptnml(0-2): 27, 5925, 14408, 6259, 39

closes https://github.com/official-stockfish/Stockfish/pull/5104

Bench: 1715522
2024-03-12 16:47:11 +01:00
FauziAkram daa3ef9148 Simplify increaseDepth to boolean expression
Non-functional Simplification, maintaining the same logic as before. Big thanks
to @peregrineshahin for helping with the code.

Passed non-regression bounds:
https://tests.stockfishchess.org/tests/view/65ec93860ec64f0526c42375
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 101088 W: 26196 L: 26047 D: 48845
Ptnml(0-2): 405, 11580, 26473, 11633, 453

closes https://github.com/official-stockfish/Stockfish/pull/5103

No functional change
2024-03-12 16:43:10 +01:00
Disservin 1a26d698de Refactor Network Usage
Continuing from PR #4968, this update improves how Stockfish handles network
usage, making it easier to manage and modify networks in the future.

With the introduction of a dedicated Network class, creating networks has become
straightforward. See uci.cpp:
```cpp
NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::embeddedNNUEBig)
```

The new `Network` encapsulates all network-related logic, significantly reducing
the complexity previously required to support multiple network types, such as
the distinction between small and big networks #4915.

Non-Regression STC:
https://tests.stockfishchess.org/tests/view/65edd26c0ec64f0526c43584
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 33760 W: 8887 L: 8661 D: 16212
Ptnml(0-2): 143, 3795, 8808, 3961, 173

Non-Regression SMP STC:
https://tests.stockfishchess.org/tests/view/65ed71970ec64f0526c42fdd
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 59088 W: 15121 L: 14931 D: 29036
Ptnml(0-2): 110, 6640, 15829, 6880, 85

Compiled with `make -j profile-build`
```
bash ./bench_parallel.sh ./stockfish ./stockfish-nnue 13 50

sf_base =  1568540 +/-   7637 (95%)
sf_test =  1573129 +/-   7301 (95%)
diff    =     4589 +/-   8720 (95%)
speedup = 0.29260% +/- 0.556% (95%)
```

Compiled with `make -j build`
```
bash ./bench_parallel.sh ./stockfish ./stockfish-nnue 13 50

sf_base =  1472653 +/-   7293 (95%)
sf_test =  1491928 +/-   7661 (95%)
diff    =    19275 +/-   7154 (95%)
speedup = 1.30886% +/- 0.486% (95%)
```

closes https://github.com/official-stockfish/Stockfish/pull/5100

No functional change
2024-03-12 16:41:08 +01:00
Gahtan Nahdi f072634e24 Simplify opponentWorsening condition
Passed non-reg STC:
https://tests.stockfishchess.org/tests/view/65ea18650ec64f0526c4033a
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 226624 W: 58601 L: 58589 D: 109434
Ptnml(0-2): 1030, 27193, 56819, 27275, 995

Passed non-reg LTC:
https://tests.stockfishchess.org/tests/view/65eb7a220ec64f0526c4161a
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 243882 W: 61462 L: 61469 D: 120951
Ptnml(0-2): 197, 27559, 66419, 27586, 180

closes https://github.com/official-stockfish/Stockfish/pull/5102

Bench: 1601012
2024-03-11 10:08:40 +01:00
Muzhen Gaming 10e2732978 VVLTC search tune
Result of 32k games of tuning at 60+0.6 8-thread. Link to the tuning
attempt:
https://tests.stockfishchess.org/tests/view/65def7b04b19edc854ebdec8

Passed VVLTC first SPRT:
https://tests.stockfishchess.org/tests/view/65e51b53416ecd92c162ab7f
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 37570 W: 9613 L: 9342 D: 18615
Ptnml(0-2): 2, 3454, 11601, 3727, 1

Passed VVLTC second SPRT:
https://tests.stockfishchess.org/tests/view/65e87d1c0ec64f0526c3eb39
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 123158 W: 31463 L: 31006 D: 60689
Ptnml(0-2): 5, 11589, 37935, 12044, 6

Note: The small net and psqt-only thresholds have been moved to
evaluate.h. The reasoning is that these values are used in both
`evaluate.cpp` and `evaluate_nnue.cpp`, and thus unifying their usage
avoids inconsistencies during testing, where one occurrence is changed
without the other (this happened during the search tune SPRT).

closes https://github.com/official-stockfish/Stockfish/pull/5101

Bench: 1741218
2024-03-11 10:04:37 +01:00
Disservin b6dfd6bd54 Assorted cleanups
- fix naming convention for `workingDirectory`
- use type alias for `EvalFiles` everywhere
- move `ponderMode` into `LimitsType`
- move limits parsing into standalone static function

closes https://github.com/official-stockfish/Stockfish/pull/5098

No functional change
2024-03-11 09:02:13 +01:00
Robert Nurnberg @ elitebook 632f1c21cd Fix wrong constant usage in go mate
Fixes an oversight in https://github.com/official-stockfish/Stockfish/pull/5094

In theory, master could stop search when run with `go mate 247` and return a TB loss (not a mate score). Also fixes the spelling of opponenWorsening.

closes https://github.com/official-stockfish/Stockfish/pull/5096

No functional change
2024-03-07 21:10:33 +01:00
Muzhen Gaming 0f01a516d2 VLTC time management tune
Result of 35k games of SPSA tuning at 180+1.8. Tuning attempt can be
found here:
https://tests.stockfishchess.org/tests/view/65e40599f2ef6c733362b03b

Passed VLTC 180+1.8:
https://tests.stockfishchess.org/tests/view/65e5a6f5416ecd92c162b5d4
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 31950 W: 8225 L: 7949 D: 15776
Ptnml(0-2): 3, 3195, 9309, 3459, 9

Passed VLTC 240+2.4:
https://tests.stockfishchess.org/tests/view/65e714de0ec64f0526c3d1f1
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 65108 W: 16558 L: 16202 D: 32348
Ptnml(0-2): 7, 6366, 19449, 6728, 4

closes https://github.com/official-stockfish/Stockfish/pull/5095

Bench: 1714391
2024-03-07 20:08:00 +01:00
Shahin M. Shahin 748791f80d Fix go mate x in multithreading
Fixes two issues with master for go mate x:

- when running go mate x in losing positions, master always goes to the
  maximal depth, arguably against what the UCI protocol demands

- when running go mate x in winning positions with multiple
  threads, master may return non-mate scores from the search (this issue
  is present in stockfish since at least sf16) The issues are fixed by
  (a) also checking if score is mate -x and by (b) only letting
  mainthread stop the search for go mate x commands, and by not looking
  for a best thread but using mainthread as per the default. Related:
    niklasf/python-chess#1070

More diagnostics can be found here peregrineshahin#6 (comment)

closes https://github.com/official-stockfish/Stockfish/pull/5094

No functional change

Co-Authored-By: Robert Nürnberg <28635489+robertnurnberg@users.noreply.github.com>
2024-03-07 19:58:33 +01:00
Michael Chaly 6136d094c5 Introduce double extensions for PV nodes
Our double/triple extensions were allowed only for non-pv nodes. This
patch allows them to be done for PV nodes, with some stricter
conditions.

Passed STC:
https://tests.stockfishchess.org/tests/view/65d657ec1d8e83c78bfddab8
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 339424 W: 88097 L: 87318 D: 164009
Ptnml(0-2): 1573, 39935, 85729, 41090, 1385

Passed LTC:
https://tests.stockfishchess.org/tests/view/65dd63824b19edc854ebc433
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 459564 W: 115812 L: 114614 D: 229138
Ptnml(0-2): 248, 51441, 125173, 52705, 215

closes https://github.com/official-stockfish/Stockfish/pull/5093

Bench: 1714391
2024-03-07 19:56:30 +01:00
rn5f107s2 1db969e620 Reduce futility_margin if opponents last move was bad
This reduces the futiltiy_margin if our opponents last move was bad by
around ~1/3 when not improving and ~1/2.7 when improving, the idea being
to retroactively futility prune moves that were played, but turned out
to be bad.  A bad move is being defined as their staticEval before their
move being lower as our staticEval now is. If the depth is 2 and we are
improving the opponent worsening flag is not set, in order to not risk
having a too low futility_margin, due to the fact that when these
conditions are met the futility_margin already drops quite low.

Passed STC:
https://tests.stockfishchess.org/tests/live_elo/65e3977bf2ef6c733362aae3
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 122432 W: 31884 L: 31436 D: 59112
Ptnml(0-2): 467, 14404, 31035, 14834, 476

Passed LTC:
https://tests.stockfishchess.org/tests/live_elo/65e47f40f2ef6c733362b6d2
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 421692 W: 106572 L: 105452 D: 209668
Ptnml(0-2): 216, 47217, 114865, 48327, 221

closes https://github.com/official-stockfish/Stockfish/pull/5092

Bench: 1565939
2024-03-07 19:55:51 +01:00
Linmiao Xu bd579ab5d1 Update default main net to nn-1ceb1ade0001.nnue
Created by retraining the previous main net `nn-b1a57edbea57.nnue` with:
- some of the same options as before:
  - ranger21, more WDL skipping, 15% more loss when Q is too high
- removal of the huge 514G pre-interleaved binpack
- removal of SF-generated dfrc data (dfrc99-16tb7p-filt-v2.min.binpack)
- interleaving many binpacks at training time
- training with some bestmove capture positions where SEE < 0
- increased usage of torch.compile to speed up training by up to 40%

```yaml
experiment-name: 2560--S10-dfrc0-to-dec2023-skip-more-wdl-15p-more-loss-high-q-see-ge0-sk28
nnue-pytorch-branch: linrock/nnue-pytorch/r21-more-wdl-skip-15p-more-loss-high-q-skip-see-ge0-torch-compile-more
start-from-engine-test-net: True

early-fen-skipping: 28
training-dataset:
  # similar, not the exact same as:
  # https://github.com/official-stockfish/Stockfish/pull/4635
  - /data/S5-5af/leela96.v2.min.binpack
  - /data/S5-5af/test60-2021-11-12-novdec-12tb7p.v6-dd.min.binpack
  - /data/S5-5af/test77-2021-12-dec-16tb7p.v6-dd.min.binpack
  - /data/S5-5af/test78-2022-01-to-05-jantomay-16tb7p.v6-dd.min.binpack
  - /data/S5-5af/test78-2022-06-to-09-juntosep-16tb7p.v6-dd.min.binpack
  - /data/S5-5af/test79-2022-04-apr-16tb7p.v6-dd.min.binpack
  - /data/S5-5af/test79-2022-05-may-16tb7p.v6-dd.min.binpack

  - /data/S5-5af/test80-2022-06-jun-16tb7p.v6-dd.min.unmin.binpack
  - /data/S5-5af/test80-2022-07-jul-16tb7p.v6-dd.min.binpack
  - /data/S5-5af/test80-2022-08-aug-16tb7p.v6-dd.min.binpack
  - /data/S5-5af/test80-2022-09-sep-16tb7p.v6-dd.min.unmin.binpack
  - /data/S5-5af/test80-2022-10-oct-16tb7p.v6-dd.min.binpack
  - /data/S5-5af/test80-2022-11-nov-16tb7p.v6-dd.min.binpack

  - /data/S5-5af/test80-2023-01-jan-16tb7p.v6-sk20.min.binpack
  - /data/S5-5af/test80-2023-02-feb-16tb7p.v6-dd.min.binpack
  - /data/S5-5af/test80-2023-03-mar-2tb7p.min.unmin.binpack
  - /data/S5-5af/test80-2023-04-apr-2tb7p.binpack
  - /data/S5-5af/test80-2023-05-may-2tb7p.min.dd.binpack

  # https://github.com/official-stockfish/Stockfish/pull/4782
  - /data/S6-1ee1aba5ed/test80-2023-06-jun-2tb7p.binpack
  - /data/S6-1ee1aba5ed/test80-2023-07-jul-2tb7p.min.binpack

  # https://github.com/official-stockfish/Stockfish/pull/4972
  - /data/S8-baff1edbea57/test80-2023-08-aug-2tb7p.v6.min.binpack
  - /data/S8-baff1edbea57/test80-2023-09-sep-2tb7p.binpack
  - /data/S8-baff1edbea57/test80-2023-10-oct-2tb7p.binpack

  # https://github.com/official-stockfish/Stockfish/pull/5056
  - /data/S9-b1a57edbea57/test80-2023-11-nov-2tb7p.binpack
  - /data/S9-b1a57edbea57/test80-2023-12-dec-2tb7p.binpack

num-epochs: 800
lr: 4.375e-4
gamma: 0.995
start-lambda: 1.0
end-lambda: 0.7
```

This particular net was reached at epoch 759. Use of more torch.compile decorators
in nnue-pytorch model.py than in the previous main net training run sped up training
by up to 40% on Tesla gpus when using recent pytorch compiled with cuda 12:
https://github.com/linrock/nnue-tools/blob/7fb9831/Dockerfile

Skipping positions with bestmove captures where static exchange evaluation is >= 0
is based on the implementation from Sopel's NNUE training & experimentation log:
https://docs.google.com/document/d/1gTlrr02qSNKiXNZ_SuO4-RjK4MXBiFlLE6jvNqqMkAY
Experiment 293 - only skip captures with see>=0

Positions with bestmove captures where score == 0 are always skipped for
compatibility with minimized binpacks, since the original minimizer sets
scores to 0 for slight improvements in compression.

The trainer branch used was:
https://github.com/linrock/nnue-pytorch/tree/r21-more-wdl-skip-15p-more-loss-high-q-skip-see-ge0-torch-compile-more

Binpacks were renamed to be sorted chronologically by default when sorted by name.
The binpack data are otherwise the same as binpacks with similar names in the prior
naming convention.

Training data can be found at:
https://robotmoon.com/nnue-training-data/

Passed STC:
https://tests.stockfishchess.org/tests/view/65e3ddd1f2ef6c733362ae5c
LLR: 2.92 (-2.94,2.94) <0.00,2.00>
Total: 149792 W: 39153 L: 38661 D: 71978
Ptnml(0-2): 675, 17586, 37905, 18032, 698

Passed LTC:
https://tests.stockfishchess.org/tests/view/65e4d91c416ecd92c162a69b
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 64416 W: 16517 L: 16135 D: 31764
Ptnml(0-2): 38, 7218, 17313, 7602, 37

closes https://github.com/official-stockfish/Stockfish/pull/5090

Bench: 1373183
2024-03-07 19:53:48 +01:00
FauziAkram a96b0d4609 Update elo estimates
Tests used to change the elo worth of some functions:

https://tests.stockfishchess.org/tests/view/65c3f69dc865510db0283eef
https://tests.stockfishchess.org/tests/view/65c3f935c865510db0283f2a
https://tests.stockfishchess.org/tests/view/65d1489f1d8e83c78bfd7dbf
https://tests.stockfishchess.org/tests/view/65ce9d361d8e83c78bfd4951
https://tests.stockfishchess.org/tests/view/65cfcd901d8e83c78bfd6184

closes https://github.com/official-stockfish/Stockfish/pull/5089

No functional change
2024-03-07 19:53:48 +01:00
FauziAkram a615efb19f Simplify Time Management
Instead of having a formula for using extra time with larger increments.
Simply set it to 1 when the increment is lower than 0.5s and to 1.1 when
the increment is higher.

The values can later on be further improved.

Passed STC:
https://tests.stockfishchess.org/tests/view/65d25d3c1d8e83c78bfd9293
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 27488 W: 7077 L: 6848 D: 13563
Ptnml(0-2): 96, 3041, 7267, 3218, 122

Passed LTC:
https://tests.stockfishchess.org/tests/view/65d2a72c1d8e83c78bfd97fa
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 137568 W: 34612 L: 34512 D: 68444
Ptnml(0-2): 60, 14672, 39221, 14770, 61

Passed VLTC:
https://tests.stockfishchess.org/tests/view/65d7d7d39b2da0226a5a205b
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 139650 W: 35229 L: 35134 D: 69287
Ptnml(0-2): 33, 14227, 41218, 14306, 41

Passed also the TCEC TC style suggested by vondele:
https://tests.stockfishchess.org/tests/view/65e4ca73416ecd92c162a57d
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 134150 W: 34278 L: 34163 D: 65709
Ptnml(0-2): 561, 15727, 34444, 15722, 621

closes https://github.com/official-stockfish/Stockfish/pull/5076

Bench: 1553115
2024-03-07 19:49:01 +01:00
Gahtan Nahdi b0ac8a4e3b Simplify extension when ttMove is assumed to fail high over current beta
Simplify extension value to -3 when ttMove is assumed to fail high over current beta.

Passed non-reg STC:
https://tests.stockfishchess.org/tests/view/65d66ed81d8e83c78bfddcba
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 235136 W: 60711 L: 60708 D: 113717
Ptnml(0-2): 969, 27904, 59874, 27797, 1024

Passed non-reg LTC:
https://tests.stockfishchess.org/tests/view/65da2994944f2a78d4733107
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 662850 W: 166161 L: 166602 D: 330087
Ptnml(0-2): 394, 74895, 181274, 74482, 380

closes https://github.com/official-stockfish/Stockfish/pull/5088

Bench: 1553115
2024-03-03 15:45:13 +01:00
Gahtan Nahdi 6d0d430860 Simplify IIR
Simplified depth reduction for PV nodes without a ttMove to 3.

Passed STC non-reg:
https://tests.stockfishchess.org/tests/view/65d1a90a1d8e83c78bfd855a
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 363168 W: 93648 L: 93791 D: 175729
Ptnml(0-2): 1557, 43692, 91221, 43565, 1549

Passed LTC non-reg:
https://tests.stockfishchess.org/tests/view/65d5612d1d8e83c78bfdc8e2
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 58818 W: 14946 L: 14761 D: 29111
Ptnml(0-2): 36, 6595, 15962, 6780, 36

closes https://github.com/official-stockfish/Stockfish/pull/5062

Bench: 1505827
2024-03-03 15:42:17 +01:00
mstembera 7831131591 Only evaluate the PSQT part of the small net for large evals.
Thanks to Viren6 for suggesting to set complexity to 0.

STC https://tests.stockfishchess.org/tests/view/65d7d6709b2da0226a5a203f
LLR: 2.92 (-2.94,2.94) <0.00,2.00>
Total: 328384 W: 85316 L: 84554 D: 158514
Ptnml(0-2): 1414, 39076, 82486, 39766, 1450

LTC https://tests.stockfishchess.org/tests/view/65dce6d290f639b028a54d2e
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 165162 W: 41918 L: 41330 D: 81914
Ptnml(0-2): 102, 18332, 45124, 18922, 101

closes https://github.com/official-stockfish/Stockfish/pull/5083

bench: 1504003
2024-03-03 15:29:58 +01:00
Disservin 0a3eb1d8fa Document TT code more
Slight refactor of the TT code with the goal to make it easier to understand / tweak.

Passed Non-Regression STC:
https://tests.stockfishchess.org/tests/view/65d51e401d8e83c78bfdc427
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 56416 W: 14750 L: 14550 D: 27116
Ptnml(0-2): 227, 6386, 14796, 6558, 241

closes https://github.com/official-stockfish/Stockfish/pull/5061

No functional change
2024-03-03 15:21:57 +01:00
Gahtan Nahdi f77eddfa2f Join conditions for move sorting heuristics
closes https://github.com/official-stockfish/Stockfish/pull/5078

No functional change.
2024-03-03 15:18:13 +01:00
Disservin 0c22d5bb1a Update Actions to Node20
ensure our CI continues to run after Node16 is obsolote on github.

closes https://github.com/official-stockfish/Stockfish/pull/5074

No functional change
2024-03-03 15:09:46 +01:00
Disservin c83c7f4e71 Make binaries executable again in CI
closes https://github.com/official-stockfish/Stockfish/pull/5072

No functional change
2024-03-03 15:07:32 +01:00
Disservin abcc090a62 Restore development
closes https://github.com/official-stockfish/Stockfish/pull/5073

No functional change
2024-03-03 15:01:29 +01:00
Disservin e67cc979fd Stockfish 16.1
Official release version of Stockfish 16.1

Bench: 1303971

---

Stockfish 16.1

Today, we have the pleasure to announce Stockfish 16.1. As always, you can
freely download it at https://stockfishchess.org/download and use it in the GUI
of your choice[1].

Don't forget to join our Discord server[2] to get in touch with the community of
developers and users of the project!

*Quality of chess play*

In our testing against its predecessor, Stockfish 16.1 shows a notable
improvement in performance, with an Elo gain of up to 27 points and winning over
2 times more game pairs[3] than it loses.

*Update highlights*

*Improved evaluation*

- Updated neural network architecture: The neural network architecture has
  undergone two updates and is currently in its 8th version[4].
- Removal of handcrafted evaluation (HCE): This release marks the removal of the
  traditional handcrafted evaluation and the transition to a fully neural
  network-based approach[5].
- Dual NNUE: For the first time, Stockfish includes a secondary neural
  network[6], used to quickly evaluate positions that are easily decided.

*UCI Options removed*

`Use NNUE` and `UCI_AnalyseMode`[7] have been removed as they no longer had any
effect. `SlowMover`[8] has also been removed in favor of `Move Overhead`.

*More binaries*

We now offer 13 new binaries. These new binaries include `avx512`, `vnni256`,
`vnni512`, `m1-apple-silicon`, and `armv8-dotprod`, which take advantage of
specific CPU instructions for improved performance.
For most users, using `sse41-popcnt` (formerly `modern`), `avx2`, or `bmi2`
should be enough, but if your CPU supports these new instructions, feel free to
try them!

*Development changes*

- Updated testing book: This new book[9], now derived exclusively from the open
  Lichess database[10], is 10 times larger than its predecessor, and has been
  used to test potential improvements to Stockfish over the past few months.
- Consolidation of repositories: Aiming to simplify access to our resources, we
  have moved most Stockfish-related repositories into the official Stockfish
  organization[11] on GitHub.
- Growing maintainer team: We welcome Disservin[12] to the team of maintainers
  of the project! This extra pair of hands will ensure the lasting success of
  Stockfish.

*Thank you*

The Stockfish project builds on a thriving community of enthusiasts (thanks
everybody!) who contribute their expertise, time, and resources to build a free
and open-source chess engine that is robust, widely available, and very strong.

We would like to express our gratitude for the 10k stars[13] that light up our
GitHub project! Thank you for your support and encouragement – your recognition
means a lot to us.

We invite our chess fans to join the Fishtest testing framework[14], and
programmers to contribute to the project either directly to Stockfish[15] (C++),
to Fishtest[16] (HTML, CSS, JavaScript, and Python), to our trainer
nnue-pytorch[17] (C++ and Python), or to our website[18] (HTML, CSS/SCSS, and
JavaScript).

The Stockfish team

[1] https://github.com/official-stockfish/Stockfish/wiki/Download-and-usage#download-a-chess-gui
[2] https://discord.gg/GWDRS3kU6R
[3] https://tests.stockfishchess.org/tests/view/65d666051d8e83c78bfddbd8
[4] https://github.com/official-stockfish/nnue-pytorch/blob/master/docs/nnue.md#sfnnv8-architecture
[5] https://github.com/official-stockfish/Stockfish/commit/af110e0
[6] https://github.com/official-stockfish/Stockfish/commit/584d9ef
[7] https://github.com/official-stockfish/Stockfish/commit/c53d2ec
[8] https://github.com/official-stockfish/Stockfish/commit/536d692
[9] https://github.com/official-stockfish/books/commit/426eca4
[10] https://database.lichess.org/
[11] https://github.com/official-stockfish/
[12] https://github.com/Disservin
[13] https://github.com/official-stockfish/Stockfish/stargazers
[14] https://github.com/official-stockfish/fishtest/wiki/Running-the-worker
[15] https://github.com/official-stockfish/Stockfish
[16] https://github.com/official-stockfish/fishtest
[17] https://github.com/official-stockfish/nnue-pytorch
[18] https://github.com/official-stockfish/stockfish-web
2024-02-24 18:15:04 +01:00
Robert Nurnberg @ elitebook 5c2b385957 Update the WDL model
Based on 130M positions from 2.1M games.

```
Look recursively in directory pgns for games from SPRT tests using books
matching "UHO_4060_v..epd|UHO_Lichess_4852_v1.epd" for SF revisions
between 8e75548f2a (from 2024-02-17
17:11:46 +0100) and HEAD (from 2024-02-17 17:13:07 +0100). Based on
127920843 positions from 2109240 games, NormalizeToPawnValue should
change from 345 to 356.
```

The patch only affects the UCI-reported cp and wdl values.

closes https://github.com/official-stockfish/Stockfish/pull/5070

No functional change
2024-02-24 17:59:41 +01:00
Disservin bec83a1869 Update Top CPU Contributors
closes https://github.com/official-stockfish/Stockfish/pull/5069

No functional change
2024-02-24 17:58:44 +01:00
Disservin d07033d5da Expose EvalFileSmall option for small net
Since https://github.com/official-stockfish/fishtest/pull/1870 has been merged
it's time for this update.

5k Fixed Games showed no problems.
https://tests.stockfishchess.org/tests/view/65d9cc274c0e22b904f574d7

closes https://github.com/official-stockfish/Stockfish/pull/5068

No functional change
2024-02-24 17:57:49 +01:00
cj5716 fc41f64dfd Simplify PV node reduction
Reduce less on PV nodes even with an upperbound TT entry.

Passed STC:
https://tests.stockfishchess.org/tests/view/65cb3a861d8e83c78bfd0497
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 118752 W: 30441 L: 30307 D: 58004
Ptnml(0-2): 476, 14179, 29921, 14335, 465

Passed LTC:
https://tests.stockfishchess.org/tests/view/65cd3b951d8e83c78bfd2b0d
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 155058 W: 38549 L: 38464 D: 78045
Ptnml(0-2): 85, 17521, 42219, 17632, 72

closes https://github.com/official-stockfish/Stockfish/pull/5057

Bench: 1303971
2024-02-17 17:13:07 +01:00
Linmiao Xu 8e75548f2a Update default main net to nn-b1a57edbea57.nnue
Created by retraining the previous main net `nn-baff1edbea57.nnue` with:
- some of the same options as before: ranger21, more WDL skipping
- the addition of T80 nov+dec 2023 data
- increasing loss by 15% when prediction is too high, up from 10%
- use of torch.compile to speed up training by over 25%

```yaml
experiment-name: 2560--S9-514G-T80-augtodec2023-more-wdl-skip-15p-more-loss-high-q-sk28

training-dataset:
  # https://github.com/official-stockfish/Stockfish/pull/4782
  - /data/S6-514G-1ee1aba5ed.binpack
  - /data/test80-aug2023-2tb7p.v6.min.binpack
  - /data/test80-sep2023-2tb7p.binpack
  - /data/test80-oct2023-2tb7p.binpack
  - /data/test80-nov2023-2tb7p.binpack
  - /data/test80-dec2023-2tb7p.binpack
early-fen-skipping: 28

start-from-engine-test-net: True
nnue-pytorch-branch: linrock/nnue-pytorch/r21-more-wdl-skip-15p-more-loss-high-q-torch-compile

num-epochs: 1000
lr: 4.375e-4
gamma: 0.995
start-lambda: 1.0
end-lambda: 0.7
```

Epoch 819 trained with the above config led to this PR. Use of torch.compile
decorators in nnue-pytorch model.py was found to speed up training by at least
25% on Ampere gpus when using recent pytorch compiled with cuda 12:
https://catalog.ngc.nvidia.com/orgs/nvidia/containers/pytorch

See recent main net PRs for more info on
- ranger21 and more WDL skipping: https://github.com/official-stockfish/Stockfish/pull/4942
- increasing loss when Q is too high: https://github.com/official-stockfish/Stockfish/pull/4972

Training data can be found at:
https://robotmoon.com/nnue-training-data/

Passed STC:
https://tests.stockfishchess.org/tests/view/65cd76151d8e83c78bfd2f52
LLR: 2.98 (-2.94,2.94) <0.00,2.00>
Total: 78336 W: 20504 L: 20115 D: 37717
Ptnml(0-2): 317, 9225, 19721, 9562, 343

Passed LTC:
https://tests.stockfishchess.org/tests/view/65ce5be61d8e83c78bfd43e9
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 41016 W: 10492 L: 10159 D: 20365
Ptnml(0-2): 22, 4533, 11071, 4854, 28

closes https://github.com/official-stockfish/Stockfish/pull/5056

Bench: 1351997
2024-02-17 17:11:46 +01:00
cj5716 f3df0cfb84 Simplify TT PV reduction
This also removes some incorrect fail-high logic.

Passed STC:
https://tests.stockfishchess.org/tests/view/65cb3b641d8e83c78bfd04a9
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 87968 W: 22634 L: 22468 D: 42866
Ptnml(0-2): 315, 10436, 22323, 10588, 322

Passed LTC:
https://tests.stockfishchess.org/tests/view/65cccee21d8e83c78bfd222c
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 70794 W: 17846 L: 17672 D: 35276
Ptnml(0-2): 44, 7980, 19189, 8126, 58

closes https://github.com/official-stockfish/Stockfish/pull/5055

Bench: 1474424
2024-02-17 17:10:13 +01:00
Gahtan Nahdi 9d61822b5d Remove penalty for quiet ttMove that fails low
Passed STC non-reg:
https://tests.stockfishchess.org/tests/view/65c691a7c865510db0286e6e
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 234336 W: 60258 L: 60255 D: 113823
Ptnml(0-2): 966, 28141, 58918, 28210, 933

Passed LTC non-reg:
https://tests.stockfishchess.org/tests/view/65c8d0d31d8e83c78bfcd4a6
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 235206 W: 59134 L: 59132 D: 116940
Ptnml(0-2): 135, 26908, 63517, 26906, 137

https://github.com/official-stockfish/Stockfish/pull/5054

Bench: 1287996
2024-02-17 17:08:01 +01:00
Disservin bf2c7306ac Use node counting to early stop search
This introduces a form of node counting which can
be used to further tweak the usage of our search
time.
The current approach stops the search when almost
all nodes are searched on a single move.

The idea originally came from Koivisto, but the
implemention is a bit different, Koivisto scales
the optimal time by the nodes effort and then
determines if the search should be stopped.
We just scale down the `totalTime` and stop the
search if we exceed it and the effort is large
enough.

Passed STC:
https://tests.stockfishchess.org/tests/view/65c8e0661d8e83c78bfcd5ec
LLR: 2.97 (-2.94,2.94) <0.00,2.00>
Total: 88672 W: 22907 L: 22512 D: 43253
Ptnml(0-2): 310, 10163, 23041, 10466, 356

Passed LTC:
https://tests.stockfishchess.org/tests/view/65ca632b1d8e83c78bfcf554
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 170856 W: 42910 L: 42320 D: 85626
Ptnml(0-2): 104, 18337, 47960, 18919, 108

closes https://github.com/official-stockfish/Stockfish/pull/5053

Bench: 1198939
2024-02-14 21:39:31 +01:00
Tierynn Byrnes f4f0b32d55 Refactor timeman.cpp
Move optExtra, optConstant and maxConstant into lower scope.

closes https://github.com/official-stockfish/Stockfish/pull/5052

No functional change
2024-02-14 21:38:17 +01:00
Muzhen Gaming 5c03883107 VVLTC search tune
Search parameters were tuned using 16k games at
VVLTC. They were tuned starting with the new
parameters (in search only) of PR #5039.

Passed VVLTC:
https://tests.stockfishchess.org/tests/view/65c8a8fc1d8e83c78bfcd163
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 20826 W: 5355 L: 5100 D: 10371
Ptnml(0-2): 1, 1941, 6275, 2194, 2

Passed 2nd VVLTC:
https://tests.stockfishchess.org/tests/view/65cadc2d1d8e83c78bfcfdaf
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 17710 W: 4611 L: 4352 D: 8747
Ptnml(0-2): 1, 1586, 5422, 1845, 1

STC Elo estimate:
https://tests.stockfishchess.org/tests/view/65cb6aed1d8e83c78bfd0802
Elo: -1.46 ± 1.8 (95%) LOS: 5.5%
Total: 40000 W: 10267 L: 10435 D: 19298
Ptnml(0-2): 200, 4860, 10023, 4742, 175
nElo: -2.77 ± 3.4 (95%) PairsRatio: 0.97

Bench: 1198939
2024-02-14 21:27:55 +01:00
Disservin 7ccde25baf Format code using clang-format
No functional change
2024-02-11 20:13:19 +01:00
Gahtan Nahdi c115e5171e Remove quiet tt move extensions
Passed STC:
https://tests.stockfishchess.org/tests/view/65c6934cc865510db0286e90
LLR: 2.99 (-2.94,2.94) <-1.75,0.25>
Total: 54016 W: 14065 L: 13854 D: 26097
Ptnml(0-2): 231, 6381, 13581, 6576, 239

Passed LTC:
https://tests.stockfishchess.org/tests/view/65c72b91c865510db0287a1a
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 55098 W: 13850 L: 13658 D: 27590
Ptnml(0-2): 37, 6257, 14777, 6433, 45

closes https://github.com/official-stockfish/Stockfish/pull/5049

Bench: 1027182
2024-02-11 19:58:35 +01:00
mstembera 531747ee78 Improve thread voting inefficiencies
Initialize the unordered map to a reasonable
number of buckets and make the move hashes well
distributed. For more see
https://github.com/official-stockfish/Stockfish/pull/4958#issuecomment-1937351190
Also make bestThreadPV and newThreadPV references
so we don't copy entire vectors.

closes https://github.com/official-stockfish/Stockfish/pull/5048

No functional change
2024-02-11 19:55:26 +01:00
Michael Chaly 91a4cea437 Adjust best value in main search depending on depth
This patch does similar thing to how it's done for
qsearch - in case of fail high adjust result to
lower value. Difference is that it is done only
for non-pv nodes and it's depth dependent - so
lower depth entries will have bigger adjustment
and higher depth entries will have smaller
adjustment.

Passed STC:
https://tests.stockfishchess.org/tests/view/65c3c0cbc865510db0283b21
LLR: 2.96 (-2.94,2.94) <0.00,2.00>
Total: 112032 W: 29142 L: 28705 D: 54185
Ptnml(0-2): 479, 13152, 28326, 13571, 488

Passed LTC:
https://tests.stockfishchess.org/tests/view/65c52e62c865510db02855d5
LLR: 2.96 (-2.94,2.94) <0.50,2.50>
Total: 132480 W: 33457 L: 32936 D: 66087
Ptnml(0-2): 67, 14697, 36222, 15156, 98

closes https://github.com/official-stockfish/Stockfish/pull/5047

Bench: 1168241
2024-02-11 19:53:45 +01:00
Disservin 9068fdc57b Assorted cleanups
Assorted cleanups

closes https://github.com/official-stockfish/Stockfish/pull/5046

No functional change

Co-Authored-By: Shahin M. Shahin <41402573+peregrineshahin@users.noreply.github.com>
Co-Authored-By: cj5716 <125858804+cj5716@users.noreply.github.com>
2024-02-11 19:52:00 +01:00
GoldenRare 3d5b16df7c Remove unnecessary assignments related to adjusted static evaluation
In both search and qsearch, there are instances
where we do unadjustedStaticEval = ss->staticEval
= eval/bestValue = tte->eval(), but immediately
after re-assign ss-static and eval/bestValue to
some new value, which makes the initial assignment
redundant.

closes https://github.com/official-stockfish/Stockfish/pull/5045

No functional change
2024-02-11 19:46:55 +01:00
Disservin 21dff6c276 Update CI actions
- Update codeql to v3
- Switch from dev-drprasad to native github cli
- Update softprops/action-gh-release to node 20 commit

`thollander/actions-comment-pull-request` needs to
be bumped to node20 too, but the author hasnt done
so atm

closes https://github.com/official-stockfish/Stockfish/pull/5044

No functional change
2024-02-11 19:40:33 +01:00
mstembera 9699f4f79a Fix the alignment of the transformer buffer
Fixes the issue mentioned in
https://github.com/official-stockfish/Stockfish/commit/584d9efedcde330eeb96a99215552ddfb06f52ba#r138417600.
Thanks to @cj5716 and @peregrineshahin for
spotting this!

closes https://github.com/official-stockfish/Stockfish/pull/5042

No functional change
2024-02-09 19:06:25 +01:00
Muzhen Gaming 96837bc439 Remove check extension
Passed simplification STC:
https://tests.stockfishchess.org/tests/view/65c38d2ac865510db02836cf
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 52288 W: 13578 L: 13371 D: 25339
Ptnml(0-2): 197, 6171, 13265, 6250, 261

Passed simplification LTC:
https://tests.stockfishchess.org/tests/view/65c4470ec865510db0284473
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 44958 W: 11255 L: 11055 D: 22648
Ptnml(0-2): 37, 4962, 12274, 5176, 30

closes https://github.com/official-stockfish/Stockfish/pull/5041

Bench: 1116591
2024-02-09 19:06:25 +01:00
gahtan-syarif 15093d43c4 Simplify opponent movecount reduction
This removes the reduction decrease that occured
when the previous ply had a movecount greater than
7.

Passed STC:
https://tests.stockfishchess.org/tests/view/65c3f6dac865510db0283ef6
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 11968 W: 3205 L: 2953 D: 5810
Ptnml(0-2): 38, 1310, 3064, 1506, 66

Passed LTC:
https://tests.stockfishchess.org/tests/view/65c42377c865510db0284217
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 35676 W: 9113 L: 8905 D: 17658
Ptnml(0-2): 22, 3893, 9802, 4097, 24

closes https://github.com/official-stockfish/Stockfish/pull/5040

Bench: 1148379
2024-02-09 19:06:25 +01:00
cj5716 c0107b3c27 Remove simple eval
With the recent introduction of the dual NNUE, the
need for simple eval is no longer there.

Passed STC:
https://tests.stockfishchess.org/tests/view/65c1f735c865510db0281652
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 85312 W: 22009 L: 21837 D: 41466
Ptnml(0-2): 334, 10155, 21567, 10205, 395

Passed LTC:
https://tests.stockfishchess.org/tests/view/65c2d64bc865510db0282810
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 49956 W: 12596 L: 12402 D: 24958
Ptnml(0-2): 28, 5553, 13624, 5743, 30

closes https://github.com/official-stockfish/Stockfish/pull/5037

Bench 1213676
2024-02-09 19:06:25 +01:00
Stefan Geschwentner f2984471c9 Tweak capture scoring for move ordering
Move divisor from capture scoring to good capture
check and sligthly increase it.

This has several effects:
- its a speedup because for quience and probcut
  search the division now never happens. For main
  search its delayed and can be avoided if a good
  capture triggers a cutoff
- through the higher resolution of scores we have
  a more granular sorting

STC: https://tests.stockfishchess.org/tests/view/65bf2a93c865510db027dc27
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 470016 W: 122150 L: 121173 D: 226693
Ptnml(0-2): 2133, 55705, 118374, 56644, 2152

LTC: https://tests.stockfishchess.org/tests/view/65c1d16dc865510db0281339
LLR: 2.96 (-2.94,2.94) <0.50,2.50>
Total: 98988 W: 25121 L: 24667 D: 49200
Ptnml(0-2): 77, 10998, 26884, 11464, 71

closes https://github.com/official-stockfish/Stockfish/pull/5036

Bench: 1233867
2024-02-09 19:06:25 +01:00
Disservin a20726eb0b Refactor the CI workflows
This refactors the CI workflows to group some
logic and makes sure that all (pre)release
binaries are actually tested.

The screenshot below shows the execution logic of
the reworked ci,
https://github.com/Disservin/Stockfish/actions/runs/7773581379.
You can also hover over the cards to see the
execution flow.

The `matrix.json` and `arm_matrix.json` define the
binaries which will be uploaded to GitHub.
Afterwards a matrix is created and each job
compiles a profile guided build for that arch and
uploads that as an artifact to GitHub. The
Binaries/ARM_Binaries workflow's are called when
the previous step has been completed, and uploads
all artifacts to the (pre)release.

This also fixes some indentations and renames the
workflows, see
https://github.com/official-stockfish/Stockfish/actions,
where every workflow is called `Stockfish` vs
https://github.com/Disservin/Stockfish/actions. It
also increases the parallel compilation used for
make from `-j2 to -j4`.

It now also prevents the prerelease action from
running on forks.

A test release can be viewed here
https://github.com/Disservin/Stockfish/releases.

closes https://github.com/official-stockfish/Stockfish/pull/5035

No functional change
2024-02-09 19:06:25 +01:00
FauziAkram 59691d46a1 Assorted trivial cleanups
Renaming doubleExtensions variable to multiExtensions, since now we have also triple extensions.

Some extra cleanups.

Recent tests used to measure the elo worth:
https://tests.stockfishchess.org/tests/view/659fd0c379aa8af82b96abc3
https://tests.stockfishchess.org/tests/view/65a8f3da79aa8af82b9751e3
https://tests.stockfishchess.org/tests/view/65b51824c865510db0272740
https://tests.stockfishchess.org/tests/view/65b58fbfc865510db0272f5b

closes https://github.com/official-stockfish/Stockfish/pull/5032

No functional change
2024-02-09 19:06:24 +01:00
Muzhen Gaming ededadcd6f VVLTC search tune
Search parameters were tuned at 60+0.6 8-thread.
Link to the tuning attempt: https://tests.stockfishchess.org/tests/view/65b84e8dc865510db0276030

The most significant change is the triple extension parameter, from 200 to 78. This presumably improves scaling.
Additionally, the value < singularBeta - 2 condition for double extensions was removed.
This can simply be considered a parameter tweak from 2 to 0.

Passed VVLTC: https://tests.stockfishchess.org/tests/view/65baec69c865510db0278f19
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 26136 W: 6564 L: 6305 D: 13267
Ptnml(0-2): 2, 2413, 7977, 2676, 0

Passed VVLTC vs passed PR #5027: https://tests.stockfishchess.org/tests/view/65bc2adfc865510db027a561
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 52968 W: 13372 L: 13046 D: 26550
Ptnml(0-2): 4, 4944, 16265, 5264, 7

STC Elo estimate: https://tests.stockfishchess.org/tests/view/65be5514c865510db027cbc5

closes https://github.com/official-stockfish/Stockfish/pull/5029

Bench: 1478189
2024-02-03 17:40:07 +01:00
gab8192 e815227c30 Simplify LMR condition
Apply LMR on captures the same way it is applied on quiets

Passed Non-Reg STC:
https://tests.stockfishchess.org/tests/view/65bbf39bc865510db027a14a
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 77152 W: 19970 L: 19791 D: 37391
Ptnml(0-2): 304, 9159, 19496, 9288, 329

Passed Non-Reg LTC:
https://tests.stockfishchess.org/tests/view/65bc8889c865510db027ac9e
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 103230 W: 25997 L: 25858 D: 51375
Ptnml(0-2): 71, 11687, 27958, 11830, 69

Hit rate of removed condition (!ss->ttPv || !capture || (cutNode && (ss - 1)->moveCount > 1))
Total 1253801 Hits 1228904 Hit Rate (%) 98.0143

Hit rate of previous LMR (depth >= 2 && moveCount > 1 + rootNode && ...)
Total 1253801 Hits 727234 Hit Rate (%) 58.0023

Hit rate of simplified LMR (depth >= 2 && moveCount > 1 + rootNode)
Total 1201839 Hits 713540 Hit Rate (%) 59.3707

closes https://github.com/official-stockfish/Stockfish/pull/5028

Bench: 1438224
2024-02-03 17:30:41 +01:00
Viren6 f2b6b5cfc9 Introduce Triple Extensions
This replaces singularquietLMR with triple instead of double extending non-capture ttmoves that have value far below singularBeta. This threshold value is initially set to 200, there is scope for more scaling by reducing it as occured with double extensions.

Passed STC:
https://tests.stockfishchess.org/tests/view/65b683b8c865510db0274074
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 222912 W: 58141 L: 57535 D: 107236
Ptnml(0-2): 1063, 26244, 56154, 27014, 981

Passed LTC:
https://tests.stockfishchess.org/tests/view/65bae6d4c865510db0278eb5
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 66306 W: 16825 L: 16440 D: 33041
Ptnml(0-2): 40, 7374, 17952, 7735, 52

closes https://github.com/official-stockfish/Stockfish/pull/5027

bench 1394701
2024-02-03 17:26:42 +01:00
FauziAkram 56b342f9b2 Simplify the extension formula
Simplify the extension formula in the case of cutNode by removing the depth condition and always setting extension to -2.

Passed STC:
LLR: 2.97 (-2.94,2.94) <-1.75,0.25>
Total: 277280 W: 70760 L: 70802 D: 135718
Ptnml(0-2): 971, 31775, 73153, 31807, 934
https://tests.stockfishchess.org/tests/view/65ad08f779aa8af82b979dd6

Passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 452976 W: 112992 L: 113215 D: 226769
Ptnml(0-2): 266, 51041, 124112, 50788, 281
https://tests.stockfishchess.org/tests/view/65ae466fc865510db026a760

closes https://github.com/official-stockfish/Stockfish/pull/5021

Bench: 1492957
2024-02-03 17:09:05 +01:00
Disservin 3cce4c4cf4 Add Apple Silicon Runners to CI
GitHub CI runners are available for macOS 14, these runners are using apple silicon chips (M1).
https://github.blog/changelog/2024-01-30-github-actions-introducing-the-new-m1-macos-runner-available-to-open-source/

closes https://github.com/official-stockfish/Stockfish/pull/5025

No functional change
2024-02-03 16:55:10 +01:00
Disservin 16afec0582 Refactor pv printing
Also fix the case which is currently printing depth 0.

fixes #5019
closes https://github.com/official-stockfish/Stockfish/pull/5020

No functional change
2024-02-03 16:50:31 +01:00
Disservin 13eb023fc0 Simplify array initializations
also retire a few std::memset calls.

Passed non-regresion STC:
https://tests.stockfishchess.org/tests/view/65b8e162c865510db0276901
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 97504 W: 25294 L: 25140 D: 47070
Ptnml(1-2): 378, 11102, 25667, 11198, 407

closes https://github.com/official-stockfish/Stockfish/pull/5018

No functional change
2024-02-03 16:43:23 +01:00
Viren6 fcbb02ffde Use ttPv in depth condition of singular extensions
This replaces the PvNode condition and tte Pv call previously with using
the precomputed ttPv, and also removes the multiplier of 2.  This new
depth condition occurs with approximately equal frequency (47%) to the
old depth condition (measured when the other conditions in the if are
true), so non-linear scaling behaviour isn't expected.

Passed Non-Reg STC:
https://tests.stockfishchess.org/tests/view/65b0e132c865510db026da27
LLR: 2.97 (-2.94,2.94) <-1.75,0.25>
Total: 243232 W: 62432 L: 62437 D: 118363
Ptnml(0-2): 910, 28937, 61900, 28986, 883

Passed Non-Reg LTC:
https://tests.stockfishchess.org/tests/view/65b2053bc865510db026eea1
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 190596 W: 47666 L: 47618 D: 95312
Ptnml(0-2): 115, 21710, 51596, 21766, 111

closes https://github.com/official-stockfish/Stockfish/pull/5015

Bench: 1492957
2024-01-26 21:15:07 +01:00
Ahmed Kerimov c17ec9524d Move OnChange callback in Option ctors
Parameter 'f' is passed by value and only copied once. Moving it to
avoid unnecessary copies.

closes https://github.com/official-stockfish/Stockfish/pull/5014

No functional change
2024-01-26 21:00:41 +01:00
Michael Chaly 37bd1e774e Do more double extensions
Parameter tweak from Black Marlin chess engine. Choose a significantly
lower value that triggers in 95% of cases, compared to the usual 84% in
standard benchmark runs.

Since the introduction by
https://github.com/official-stockfish/Stockfish/commit/33a858eaa1f792b3413384a3d0993dba36aca92e
this constant has only decreased in value over time.
2-16-17-18-21-22-25-26-52-71-75-93-140

Failed STC really fast:
https://tests.stockfishchess.org/tests/view/65b11d05c865510db026df7b
LLR: -2.94 (-2.94,2.94) <0.00,2.00>
Total: 13216 W: 3242 L: 3485 D: 6489
Ptnml(0-2): 50, 1682, 3371, 1471, 34

Was reasonable at LTC:

https://tests.stockfishchess.org/tests/view/65b13e20c865510db026e210
Elo: 1.18 ± 1.5 (95%) LOS: 94.3%
Total: 50000 W: 12517 L: 12347 D: 25136
Ptnml(0-2): 31, 5598, 13579, 5754, 38
nElo: 2.45 ± 3.0 (95%) PairsRatio: 1.03

Passed VLTC with STC bounds:
https://tests.stockfishchess.org/tests/view/65b18870c865510db026e769
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 30456 W: 7726 L: 7448 D: 15282
Ptnml(0-2): 6, 3111, 8717, 3387, 7

Passed VVLTC with LTC bounds:
https://tests.stockfishchess.org/tests/view/65b20b95c865510db026eef0
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 36134 W: 9158 L: 8859 D: 18117
Ptnml(0-2): 3, 3455, 10850, 3758, 1

closes https://github.com/official-stockfish/Stockfish/pull/5013

Bench: 1503692
2024-01-26 20:55:16 +01:00
Disservin 1dfbde2d10 Move perft out of search
This splits the logic of search and perft. Before, threads were started,
which then constructed a search object, which then started perft and
returned immediately. All of this is unnecessary, instead uci should
start perft right away.

closes https://github.com/official-stockfish/Stockfish/pull/5008

No functional change
2024-01-26 20:52:26 +01:00
FauziAkram 3d49a99aaf Refactor history score calculation
Passed STC:
https://tests.stockfishchess.org/tests/view/65ad08b179aa8af82b979dd1
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 161376 W: 41582 L: 41498 D: 78296
Ptnml(0-2): 633, 19354, 40611, 19476, 614

Passed LTC:
https://tests.stockfishchess.org/tests/view/65af966fc865510db026c0f0
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 116526 W: 29269 L: 29143 D: 58114
Ptnml(0-2): 71, 13252, 31509, 13342, 89

closes https://github.com/official-stockfish/Stockfish/pull/5006

Bench: 1317504
2024-01-26 20:44:16 +01:00
FauziAkram 2b62c4452d Remove redundant max operation on lmrDepth
Removed a restriction that prohibited history heuristics sum in futility
pruning to exceed some negative value.

Passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 279040 W: 71095 L: 71143 D: 136802
Ptnml(0-2): 949, 33574, 70474, 33622, 901
https://tests.stockfishchess.org/tests/view/65aaef4c79aa8af82b977631

Passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 75156 W: 18884 L: 18715 D: 37557
Ptnml(0-2): 52, 8445, 20408, 8628, 45
https://tests.stockfishchess.org/tests/view/65ae7ef3c865510db026abf5

closes https://github.com/official-stockfish/Stockfish/pull/5004

Bench: 1566543
2024-01-26 20:40:22 +01:00
Muzhen Gaming a6fd17f27d VLTC search tune
Search parameters were tuned using 152k games at 180+1.8.

Passed VLTC:
https://tests.stockfishchess.org/tests/view/65a7a81979aa8af82b973a20
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 117338 W: 29244 L: 28848 D: 59246
Ptnml(0-2): 24, 12474, 33267, 12890, 14

Passed VVLTC:
https://tests.stockfishchess.org/tests/view/65ab246679aa8af82b977982
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 28164 W: 7239 L: 6957 D: 13968
Ptnml(0-2): 3, 2651, 8490, 2937, 1

STC Elo estimate:
https://tests.stockfishchess.org/tests/view/65ac7c0979aa8af82b9792a6
Elo: -0.53 ± 2.0 (95%) LOS: 30.4%
Total: 30000 W: 7688 L: 7734 D: 14578
Ptnml(0-2): 102, 3617, 7614, 3559, 108
nElo: -1.03 ± 3.9 (95%) PairsRatio: 0.99

closes https://github.com/official-stockfish/Stockfish/pull/5003

Bench: 1235377
2024-01-21 12:49:30 +01:00
Robert Nurnberg @ elitebook a901474bf9 Update the WDL model
Update the internal WDL model. After the dual net merge, the internal
evaluations have drifted upwards a bit. With this PR
`NormalizeToPawnValue` changes from `328` to `345`.

The new model was fitted based on about 200M positions extracted from
3.4M fishtest LTC games from the last two weeks, involving SF versions
from 6deb88728f to current master.

Apart from the WDL model parameter update, this PR implements the
following changes:

WDL Model:
- an incorrect 8-move shift in master's WDL model has been fixed
- the polynomials `p_a` and `p_b` are fitted over the move range [8, 120]
- the coefficients for `p_a` and `p_b` are optimized by maximizing the
  probability of predicting the observed outcome (credits to @vondele)

SF code:
- for wdl values, move will be clamped to `max(8, min(120, move))`
- no longer clamp the internal eval to [-4000,4000]
- compute `NormalizeToPawnValue` with `round`, not `trunc`

The PR only affects displayed `cp` and `wdl` values.

closes https://github.com/official-stockfish/Stockfish/pull/5002

No functional change
2024-01-21 12:45:03 +01:00
Shahin M. Shahin ad9fcbc496 Refactor get_best_thread
Make get_best_thread function easier to understand.

Passed non-reg SMP STC:
https://tests.stockfishchess.org/tests/view/65a91c6679aa8af82b975500
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 186000 W: 46379 L: 46325 D: 93296
Ptnml(0-2): 269, 21374, 49634, 21480, 243

closes https://github.com/official-stockfish/Stockfish/pull/5001

No functional change
2024-01-21 12:42:28 +01:00
rn5f107s2 e860f620aa Reduce futility_margin further when improving
The idea of this is to unroll the futility_margin calculation to allow
for the improving flag to have a greater effect on the futility margin.
The current factor is 1.5 instead of the previous 1 resulting in a
deduction of an extra margin/2 from futilit_margin if improving. The
chosen value was not tuned, meaning that there is room for tweaking it.
This patch is partially inspired by @Vizvezdenec, who, although quite
different in execution, tested another idea where the futility_margin is
lowered further when improving [1].

[1]: (first take) https://tests.stockfishchess.org/tests/view/65a56b1879aa8af82b97164b

Passed STC:
https://tests.stockfishchess.org/tests/live_elo/65a8bfc179aa8af82b974e3c
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 161152 W: 41321 L: 40816 D: 79015
Ptnml(0-2): 559, 19030, 40921, 19479, 587

Passed rebased LTC:
https://tests.stockfishchess.org/tests/live_elo/65a8b9ef79aa8af82b974dc0
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 96024 W: 24172 L: 23728 D: 48124
Ptnml(0-2): 56, 10598, 26275, 11012, 71

closes https://github.com/official-stockfish/Stockfish/pull/5000

Bench: 1281703
2024-01-21 12:42:07 +01:00
Viren6 aa15a9179b Refactor ttPv reduction conditions
closes https://github.com/official-stockfish/Stockfish/pull/4999

No functional change
2024-01-21 12:33:08 +01:00
Disservin 856e60d12f Refactor NativeThread start_routine
Removes the free function and fixes the formatting for the function
call.

closes https://github.com/official-stockfish/Stockfish/pull/4995

No functional change
2024-01-21 12:21:01 +01:00
Viren6 c8bc2ce4fa Improve ttPv reduction
This patch allows a partial reduction decrease when a node is likely to
fail low, and increases the reduction decrease when a node has failed
high.

Passed STC:
https://tests.stockfishchess.org/tests/view/65a626e779aa8af82b9722bc
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 157824 W: 40332 L: 39835 D: 77657
Ptnml(0-2): 543, 18617, 40098, 19108, 546

Passed LTC:
https://tests.stockfishchess.org/tests/view/65a7290279aa8af82b97328a
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 57228 W: 14475 L: 14111 D: 28642
Ptnml(0-2): 34, 6278, 15633, 6628, 41

closes https://github.com/official-stockfish/Stockfish/pull/4994

Bench: 1364759
2024-01-17 18:56:37 +01:00
FauziAkram 9a9702d668 Remove threatenedByPawn from rook threat
Can be simplified away.

Passed STC:
https://tests.stockfishchess.org/tests/view/65a3fa4179aa8af82b96face
LLR: 2.92 (-2.94,2.94) <-1.75,0.25>
Total: 30592 W: 7903 L: 7674 D: 15015
Ptnml(0-2): 96, 3590, 7711, 3787, 112

Passed LTC:
https://tests.stockfishchess.org/tests/view/65a42b9a79aa8af82b96fe88
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 73656 W: 18382 L: 18212 D: 37062
Ptnml(0-2): 47, 8287, 19981, 8475, 38

closes https://github.com/official-stockfish/Stockfish/pull/4993

Bench: 1430061
2024-01-17 18:55:44 +01:00
pb00067 0fbad56c50 Refactor code for correcting unadjustedStaticEval
Passed non-regression STC:
https://tests.stockfishchess.org/tests/live_elo/65a4df6a79aa8af82b970ca0
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 43328 W: 11103 L: 10892 D: 21333
Ptnml(0-2): 120, 4920, 11407, 5063, 154

https://github.com/official-stockfish/Stockfish/pull/4992

No functional change
2024-01-17 18:51:03 +01:00
Torsten Hellwig 6c02329860 Fix dotprod detection
This fixes the detection of dotprod capable CPUs. Previously it looked
for the `dotprod` flag, but this does not exist
(https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm64/kernel/cpuinfo.c#n50).
The correct flag that specifies the dotprod capability is the `asimddp`
flag.

fixes #4931

closes https://github.com/official-stockfish/Stockfish/pull/4991

No functional change
2024-01-17 18:32:20 +01:00
Shahin M. Shahin 0c7f56dea6 Fix mated-in behaviour
This addresses the issue where Stockfish may output non-proven checkmate
scores if the search is prematurely halted, either due to a time control
or node limit, before it explores other possibilities where the
checkmate score could have been delayed or refuted.

The fix also replaces staving off from proven mated scores in a
multithread environment making use of the threads instead of a negative
effect with multithreads (1t was better in proving mated in scores than
more threads).

Issue reported on mate tracker repo by and this PR is co-authored with
@robertnurnberg Special thanks to @AndyGrant for outlining that a fix is
eventually possible.

Passed Adj off SMP STC:
https://tests.stockfishchess.org/tests/view/65a125d779aa8af82b96c3eb
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 303256 W: 75823 L: 75892 D: 151541
Ptnml(0-2): 406, 35269, 80395, 35104, 454

Passed Adj off SMP LTC:
https://tests.stockfishchess.org/tests/view/65a37add79aa8af82b96f0f7
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 56056 W: 13951 L: 13770 D: 28335
Ptnml(0-2): 11, 5910, 16002, 6097, 8

Passed all tests in matetrack without any better mate for opponent found in 1t and multithreads.

Fixed bugs in https://github.com/official-stockfish/Stockfish/pull/4976

closes https://github.com/official-stockfish/Stockfish/pull/4990

Bench: 1308279

Co-Authored-By: Robert Nürnberg <28635489+robertnurnberg@users.noreply.github.com>
2024-01-17 18:12:16 +01:00
Disservin f15e4f50aa Update installation guide links in CONTRIBUTING.md
Link to more user friendly installation guides, these are shorter and
easier to follow.

closes https://github.com/official-stockfish/Stockfish/pull/4985

No functional change
2024-01-17 18:06:20 +01:00
Disservin a5675f19d8 Remove global TB variables from search.cpp
Follow up cleanup of #4968, removes the global variables from search and
instead uses a dedicated tb config struct.

closes https://github.com/official-stockfish/Stockfish/pull/4982

No functional change
2024-01-17 18:05:00 +01:00
mstembera 32e46fc47f Remove some outdated SIMD functions
Since https://github.com/official-stockfish/Stockfish/pull/4391 the x2
SIMD functions no longer serve any useful purpose.

Passed non-regression STC:
https://tests.stockfishchess.org/tests/view/659cf42579aa8af82b966d55
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 67392 W: 17222 L: 17037 D: 33133
Ptnml(0-2): 207, 7668, 17762, 7851, 208

closes https://github.com/official-stockfish/Stockfish/pull/4974

No functional change
2024-01-17 18:04:29 +01:00
Disservin b5e8169a85 Add ignoreRevsFile to CONTRIBUTING.md
closes https://github.com/official-stockfish/Stockfish/pull/4980

No functional change
2024-01-14 10:46:13 +01:00
Disservin 88331add0d Remove the dependency on a Worker from evaluate
Also remove dead code, `rootSimpleEval` is no longer used since the introduction of dual net.
`iterBestValue` is also no longer used in evaluate and can be reduced to a local variable.

closes https://github.com/official-stockfish/Stockfish/pull/4979

No functional change
2024-01-14 10:46:13 +01:00
Disservin 12e97701b2 Fix UCI options
Fixes the type for 'Clear Hash' and uses MAX_MOVES for 'MultiPV' as we
had before.

No functional change
2024-01-14 10:46:13 +01:00
Disservin cf5b070913 Remove unused method
init() is no longer used, and was previously replaced by the clear
function.

fixes https://github.com/official-stockfish/Stockfish/issues/4981

No functional change
2024-01-14 00:30:06 +01:00
mstembera eec361f64c Simplify bad quiets
The main difference is that instead of returning the first bad quiet as
a good one we fall through. This is actually more correct and simpler
to implement.

Non regression STC:
https://tests.stockfishchess.org/tests/view/659bbb3479aa8af82b964ec7
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 150944 W: 38399 L: 38305 D: 74240
Ptnml(0-2): 485, 18042, 38298, 18188, 459

Non regression LTC:
https://tests.stockfishchess.org/tests/view/659c6e6279aa8af82b9660eb
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 192060 W: 47871 L: 47823 D: 96366
Ptnml(0-2): 144, 21912, 51845, 22010, 119

The cutoff is now -8K instead of -7.5K.
-7.5K failed. https://tests.stockfishchess.org/tests/view/659a1f4b79aa8af82b962a0e
This was likely a false negative.

closes https://github.com/official-stockfish/Stockfish/pull/4975

Bench: 1308279
2024-01-13 19:40:53 +01:00
FauziAkram 3372ee9c26 Remove threatenedByPawn term for queen threats
Passed STC:
https://tests.stockfishchess.org/tests/view/659d614c79aa8af82b9677d0
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 151776 W: 38690 L: 38597 D: 74489
Ptnml(0-2): 522, 17841, 39015, 18042, 468

Passed LTC:
https://tests.stockfishchess.org/tests/view/659d94d379aa8af82b967cb2
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 91908 W: 23075 L: 22924 D: 45909
Ptnml(0-2): 70, 10311, 25037, 10470, 66

closes https://github.com/official-stockfish/Stockfish/pull/4977

Bench: 1266493
2024-01-13 19:40:53 +01:00
Disservin a107910951 Refactor global variables
This aims to remove some of the annoying global structure which Stockfish has.

Overall there is no major elo regression to be expected.

Non regression SMP STC (paused, early version):
https://tests.stockfishchess.org/tests/view/65983d7979aa8af82b9608f1
LLR: 0.23 (-2.94,2.94) <-1.75,0.25>
Total: 76232 W: 19035 L: 19096 D: 38101
Ptnml(0-2): 92, 8735, 20515, 8690, 84

Non regression STC (early version):
https://tests.stockfishchess.org/tests/view/6595b3a479aa8af82b95da7f
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 185344 W: 47027 L: 46972 D: 91345
Ptnml(0-2): 571, 21285, 48943, 21264, 609

Non regression SMP STC:
https://tests.stockfishchess.org/tests/view/65a0715c79aa8af82b96b7e4
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 142936 W: 35761 L: 35662 D: 71513
Ptnml(0-2): 209, 16400, 38135, 16531, 193

These global structures/variables add hidden dependencies and allow data
to be mutable from where it shouldn't it be (i.e. options). They also
prevent Stockfish from internal selfplay, which would be a nice thing to
be able to do, i.e. instantiate two Stockfish instances and let them
play against each other. It will also allow us to make Stockfish a
library, which can be easier used on other platforms.

For consistency with the old search code, `thisThread` has been kept,
even though it is not strictly necessary anymore. This the first major
refactor of this kind (in recent time), and future changes are required,
to achieve the previously described goals. This includes cleaning up the
dependencies, transforming the network to be self contained and coming
up with a plan to deal with proper tablebase memory management (see
comments for more information on this).

The removal of these global structures has been discussed in parts with
Vondele and Sopel.

closes https://github.com/official-stockfish/Stockfish/pull/4968

No functional change
2024-01-13 19:40:53 +01:00
Linmiao Xu 6deb88728f Update default main net to nn-baff1edbea57.nnue
Created by retraining the previous main net nn-b1e55edbea57.nnue with:
- some of the same options as before: ranger21 optimizer, more WDL
  skipping
- adding T80 aug filter-v6, sep, and oct 2023 data to the previous best
  dataset
- increasing training loss for positions where predicted win rates were
  higher than estimated match results from training data position scores

```yaml
experiment-name: 2560--S8-r21-more-wdl-skip-10p-more-loss-high-q-sk28

training-dataset:
  # https://github.com/official-stockfish/Stockfish/pull/4782
  - /data/S6-1ee1aba5ed.binpack
  - /data/test80-aug2023-2tb7p.v6.min.binpack
  - /data/test80-sep2023-2tb7p.binpack
  - /data/test80-oct2023-2tb7p.binpack
early-fen-skipping: 28

start-from-engine-test-net: True
nnue-pytorch-branch: linrock/nnue-pytorch/r21-more-wdl-skip-10p-more-loss-high-q

num-epochs: 1000
lr: 4.375e-4
gamma: 0.995
start-lambda: 1.0
end-lambda: 0.7
```

Training data can be found at:
https://robotmoon.com/nnue-training-data/

Training loss was increased by 10% for positions where predicted win
rates were higher than suggested by the win rate model based on the
training data, by multiplying with: ((qf > pt) * 0.1 + 1). This was a
variant of experiments from Sopel's NNUE training & experimentation log:
https://docs.google.com/document/d/1gTlrr02qSNKiXNZ_SuO4-RjK4MXBiFlLE6jvNqqMkAY
Experiment 302 - increase loss when prediction too high, vondele’s idea
Experiment 309 - increase loss when prediction too high, normalize in a
batch

Passed STC:
https://tests.stockfishchess.org/tests/view/6597a21c79aa8af82b95fd5c
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 148320 W: 37960 L: 37475 D: 72885
Ptnml(0-2): 542, 17565, 37383, 18206, 464

Passed LTC:
https://tests.stockfishchess.org/tests/view/659834a679aa8af82b960845
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 55188 W: 13955 L: 13592 D: 27641
Ptnml(0-2): 34, 6162, 14834, 6535, 29

closes https://github.com/official-stockfish/Stockfish/pull/4972

Bench: 1219824
2024-01-08 18:34:36 +01:00
Disservin 99cdb920fc Cleanup Evalfile handling
This cleans up the EvalFile handling after the merge of #4915,
which has become a bit confusing on what it is actually doing.

closes https://github.com/official-stockfish/Stockfish/pull/4971

No functional change
2024-01-08 18:33:38 +01:00
Disservin 7c5e3f2865 Prefix abs with std:: 2024-01-07 21:41:52 +01:00
Linmiao Xu f09adaa4a4 Update smallnet to nn-baff1ede1f90.nnue with wider eval range
Created by training an L1-128 net from scratch with a wider range of
evals in the training data and wld-fen-skipping disabled during
training. The differences in this training data compared to the first
dual nnue PR are:

- removal of all positions with 3 pieces
- when piece count >= 16, keep positions with simple eval above 750
- when piece count < 16, remove positions with simple eval above 3000

The asymmetric data filtering was meant to flatten the training data
piece count distribution, which was previously heavily skewed towards
positions with low piece counts.

Additionally, the simple eval range where the smallnet is used was
widened to cover more positions previously evaluated by the big net and
simple eval.

```yaml
experiment-name: 128--S1-hse-S7-v4-S3-v1-no-wld-skip

training-dataset:
  - /data/hse/S3/leela96-filt-v2.min.high-simple-eval-1k.binpack
  - /data/hse/S3/dfrc99-16tb7p-eval-filt-v2.min.high-simple-eval-1k.binpack
  - /data/hse/S3/test80-apr2022-16tb7p.min.high-simple-eval-1k.binpack

  - /data/hse/S7/test60-2020-2tb7p.v6-3072.high-simple-eval-v4.binpack
  - /data/hse/S7/test60-novdec2021-12tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack

  - /data/hse/S7/test77-nov2021-2tb7p.v6-3072.min.high-simple-eval-v4.binpack
  - /data/hse/S7/test77-dec2021-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack
  - /data/hse/S7/test77-jan2022-2tb7p.high-simple-eval-v4.binpack

  - /data/hse/S7/test78-jantomay2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack
  - /data/hse/S7/test78-juntosep2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack

  - /data/hse/S7/test79-apr2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack
  - /data/hse/S7/test79-may2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack

  - /data/hse/S7/test80-may2022-16tb7p.high-simple-eval-v4.binpack
  - /data/hse/S7/test80-jun2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack
  - /data/hse/S7/test80-jul2022-16tb7p.v6-dd.min.high-simple-eval-v4.binpack
  - /data/hse/S7/test80-aug2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack
  - /data/hse/S7/test80-sep2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack
  - /data/hse/S7/test80-oct2022-16tb7p.v6-dd.high-simple-eval-v4.binpack
  - /data/hse/S7/test80-nov2022-16tb7p-v6-dd.min.high-simple-eval-v4.binpack

  - /data/hse/S7/test80-jan2023-3of3-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack
  - /data/hse/S7/test80-feb2023-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack
  - /data/hse/S7/test80-mar2023-2tb7p.v6-sk16.min.high-simple-eval-v4.binpack
  - /data/hse/S7/test80-apr2023-2tb7p-filter-v6-sk16.min.high-simple-eval-v4.binpack
  - /data/hse/S7/test80-may2023-2tb7p.v6.min.high-simple-eval-v4.binpack
  - /data/hse/S7/test80-jun2023-2tb7p.v6-3072.min.high-simple-eval-v4.binpack
  - /data/hse/S7/test80-jul2023-2tb7p.v6-3072.min.high-simple-eval-v4.binpack
  - /data/hse/S7/test80-aug2023-2tb7p.v6.min.high-simple-eval-v4.binpack
  - /data/hse/S7/test80-sep2023-2tb7p.high-simple-eval-v4.binpack
  - /data/hse/S7/test80-oct2023-2tb7p.high-simple-eval-v4.binpack

wld-fen-skipping: False
start-from-engine-test-net: False

nnue-pytorch-branch: linrock/nnue-pytorch/L1-128
engine-test-branch: linrock/Stockfish/L1-128-nolazy
engine-base-branch: linrock/Stockfish/L1-128

num-epochs: 500
start-lambda: 1.0
end-lambda: 1.0
```

Experiment yaml configs converted to easy_train.sh commands with:
https://github.com/linrock/nnue-tools/blob/4339954/yaml_easy_train.py

Binpacks interleaved at training time with:
https://github.com/official-stockfish/nnue-pytorch/pull/259

FT weights permuted with 10k positions from fishpack32.binpack with:
https://github.com/official-stockfish/nnue-pytorch/pull/254

Data filtered for high simple eval positions (v4) with:
https://github.com/linrock/Stockfish/blob/b9c8440/src/tools/transform.cpp#L640-L675

Training data can be found at:
https://robotmoon.com/nnue-training-data/

Local elo at 25k nodes per move of
L1-128 smallnet (nnue-only eval) vs. L1-128 trained on standard S1 data:
nn-epoch319.nnue : -241.7 +/- 3.2

Passed STC vs. 36db936:
https://tests.stockfishchess.org/tests/view/6576b3484d789acf40aabbfe
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 21920 W: 5680 L: 5381 D: 10859
Ptnml(0-2): 82, 2488, 5520, 2789, 81

Passed LTC vs. DualNNUE #4915:
https://tests.stockfishchess.org/tests/view/65775c034d789acf40aac7e3
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 147606 W: 36619 L: 36063 D: 74924
Ptnml(0-2): 98, 16591, 39891, 17103, 120

closes https://github.com/official-stockfish/Stockfish/pull/4919

Bench: 1438336
2024-01-07 21:20:15 +01:00
Linmiao Xu 584d9efedc Dual NNUE with L1-128 smallnet
Credit goes to @mstembera for:
- writing the code enabling dual NNUE:
  https://github.com/official-stockfish/Stockfish/pull/4898
- the idea of trying L1-128 trained exclusively on high simple eval
  positions

The L1-128 smallnet is:
- epoch 399 of a single-stage training from scratch
- trained only on positions from filtered data with high material
  difference
  - defined by abs(simple_eval) > 1000

```yaml
experiment-name: 128--S1-only-hse-v2

training-dataset:
  - /data/hse/S3/dfrc99-16tb7p-eval-filt-v2.min.high-simple-eval-1k.binpack
  - /data/hse/S3/leela96-filt-v2.min.high-simple-eval-1k.binpack
  - /data/hse/S3/test80-apr2022-16tb7p.min.high-simple-eval-1k.binpack

  - /data/hse/S7/test60-2020-2tb7p.v6-3072.high-simple-eval-1k.binpack
  - /data/hse/S7/test60-novdec2021-12tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack

  - /data/hse/S7/test77-nov2021-2tb7p.v6-3072.min.high-simple-eval-1k.binpack
  - /data/hse/S7/test77-dec2021-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack
  - /data/hse/S7/test77-jan2022-2tb7p.high-simple-eval-1k.binpack

  - /data/hse/S7/test78-jantomay2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack
  - /data/hse/S7/test78-juntosep2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack

  - /data/hse/S7/test79-apr2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack
  - /data/hse/S7/test79-may2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack

  # T80 2022
  - /data/hse/S7/test80-may2022-16tb7p.high-simple-eval-1k.binpack
  - /data/hse/S7/test80-jun2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack
  - /data/hse/S7/test80-jul2022-16tb7p.v6-dd.min.high-simple-eval-1k.binpack
  - /data/hse/S7/test80-aug2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack
  - /data/hse/S7/test80-sep2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack
  - /data/hse/S7/test80-oct2022-16tb7p.v6-dd.high-simple-eval-1k.binpack
  - /data/hse/S7/test80-nov2022-16tb7p-v6-dd.min.high-simple-eval-1k.binpack

  # T80 2023
  - /data/hse/S7/test80-jan2023-3of3-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack
  - /data/hse/S7/test80-feb2023-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack
  - /data/hse/S7/test80-mar2023-2tb7p.v6-sk16.min.high-simple-eval-1k.binpack
  - /data/hse/S7/test80-apr2023-2tb7p-filter-v6-sk16.min.high-simple-eval-1k.binpack
  - /data/hse/S7/test80-may2023-2tb7p.v6.min.high-simple-eval-1k.binpack
  - /data/hse/S7/test80-jun2023-2tb7p.v6-3072.min.high-simple-eval-1k.binpack
  - /data/hse/S7/test80-jul2023-2tb7p.v6-3072.min.high-simple-eval-1k.binpack
  - /data/hse/S7/test80-aug2023-2tb7p.v6.min.high-simple-eval-1k.binpack
  - /data/hse/S7/test80-sep2023-2tb7p.high-simple-eval-1k.binpack
  - /data/hse/S7/test80-oct2023-2tb7p.high-simple-eval-1k.binpack

start-from-engine-test-net: False

nnue-pytorch-branch: linrock/nnue-pytorch/L1-128
engine-test-branch: linrock/Stockfish/L1-128-nolazy
engine-base-branch: linrock/Stockfish/L1-128

num-epochs: 500
lambda: 1.0
```

Experiment yaml configs converted to easy_train.sh commands with:
https://github.com/linrock/nnue-tools/blob/4339954/yaml_easy_train.py

Binpacks interleaved at training time with:
https://github.com/official-stockfish/nnue-pytorch/pull/259

Data filtered for high simple eval positions with:
https://github.com/linrock/nnue-data/blob/32d6a68/filter_high_simple_eval_plain.py
https://github.com/linrock/Stockfish/blob/61dbfe/src/tools/transform.cpp#L626-L655

Training data can be found at:
https://robotmoon.com/nnue-training-data/

Local elo at 25k nodes per move of
L1-128 smallnet (nnue-only eval) vs. L1-128 trained on standard S1 data:
nn-epoch399.nnue : -318.1 +/- 2.1

Passed STC:
https://tests.stockfishchess.org/tests/view/6574cb9d95ea6ba1fcd49e3b
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 62432 W: 15875 L: 15521 D: 31036
Ptnml(0-2): 177, 7331, 15872, 7633, 203

Passed LTC:
https://tests.stockfishchess.org/tests/view/6575da2d4d789acf40aaac6e
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 64830 W: 16118 L: 15738 D: 32974
Ptnml(0-2): 43, 7129, 17697, 7497, 49

closes https://github.com/official-stockfish/Stockfish/pulls

Bench: 1330050

Co-Authored-By: mstembera <5421953+mstembera@users.noreply.github.com>
2024-01-07 21:15:52 +01:00
mstembera a5a76a6370 Introduce BAD_QUIET movepicker stage
Split quiets into good and bad as we do with captures. When we find
the first quiet move below a certain threshold that has been sorted we
consider all subsequent quiets bad.  Inspired by @locutus2 idea to skip
bad captures.

Passed STC:
https://tests.stockfishchess.org/tests/view/6597759f79aa8af82b95fa17
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 138688 W: 35566 L: 35096 D: 68026
Ptnml(0-2): 476, 16367, 35183, 16847, 471

Passed LTC:
https://tests.stockfishchess.org/tests/view/6598583c79aa8af82b960ad0
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 84108 W: 21468 L: 21048 D: 41592
Ptnml(0-2): 38, 9355, 22858, 9755, 48

closes https://github.com/official-stockfish/Stockfish/pull/4970

Bench: 1336907
2024-01-07 13:41:50 +01:00
Disservin 19f9a197be Add .git-blame-ignore-revs
Add a `.git-blame-ignore-revs` file which can be used to skip specified
commits when blaming, this is useful to ignore formatting commits, like
clang-format #4790.

Github blame automatically supports this file format, as well as other
third party tools. Git itself needs to be told about the file name to
work, the following command will add it to the current git repo. `git
config blame.ignoreRevsFile .git-blame-ignore-revs`, alternatively one
has to specify it with every blame. `git blame --ignore-revs-file
.git-blame-ignore-revs search.cpp`

Supported since git 2.23.

closes https://github.com/official-stockfish/Stockfish/pull/4969

No functional change
2024-01-07 13:38:55 +01:00
Michael Chaly 6f9071c643 Tweak usage of correction history
Instead of using linear formula use quadratic one. Maximum impact of
correction history is doubled this way, it breaks even with previous
formula on half of maximum value.

Passed STC:
https://tests.stockfishchess.org/tests/view/659591e579aa8af82b95d7e8
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 225216 W: 57616 L: 57019 D: 110581
Ptnml(0-2): 747, 26677, 57201, 27198, 785

Passed LTC:
https://tests.stockfishchess.org/tests/view/6596ee0b79aa8af82b95f08a
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 73314 W: 18524 L: 18125 D: 36665
Ptnml(0-2): 41, 8159, 19875, 8524, 58

closes https://github.com/official-stockfish/Stockfish/pull/4967

Bench: 1464785
2024-01-07 13:37:28 +01:00
Miguel Lahoz a5f7386efb Remove unneeded operator overload macros
Only Direction type is using two of the enable overload macros.
Aside from this, only two of the overloads are even being used.

Therefore, we can just define the needed overloads and remove the macros.

closes https://github.com/official-stockfish/Stockfish/pull/4966

No functional change.
2024-01-07 13:37:12 +01:00
FauziAkram 8b4583bce7 Remove redundant int cast
Remove a redundant int cast in the calculation of fwdOut. The variable
OutputType is already defined as std::int32_t, which is an integer type, making
the cast unnecessary.

closes https://github.com/official-stockfish/Stockfish/pull/4961

No functional change
2024-01-04 15:56:53 +01:00
Disservin b987d4f033 Use type aliases instead of enums for Value types
The primary rationale behind this lies in the fact that enums were not
originally designed to be employed in the manner we currently utilize them.

The Value enum was used like a type alias throughout the code and was often
misused. Furthermore, changing the underlying size of the enum to int16_t broke
everything, mostly because of the operator overloads for the Value enum, were
causing data to be truncated. Since Value is now a type alias, the operator
overloads are no longer required.

Passed Non-Regression STC:
https://tests.stockfishchess.org/tests/view/6593b8bb79aa8af82b95b401
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 235296 W: 59919 L: 59917 D: 115460
Ptnml(0-2): 743, 27085, 62054, 26959, 807

closes https://github.com/official-stockfish/Stockfish/pull/4960

No functional change
2024-01-04 15:54:23 +01:00
RainRat 4930892985 Fix typo in tbprobe.cpp
closes https://github.com/official-stockfish/Stockfish/pull/4959

No functional change
2024-01-04 15:51:56 +01:00
Disservin cafbe8e8e8 Change the Move enum to a class
This changes the Move enum to a class, this way
all move related functions can be moved into the class
and be more self contained.

closes https://github.com/official-stockfish/Stockfish/pull/4958

No functional change
2024-01-04 15:51:04 +01:00
Viren6 28f8663f39 Modify ttPV reduction
This patch modifies ttPV reduction by reducing 1 more unless ttValue is above alpha.

Inspired from @pb00068 https://tests.stockfishchess.org/tests/view/658060796a3b4f6202215f1f

Passed STC:
https://tests.stockfishchess.org/tests/view/6591867679aa8af82b958328
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 37856 W: 9727 L: 9407 D: 18722
Ptnml(0-2): 99, 4444, 9568, 4672, 145

Passed LTC:
https://tests.stockfishchess.org/tests/view/6591d9b679aa8af82b958a6c
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 128256 W: 32152 L: 31639 D: 64465
Ptnml(0-2): 64, 14364, 34772, 14851, 77

closes https://github.com/official-stockfish/Stockfish/pull/4957

Bench: 1176235
2024-01-04 15:49:33 +01:00
FauziAkram 5546bc0a26 Simplification of partial_insertion_sort formula.
Passed STC:
https://tests.stockfishchess.org/tests/view/6590110879aa8af82b9562e9
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 134880 W: 34468 L: 34355 D: 66057
Ptnml(0-2): 476, 16060, 34220, 16243, 441

Passed LTC:
https://tests.stockfishchess.org/tests/view/659156ca79aa8af82b957f07
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 60780 W: 15179 L: 14996 D: 30605
Ptnml(0-2): 27, 6847, 16464, 7020, 32

closes https://github.com/official-stockfish/Stockfish/pull/4955

Bench: 1338331
2024-01-04 15:47:37 +01:00
Disservin 444f03ee95 Update copyright year
closes https://github.com/official-stockfish/Stockfish/pull/4954

No functional change
2024-01-04 15:47:10 +01:00
Disservin a25f48a236 Silence security alert warning about possible infinite loop
As some have noticed, a security alert has been complaining about a for loop in
our TB code for quite some now. Though it was never a real issue, so not of high
importance.

A few lines earlier the symlen vector is resized
`d->symlen.resize(number<uint16_t, LittleEndian>(data));` while this code seems
odd at first, it resizes the array to at most (2 << 16) - 1 elements, basically
making the infinite loop issue impossible to occur.

closes https://github.com/official-stockfish/Stockfish/pull/4953

No functional change
2024-01-04 15:45:33 +01:00
Joseph Huang 154abb337e Lower MultiPV max to MAX_MOVES
Link max value of MultiPV to that of MAX_MOVES which is 256

closes https://github.com/official-stockfish/Stockfish/pull/4951

No functional change
2024-01-04 15:45:03 +01:00
Disservin 0fca5605fa Fix formatting in search.cpp
fixes the formatting for 1fe562fdf3
2024-01-01 02:31:25 +01:00
Stefan Geschwentner 3cfaef7431 Tweak static eval history update
Modify the applied static eval bonus for main and pawn history with different
factors for positive and negative values.

Passed STC:
https://tests.stockfishchess.org/tests/view/659132e179aa8af82b957bb0
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 12512 W: 3308 L: 3027 D: 6177
Ptnml(0-2): 32, 1372, 3189, 1609, 54

Passed LTC:
https://tests.stockfishchess.org/tests/view/65913e3d79aa8af82b957cd2
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 35946 W: 9128 L: 8809 D: 18009
Ptnml(0-2): 19, 3879, 9862, 4190, 23

closes https://github.com/official-stockfish/Stockfish/pull/4952

Bench: 1392883
2023-12-31 20:09:09 +01:00
Michael Chaly b4d995d0d9 Introduce static evaluation correction history
Idea from Caissa (https://github.com/Witek902/Caissa) chess engine.

With given pawn structure collect data with how often search result and by how
much it was better / worse than static evalution of position and use it to
adjust static evaluation of positions with given pawn structure. Details:

1. excludes positions with fail highs and moves producing it being a capture;
2. update value is function of not only difference between best value and static
   evaluation but also is multiplied by linear function of depth;
3. maximum update value is maximum value of correction history divided by 2;
4. correction history itself is divided by 32 when applied so maximum value of
   static evaluation adjustment is 32 internal units.

Passed STC:
https://tests.stockfishchess.org/tests/view/658fc7b679aa8af82b955cac
LLR: 2.96 (-2.94,2.94) <0.00,2.00>
Total: 128672 W: 32757 L: 32299 D: 63616
Ptnml(0-2): 441, 15241, 32543, 15641, 470

Passed LTC:
https://tests.stockfishchess.org/tests/view/65903f6979aa8af82b9566f1
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 97422 W: 24626 L: 24178 D: 48618
Ptnml(0-2): 41, 10837, 26527, 11245, 61

closes https://github.com/official-stockfish/Stockfish/pull/4950

Bench: 1157852
2023-12-31 20:00:06 +01:00
FauziAkram 4ff297a6df Mark square_bb() as constexpr
closes https://github.com/official-stockfish/Stockfish/pull/4949

No functional change
2023-12-31 19:58:10 +01:00
FauziAkram 1fe562fdf3 Simplify the improving flag calculation
Passed STC:
https://tests.stockfishchess.org/tests/view/658ec29979aa8af82b9547f6
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 93408 W: 23747 L: 23587 D: 46074
Ptnml(0-2): 340, 11178, 23527, 11300, 359

Passed LTC:
https://tests.stockfishchess.org/tests/view/658f73e479aa8af82b9555b6
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 64026 W: 15984 L: 15806 D: 32236
Ptnml(0-2): 31, 7113, 17552, 7281, 36

closes https://github.com/official-stockfish/Stockfish/pull/4948

Bench: 1143749
2023-12-31 19:57:34 +01:00
FauziAkram 833a2e2bc0 Cleanup comments
Tests used to derive some Elo worth comments:
https://tests.stockfishchess.org/tests/view/656a7f4e136acbc573555a31
https://tests.stockfishchess.org/tests/view/6585fb455457644dc984620f

closes https://github.com/official-stockfish/Stockfish/pull/4945

No functional change
2023-12-31 19:54:27 +01:00
Tobias Steinmann 4f99dfcae2 Update Makefile for android x86-64 builds
For developing an Android GUI it can be helpful to use the Emulator on Windows.
Therefor an android_x86-64 library of Stockfish is needed. It would be nice to
compile it "out-of-the-box".

This change is originally suggested by Craftyawesome

closes https://github.com/official-stockfish/Stockfish/pull/4927

No functional change
2023-12-31 19:51:04 +01:00
Shahin M. Shahin 1a69efbb40 Fix scores from reverse futility pruning
This fixes futility pruning return values after recent tweaks, `eval` is
guaranteed to be less than the mate-in range but it can be as low value such
that the average between eval and beta can still fall in the mated-in range when
beta is as low in mated range. i.e. (eval + beta) / 2 being at mated-range which
can break mates.

Passed non-regression STC:
https://tests.stockfishchess.org/tests/view/658f3eed79aa8af82b955139
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 117408 W: 29891 L: 29761 D: 57756
Ptnml(0-2): 386, 13355, 31120, 13429, 414

Passed non-regression LTC:
https://tests.stockfishchess.org/tests/view/658f8b7a79aa8af82b9557bd
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 60240 W: 14962 L: 14786 D: 30492
Ptnml(0-2): 22, 6257, 17390, 6425, 26

changes signature at higher depth e.g. `128 1 15`

closes https://github.com/official-stockfish/Stockfish/pull/4944

Bench: 1304666
2023-12-30 12:19:48 +01:00
Linmiao Xu f12035c88c Update default net to nn-b1e55edbea57.nnue
Created by retraining the master big net `nn-0000000000a0.nnue` on the same
dataset with the ranger21 optimizer and more WDL skipping at training time.

More WDL skipping is meant to increase lambda accuracy and train on fewer
misevaluated positions where position scores are unlikely to correlate
with game outcomes. Inspired by:
- repeated reports in discord #events-discuss about SF misplaying due to wrong endgame
  evals, possibly due to Leela's endgame weaknesses reflected in training data
- an attempt to reduce the skewed dataset piece count distribution where there
  are much more positions with less than 16 pieces, since the target piece count
  distribution in the trainer is symmetric around 16

The faster convergence seen with ranger21 is meant to:
- prune experiment ideas more quickly since fewer epochs are needed to reach elo maxima
- research faster potential trainings by shortening each run

```yaml
experiment-name: 2560-S7-Re-514G-ranger21-more-wdl-skip
training-dataset: /data/S6-514G.binpack
early-fen-skipping: 28

start-from-engine-test-net: True
nnue-pytorch-branch: linrock/nnue-pytorch/r21-more-wdl-skip

num-epochs: 1200
lr: 4.375e-4
gamma: 0.995
start-lambda: 1.0
end-lambda: 0.7
```

Experiment yaml configs converted to easy_train.sh commands with:
https://github.com/linrock/nnue-tools/blob/4339954/yaml_easy_train.py

Implementations based off of Sopel's NNUE training & experimentation log:
https://docs.google.com/document/d/1gTlrr02qSNKiXNZ_SuO4-RjK4MXBiFlLE6jvNqqMkAY
- Experiment 336 - ranger21 https://github.com/Sopel97/nnue-pytorch/tree/experiment_336
- Experiment 351 - more WDL skipping

The version of the ranger21 optimizer used is:
https://github.com/lessw2020/Ranger21/blob/b507df6/ranger21/ranger21.py

The dataset is the exact same as in:
https://github.com/official-stockfish/Stockfish/pull/4782

Local elo at 25k nodes per move:
nn-epoch619.nnue : 6.2 +/- 4.2

Passed STC:
https://tests.stockfishchess.org/tests/view/658a029779aa8af82b94fbe6
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 46528 W: 11985 L: 11650 D: 22893
Ptnml(0-2): 154, 5489, 11688, 5734, 199

Passed LTC:
https://tests.stockfishchess.org/tests/view/658a448979aa8af82b95010f
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 265326 W: 66378 L: 65574 D: 133374
Ptnml(0-2): 153, 30175, 71254, 30877, 204

This was additionally tested with the latest DualNNUE and passed SPRTs:

Passed STC vs. https://github.com/official-stockfish/Stockfish/pull/4919
https://tests.stockfishchess.org/tests/view/658bcd5c79aa8af82b951846
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 296128 W: 76273 L: 75554 D: 144301
Ptnml(0-2): 1223, 35768, 73617, 35979, 1477

Passed LTC vs. https://github.com/official-stockfish/Stockfish/pull/4919
https://tests.stockfishchess.org/tests/view/658c988d79aa8af82b95240f
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 75618 W: 19085 L: 18680 D: 37853
Ptnml(0-2): 45, 8420, 20497, 8779, 68

closes https://github.com/official-stockfish/Stockfish/pull/4942

Bench: 1304666
2023-12-30 11:08:03 +01:00
FauziAkram bab1cc300c Refactor bestvalue adjustment in qsearch
closes https://github.com/official-stockfish/Stockfish/pull/4935

No functional change
2023-12-30 11:05:19 +01:00
Michael Chaly f388e41809 Adjust value returned after TT cutoff
Instead of returning value from TT in case of a fail high return mix between it
and beta.

Passed STC:
https://tests.stockfishchess.org/tests/view/658465395457644dc98446c7
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 220704 W: 56404 L: 55811 D: 108489
Ptnml(0-2): 750, 26214, 55921, 26627, 840

Passed LTC:
https://tests.stockfishchess.org/tests/view/6585c3f55457644dc9845db9
LLR: 2.97 (-2.94,2.94) <0.50,2.50>
Total: 124980 W: 31169 L: 30658 D: 63153
Ptnml(0-2): 57, 14147, 33603, 14594, 89

closes https://github.com/official-stockfish/Stockfish/pull/4934

Bench: 1191093
2023-12-30 11:01:32 +01:00
peregrineshahin 3f5adc037e Fix wrong mate/tb scores from probCut
This fixes returning wrong mated-in scores, or losing a proven mate-in score
from probCut after recent tweaks. The issue reported by @cj5716 on discord.

Passed non-reg STC:
https://tests.stockfishchess.org/tests/view/6583c36b5457644dc9843afe
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 295936 W: 75011 L: 75075 D: 145850
Ptnml(0-2): 978, 33947, 78146, 33955, 942

Passed non-reg LTC:
https://tests.stockfishchess.org/tests/view/658513075457644dc98451cd
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 55932 W: 13970 L: 13786 D: 28176
Ptnml(0-2): 33, 5933, 15837, 6143, 20

closes https://github.com/official-stockfish/Stockfish/pull/4933

Bench: 1308739
2023-12-30 10:57:48 +01:00
FauziAkram fbdf5d94a9 Tweak quiet move bonus
Improving quiet move bonus by replacing bestvalue and alpha comparison, with
checking the statScore of the previous search step instead.

Inspired by @locutus2

Passed STC:
https://tests.stockfishchess.org/tests/view/657f22fb893104ee25b614e8
LLR: 2.96 (-2.94,2.94) <0.00,2.00>
Total: 51296 W: 13121 L: 12774 D: 25401
Ptnml(0-2): 225, 5986, 12868, 6355, 214

Passed LTC:
https://tests.stockfishchess.org/tests/view/658024a2893104ee25b62587
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 82758 W: 20606 L: 20189 D: 41963
Ptnml(0-2): 51, 9149, 22555, 9580, 44

closes https://github.com/official-stockfish/Stockfish/pull/4930

Bench: 1312822
2023-12-22 11:51:08 +01:00
Disservin 358a853790 Revert "Adjust stand pat in qsearch on pv nodes"
This reverts commit d9ec82e743.

Bench: 1249544
2023-12-22 11:48:43 +01:00
Michael Chaly 9be0360aa4 Adjust return value in qsearch after fail high
Instead of returning strict fail soft fail high return value between value from
search and beta (somewhat by analogy to futility pruning and probcut).

This seems to be somewhat depth sensitive heuristic which performed much worse
at LTC while performing much better at STC if it is more aggressive, passed
version is the least aggressive one.

Passed STC:
https://tests.stockfishchess.org/tests/view/657b06414d789acf40ab1475
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 212352 W: 53900 L: 53315 D: 105137
Ptnml(0-2): 809, 25236, 53520, 25783, 828

Passed LTC:
https://tests.stockfishchess.org/tests/view/657ce36f393ac02e79120a7c
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 319362 W: 79541 L: 78630 D: 161191
Ptnml(0-2): 202, 35839, 86709, 36708, 223

closes https://github.com/official-stockfish/Stockfish/pull/4928

Bench: 974739
2023-12-19 18:22:10 +01:00
FauziAkram a069a1bbbf Use std::abs over abs
closes https://github.com/official-stockfish/Stockfish/pull/4926
closes https://github.com/official-stockfish/Stockfish/pull/4909

No functional change

Co-Authored-By: fffelix-huang <72808219+fffelix-huang@users.noreply.github.com>
2023-12-19 18:22:10 +01:00
FauziAkram 07a2619b62 Improvement of Time Management Parameters
Passed STC:
https://tests.stockfishchess.org/tests/view/6579c5574d789acf40aaf914
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 44672 W: 11354 L: 11030 D: 22288
Ptnml(0-2): 140, 5033, 11685, 5319, 159

Passed LTC:
https://tests.stockfishchess.org/tests/view/657ad7f44d789acf40ab105e
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 40932 W: 10275 L: 9950 D: 20707
Ptnml(0-2): 21, 4316, 11473, 4629, 27

Passed non-regression Sudden death 10+0:
https://tests.stockfishchess.org/tests/view/657b9b9e393ac02e7911f1a8
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 21384 W: 5171 L: 4925 D: 11288
Ptnml(0-2): 112, 2420, 5409, 2612, 139

closes https://github.com/official-stockfish/Stockfish/pull/4923

No functional change
2023-12-19 18:22:10 +01:00
Michael Chaly d9ec82e743 Adjust stand pat in qsearch on pv nodes
Instead of immediately returning a fail high do this only at non-pv nodes, for
pv nodes adjust bestValue to value between alpha and beta and continue
searching. Idea is to do it the same way as it's done in search where we don't
return positive beta cutoffs after ttHits / zero window search at PvNodes and
instead fully search lines.

Passed STC:
https://tests.stockfishchess.org/tests/view/65739b0af09ce1261f122f33
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 189216 W: 48142 L: 47598 D: 93476
Ptnml(0-2): 584, 22463, 48051, 22845, 665

Passed LTC:
https://tests.stockfishchess.org/tests/view/657701214d789acf40aac194
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 82506 W: 20689 L: 20269 D: 41548
Ptnml(0-2): 56, 9236, 22268, 9618, 75

Two issues had to be resolved:
    - in rare cases it set alpha to the same value as beta and thus broke some asserts;
    - messed up with returning tb win values.

      Fix passed non-regression LTC vs this patch:
      https://tests.stockfishchess.org/tests/view/6578113b4d789acf40aad544
      LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
      Total: 277308 W: 68839 L: 68880 D: 139589
      Ptnml(0-2): 167, 31580, 75212, 31517, 178

closes https://github.com/official-stockfish/Stockfish/pull/4922

Bench: 1069503

Co-Authored-By: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com>
Co-Authored-By: Shahin M. Shahin <41402573+peregrineshahin@users.noreply.github.com>
Co-Authored-By: fffelix-huang <72808219+fffelix-huang@users.noreply.github.com>
2023-12-19 18:22:10 +01:00
Muzhen Gaming c53d2ec253 Remove UCI_AnalyseMode Option
Simplify away the useless option, as documented: "An option handled by your GUI.
This currently doesn't do anything."

The option was originally added with the introduction of contempt
(https://github.com/official-stockfish/Stockfish/commit/e9aeaad05266ca557a9496b5a17b4c5f82f0e946),
but it is now no longer used.

closes https://github.com/official-stockfish/Stockfish/pull/4918

No functional change
2023-12-14 18:50:51 +01:00
FauziAkram 536d692a30 Remove SlowMover Option
The SlowMover option allows users to modify the timeLeft variant, impacting the
engine's time management. However, this feature, while theoretically flexible,
doesn't offer substantial benefits. Instead, it introduces the risk of
non-experienced users altering values without a clear understanding of the
effects, potentially leading to a weaker engine.

The vast majority of SF users don't use it anyway, and based on tests conducted
by fauzi several months ago suggest that changing it would only lose Elo.

Examples:
https://tests.stockfishchess.org/tests/view/651f309bac57711436726bba
https://tests.stockfishchess.org/tests/view/651fea29ac57711436727d85
https://tests.stockfishchess.org/tests/view/65257c343125598fc7eb68a1
https://tests.stockfishchess.org/tests/view/652296c83125598fc7eb2ad7
Tune:
https://tests.stockfishchess.org/tests/view/652a70313125598fc7ebd706
(keeping the value at 100, zz2)

closes https://github.com/official-stockfish/Stockfish/pull/4917

No functional change
2023-12-14 18:44:46 +01:00
Disservin 9fc064e872 Fix action deprecation warning for dev-drprasad
closes https://github.com/official-stockfish/Stockfish/pull/4914

No functional change
2023-12-14 18:43:02 +01:00
WangXiang cdfafb3426 Add loongarch64 support
Adding support for LoongArch64 architecture. Tested on Loongson 3A6000 EVB
Board. Since Loongson's SIMD extended instruction set
([LSX](https://gcc.gnu.org/onlinedocs/gcc/LoongArch-SX-Vector-Intrinsics.html),
[LASX](https://gcc.gnu.org/onlinedocs/gcc/LoongArch-ASX-Vector-Intrinsics.html))
is already supported by GCC, more optimizations are being developed.

Here's the benchmark result for Loongson 3A6000 (4c8t, 2.5Ghz) without SIMD
optimizations.
```
Total time (ms) : 17903
Nodes searched  : 1244386
Nodes/second    : 69507
```

closes https://github.com/official-stockfish/Stockfish/pull/4913

No functional change
2023-12-14 18:41:53 +01:00
peregrineshahin 7885fa5bd3 Track seldepth in qsearch too
Sometimes if we count the reported PV length, it turns out to be longer than the
selective depth reported. This fixes this behavior by applying the selective
depth to qsearch since we do report PVs from it as well.

Passed non-regression STC:
https://tests.stockfishchess.org/tests/view/656cf5b66980e15f69c7499d
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 223648 W: 56372 L: 56356 D: 110920
Ptnml(0-2): 710, 25580, 59231, 25590, 713

closes https://github.com/official-stockfish/Stockfish/pull/4903

No functional change
2023-12-14 18:36:45 +01:00
Shahin M. Shahin 282e15bf75 Fix TB score output in UCI without using TB
This is a rewrite of the fix introduced for
https://github.com/official-stockfish/Stockfish/issues/4413 in
https://github.com/official-stockfish/Stockfish/pull/4591 by @windfishballad it
targets only the relevant part of this issue that returns TB scores (CP 20000)
without using TB due to the downgrading of potentially false mates from the TT
to an optimal TB score.

the difference is that it is a much clearer code that introduces a separate
TB_VALUE constant to account for a correct distance from the TB_VALUE with
MAX_PLY.

the originally posted position in the issue does not trigger the problem
anymore, so here is a new position to test:
```
position fen 3k4/8/8/8/8/8/3BN3/3K4 w - - 0 1
go infinite
```

Passed non-regression STC:
https://tests.stockfishchess.org/tests/view/65578994136acbc57353b258
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 119264 W: 29993 L: 29863 D: 59408
Ptnml(0-2): 372, 13692, 31379, 13812, 377

Passed non-regression LTC:
https://tests.stockfishchess.org/tests/view/6558323f136acbc57353c1ca
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 237834 W: 58791 L: 58792 D: 120251
Ptnml(0-2): 193, 26200, 66111, 26241, 172

fixes https://github.com/official-stockfish/Stockfish/issues/4413
closes https://github.com/official-stockfish/Stockfish/pull/4591
closes https://github.com/official-stockfish/Stockfish/pull/4882

Bench: 1305821
2023-12-14 18:35:38 +01:00
Muzhen Gaming 36db936e76 VLTC Search parameters tune
The SPSA tuning was done for 44k games at 120+1.2.
https://tests.stockfishchess.org/tests/view/656ee2a76980e15f69c7767f.

Note that the tune was originally done in combination with the recent dual NNUE
idea (see #4910).

VLTC:
https://tests.stockfishchess.org/tests/view/65731ccbf09ce1261f12246e
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 52806 W: 13069 L: 12760 D: 26977
Ptnml(0-2): 19, 5498, 15056, 5815, 15

VLTC SMP:
https://tests.stockfishchess.org/tests/view/65740ffaf09ce1261f1239ba
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 27630 W: 6934 L: 6651 D: 14045
Ptnml(0-2): 1, 2643, 8243, 2928, 0

Estimated close to neutral at LTC:
https://tests.stockfishchess.org/tests/view/6575485a8ec68176cf7d9423
Elo: -0.59 ± 1.8 (95%) LOS: 26.6%
Total: 32060 W: 7859 L: 7913 D: 16288
Ptnml(0-2): 20, 3679, 8676, 3645, 10
nElo: -1.21 ± 3.8 (95%) PairsRatio: 0.99

closes https://github.com/official-stockfish/Stockfish/pull/4912

Bench: 1283323
2023-12-10 23:23:28 +01:00
ppigazzini 8724503d9c Simplify the code to get the native flags
closes https://github.com/official-stockfish/Stockfish/pull/4908

No functional change
2023-12-10 23:18:21 +01:00
Taras Vuk 53ad6d23b0 Remove moveMalus
Passed STC:
https://tests.stockfishchess.org/tests/view/656e0bb86980e15f69c763fa
LLR: 3.15 (-2.94,2.94) <-1.75,0.25>
Total: 123008 W: 30973 L: 30831 D: 61204
Ptnml(0-2): 368, 14032, 32568, 14162, 374

closes https://github.com/official-stockfish/Stockfish/pull/4905

No functional change
2023-12-10 23:17:14 +01:00
Disservin dadff46986 Revert "Temporarily disable CI include checks"
This reverts commit 8f65953583.

closes https://github.com/official-stockfish/Stockfish/pull/4904

No functional change
2023-12-10 23:16:12 +01:00
FauziAkram 7a8bcfc229 Remove cutNode condition
cutNode condition seems to be irrelevant.

Passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 24224 W: 6206 L: 5970 D: 12048
Ptnml(0-2): 69, 2818, 6122, 3014, 89
https://tests.stockfishchess.org/tests/view/65686910136acbc5735529ec

Passed LTC:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 236538 W: 58624 L: 58622 D: 119292
Ptnml(0-2): 136, 26955, 64091, 26945, 142
https://tests.stockfishchess.org/tests/view/6568925a136acbc573552d8f

closes https://github.com/official-stockfish/Stockfish/pull/4901

Bench: 1244386
2023-12-04 11:33:58 +01:00
ppigazzini 0ff2ea6549 Update GitHub workflows
- Use the latest version of the actions
- Use commit hash for actions from little providers
- Update Intel SDE to 9.27

closes https://github.com/official-stockfish/Stockfish/pull/4900

No functional change
2023-12-04 11:27:28 +01:00
Sebastian Buchwald 8f65953583 Temporarily disable CI include checks
The include checks currently fail because of broken LLVM nightly
packages: https://github.com/llvm/llvm-project/issues/73402.

closes https://github.com/official-stockfish/Stockfish/pull/4899

No functional change
2023-12-04 11:26:09 +01:00
lonfom169 08cdbca56f Tweak return value in futility pruning
In futility pruning, return the average between eval and beta.

Passed STC:
https://tests.stockfishchess.org/tests/view/65680bb6136acbc5735521d7
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 15200 W: 3926 L: 3642 D: 7632
Ptnml(0-2): 36, 1699, 3867, 1941, 57

Passed LTC:
https://tests.stockfishchess.org/tests/view/656817fc136acbc573552304
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 200376 W: 49700 L: 49036 D: 101640
Ptnml(0-2): 110, 22584, 54137, 23246, 111

closes https://github.com/official-stockfish/Stockfish/pull/4897

Bench: 1403703
2023-12-02 11:46:44 +01:00
cj5716 15d47a2b38 Remove recaptures stage in qsearch
Simplify an old commit
https://github.com/official-stockfish/Stockfish/commit/72760c05c64d1fb2bb71c2ac54acfbeecf513b87.

Search is not stuck on the test position given
r1n1n1b1/1P1P1P1P/1N1N1N2/2RnQrRq/2pKp3/3BNQbQ/k7/4Bq2 w - - 0 1

Passed STC:
https://tests.stockfishchess.org/tests/view/6567050d136acbc573550919
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 236160 W: 59475 L: 59475 D: 117210
Ptnml(0-2): 841, 28266, 59816, 28366, 791

Passed LTC:
https://tests.stockfishchess.org/tests/view/6567d133136acbc573551c78
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 201690 W: 49630 L: 49593 D: 102467
Ptnml(0-2): 128, 23214, 54122, 23255, 126

closes https://github.com/official-stockfish/Stockfish/pull/4896

Bench: 1604361
2023-12-02 11:45:38 +01:00
Taras Vuk 85403a89ba Skip LMR for 2nd move at the root only
This patch reverts commit by Vizvezdenec:
https://github.com/official-stockfish/Stockfish/commit/27139dedac14af400f5b18e2ab50aca3f8cf0e33

Passed STC:
https://tests.stockfishchess.org/tests/view/65660b4a136acbc57354f13d
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 301952 W: 76271 L: 76344 D: 149337
Ptnml(0-2): 1053, 36293, 76348, 36238, 1044

Passed LTC:
https://tests.stockfishchess.org/tests/view/656738ab136acbc573550e39
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 25050 W: 6283 L: 6063 D: 12704
Ptnml(0-2): 10, 2756, 6775, 2972, 12

closes https://github.com/official-stockfish/Stockfish/pull/4895

Bench: 1722961
2023-12-02 11:41:31 +01:00
FauziAkram 7dc40ac643 Simplify quietMoveMalus malus
Use a simple depth instead of depth + 1 in the quietMoveMalus formula.

Passed STC:
https://tests.stockfishchess.org/tests/view/65636bf0136acbc57354b662
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 105248 W: 26680 L: 26532 D: 52036
Ptnml(0-2): 409, 12590, 26481, 12732, 412

Passed LTC:
https://tests.stockfishchess.org/tests/view/6563b5db136acbc57354bcab
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 204204 W: 50200 L: 50166 D: 103838
Ptnml(0-2): 123, 23331, 55145, 23395, 108

closes https://github.com/official-stockfish/Stockfish/pull/4893

Bench: 1717495
2023-12-02 11:40:36 +01:00
cj5716 883163395e Simplify promotion move generation
closes https://github.com/official-stockfish/Stockfish/pull/4892

No functional change
2023-12-02 11:38:18 +01:00
FauziAkram f17db4641e Simplify doDeeperSearch
Removing dependence on d simplifies the doDeeperSearch formula and eliminates a
variable that is not necessary in this context.

Passed STC:
https://tests.stockfishchess.org/tests/view/65647980136acbc57354c9f6
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 37440 W: 9558 L: 9334 D: 18548
Ptnml(0-2): 127, 4439, 9375, 4641, 138

Passed LTC:
https://tests.stockfishchess.org/tests/view/6564c3f0136acbc57354d126
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 113946 W: 27993 L: 27864 D: 58089
Ptnml(0-2): 67, 12975, 30783, 13058, 90

closes https://github.com/official-stockfish/Stockfish/pull/4888

Bench: 1427733
2023-12-02 11:35:28 +01:00
Muzhen Gaming 757ae2ff53 Simplify move history reduction
Recent VLTC search tuning has suggested that the depth limit can be increased
by a lot. This patch simplifies away the depth-based bonus from statScore
reduction, making the divisor a constant.

Passed STC:
https://tests.stockfishchess.org/tests/view/656201f5136acbc573549791
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 91520 W: 23130 L: 22967 D: 45423
Ptnml(0-2): 282, 10947, 23141, 11106, 284

Passed LTC:
https://tests.stockfishchess.org/tests/view/6562b43a136acbc57354a581
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 352902 W: 86796 L: 86917 D: 179189
Ptnml(0-2): 190, 40227, 95741, 40100, 193

closes https://github.com/official-stockfish/Stockfish/pull/4886

Bench: 1297179
2023-12-02 11:30:47 +01:00
Stefan Geschwentner 13426a93c1 Simplify history update.
Removal of the slowdown factor from the history update formula with
corresponding adjustment of the stat bonus used in the search.

Passed STC:
https://tests.stockfishchess.org/tests/view/655e1079136acbc573544744
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 128096 W: 32355 L: 32235 D: 63506
Ptnml(0-2): 466, 15187, 32573, 15405, 417

Passed LTC:
https://tests.stockfishchess.org/tests/view/655f4e60136acbc573546266
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 50652 W: 12653 L: 12459 D: 25540
Ptnml(0-2): 28, 5666, 13751, 5846, 35

closes https://github.com/official-stockfish/Stockfish/pull/4883

Bench: 1303857
2023-12-02 11:23:15 +01:00
FauziAkram b4e9ee72e3 Reformat some comments
Tests used to derive some Elo worth comments:

https://tests.stockfishchess.org/tests/view/653cf6b7cc309ae83956263a
https://tests.stockfishchess.org/tests/view/655250b7136acbc573534711
https://tests.stockfishchess.org/tests/view/65525767136acbc5735347b9
https://tests.stockfishchess.org/tests/view/65525aa1136acbc573534801

closes https://github.com/official-stockfish/Stockfish/pull/4879

No functional change
2023-11-20 19:10:38 +01:00
FauziAkram b59786e750 Remove doEvenDeeperSearch
Passed STC:
LLR: 2.98 (-2.94,2.94) <-1.75,0.25>
Total: 51040 W: 13014 L: 12804 D: 25222
Ptnml(0-2): 166, 6032, 12917, 6236, 169
https://tests.stockfishchess.org/tests/view/65525aa1136acbc573534801

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 165168 W: 40863 L: 40789 D: 83516
Ptnml(0-2): 73, 18783, 44792, 18869, 67
https://tests.stockfishchess.org/tests/view/65535af5136acbc573535c84

closes https://github.com/official-stockfish/Stockfish/pull/4880

Bench: 1477007
2023-11-20 19:00:47 +01:00
FauziAkram 504bf0e8b8 Change depth - 1 to newDepth
Replacing 'depth - 1' with 'newDepth' in the singularbeta formula
utilizes existing variables more succinctly.

closes https://github.com/official-stockfish/Stockfish/pull/4876

No functional change
2023-11-20 18:59:01 +01:00
Stefan Geschwentner 7970236e9e Fix undefined behavior in search.
We use following line to clamp the search depth in some range:
Depth d = std::clamp(newDepth - r, 1, newDepth + 1);

Through negative extension its possible that the maximum value becomes smaller than the minimum value but then the behavior is undefined (see https://en.cppreference.com/w/cpp/algorithm/clamp). So replace this line with a safe implementation.

Remark:
We have in recent master already one line where up to 3 negative extensions are possible which could trigger this undefined behavior but this can only be happen for completed depth > 24 so its not discovered by our default bench. Recent negative extension tests by @fauzi shows then this undefined behavior with wrong bench numbers.

closes https://github.com/official-stockfish/Stockfish/pull/4877

No functional change
2023-11-16 09:10:20 +01:00
Joost VandeVondele d89217766b CI updates
- updates the SDE action to v2.2
- removes the linux x86-32 builds, which were almost unused,
  and the build process under SDE started failing recently,
  possibly related to glibc update (The futex facility returned an unexpected error code.)

closes https://github.com/official-stockfish/Stockfish/pull/4875

No functional change
2023-11-16 09:01:57 +01:00
Joost VandeVondele f9d8717844 Symmetrize optimism
Removes some additional parameters, making the term more logical at the same
time.

Passed STC:
https://tests.stockfishchess.org/tests/view/6550e896136acbc5735328ed
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 271104 W: 68441 L: 68480 D: 134183
Ptnml(0-2): 827, 32590, 68816, 32433, 886

Passed LTC:
https://tests.stockfishchess.org/tests/view/65523858136acbc5735344f7
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 198954 W: 49250 L: 49211 D: 100493
Ptnml(0-2): 93, 22565, 54117, 22614, 88

closes https://github.com/official-stockfish/Stockfish/pull/4874

Bench: 1334248
2023-11-15 19:35:14 +01:00
Taras Vuk 863a1f2b4c Introduce recapture extensions
When in a PV-node this patch extends ttMove if it is a recapture and has a good
history.

Passed STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 83840 W: 21560 L: 21166 D: 41114
Ptnml(0-2): 343, 9905, 21027, 10305, 340
https://tests.stockfishchess.org/tests/view/654f4b02136acbc5735308ab

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 165318 W: 41068 L: 40476 D: 83774
Ptnml(0-2): 98, 18670, 44517, 19290, 84
https://tests.stockfishchess.org/tests/view/654fde04136acbc5735314e0

closes https://github.com/official-stockfish/Stockfish/pull/4872

Bench: 1393911
2023-11-15 19:32:59 +01:00
Linmiao Xu fbc6b27505 Simplify away optimism average score offset params
Passed non-regression STC:
https://tests.stockfishchess.org/tests/view/654abf6b136acbc57352ac4b
LLR: 2.97 (-2.94,2.94) <-1.75,0.25>
Total: 49664 W: 12687 L: 12477 D: 24500
Ptnml(0-2): 138, 5840, 12703, 5976, 175

Passed non-regression LTC:
https://tests.stockfishchess.org/tests/view/654b638e136acbc57352b961
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 347166 W: 85561 L: 85676 D: 175929
Ptnml(0-2): 206, 39569, 94150, 39450, 208

closes https://github.com/official-stockfish/Stockfish/pull/4871

bench 1257641
2023-11-11 15:26:56 +01:00
Stefan Geschwentner 80b0e37543 Double weight of pawn history for quiet move ordering.
I measured on my 1000 position bench the average additional added pawn history per depth.
This shows on average negative value with even smaller values with increaing depth.

A linear regression against depth get following formula:

-1960 - 130 * depth

For compensation add this to the used sort limit to maintain roughly the same proportion of sorted quiet moves.

Remarks:
1. using no compensation failed here https://tests.stockfishchess.org/tests/view/6547664f136acbc5735265f0
2. using only the compensation failed at LTC:
   passed STC: https://tests.stockfishchess.org/tests/view/65477457136acbc5735266f8
   failed LTC: https://tests.stockfishchess.org/tests/view/65487fc8136acbc573527d1c

STC:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 98528 W: 25109 L: 24699 D: 48720
Ptnml(0-2): 334, 11586, 25009, 12006, 329
https://tests.stockfishchess.org/tests/view/65475873136acbc5735264f7

LTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 69726 W: 17467 L: 17073 D: 35186
Ptnml(0-2): 39, 7814, 18769, 8196, 45
https://tests.stockfishchess.org/tests/view/6547e759136acbc573527071

closes https://github.com/official-stockfish/Stockfish/pull/4866

Bench: 1379422
2023-11-07 08:28:43 +01:00
Taras Vuk d0e87104aa Remove pawn history from ProbCut constructor
use same style as other history tables

Passed STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 184672 W: 46953 L: 46896 D: 90823
Ptnml(0-2): 604, 21095, 48887, 21140, 610
https://tests.stockfishchess.org/tests/view/654766b4136acbc573526602

closes https://github.com/official-stockfish/Stockfish/pull/4865

No functional change
2023-11-07 08:23:11 +01:00
Taras Vuk 442c294a07 Use stat_malus when decreasing stats
This patch applies stat_bonus when increasing and stat_malus when decreasing stats.

Passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 133792 W: 34221 L: 33758 D: 65813
Ptnml(0-2): 477, 15764, 33959, 16211, 485
https://tests.stockfishchess.org/tests/view/654699f3136acbc5735256b2

Passed LTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 67374 W: 16912 L: 16523 D: 33939
Ptnml(0-2): 42, 7528, 18171, 7891, 55
https://tests.stockfishchess.org/tests/view/65474558136acbc5735263ab

closes https://github.com/official-stockfish/Stockfish/pull/4864

bench: 1114417
2023-11-05 19:54:59 +01:00
FauziAkram d4b46ea6db Set reduction to 0 if the move is a TT move
The reduction formula currently decreases by 1 if the move is a TT move.
This changes this by just setting the reduction to 0 instead.

Passed STC:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 83136 W: 21145 L: 20758 D: 41233
Ptnml(0-2): 279, 9772, 21090, 10137, 290
https://tests.stockfishchess.org/tests/view/653c0fbacc309ae839561584

Passed LTC:
LLR: 2.96 (-2.94,2.94) <0.50,2.50>
Total: 273150 W: 67987 L: 67171 D: 137992
Ptnml(0-2): 155, 30730, 73966, 31592, 132
https://tests.stockfishchess.org/tests/view/653d9d02cc309ae839562fdf

closes https://github.com/official-stockfish/Stockfish/pull/4863

bench: 1110556
2023-11-05 19:53:15 +01:00
Shahin M. Shahin 791419aab5 Enhance some comments
This documents some code and makes some hard code easier to understand for new developers.

closes https://github.com/official-stockfish/Stockfish/pull/4862

No functional change
2023-11-05 19:51:02 +01:00
FauziAkram 7f97ba775e Tweaking the futility pruning formula
Huge credit goes also to candirufish,
as the idea was first tried by him, and then tuned by me at multiple phases.

Tweaking the futility pruning formula to be a bit more selective about when pruning is applied.
Adjust the value added to the static eval based on the bestValue relative to ss->staticEval. If bestValue is significantly lower, we add a larger value.

Passed STC:
LLR: 2.98 (-2.94,2.94) <0.00,2.00>
Total: 37120 W: 9590 L: 9266 D: 18264
Ptnml(0-2): 130, 4301, 9385, 4603, 141
https://tests.stockfishchess.org/tests/view/6544cf90136acbc573523247

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 49632 W: 12381 L: 12033 D: 25218
Ptnml(0-2): 30, 5429, 13549, 5779, 29
https://tests.stockfishchess.org/tests/view/65453bc1136acbc573523a3c

closes https://github.com/official-stockfish/Stockfish/pull/4861

bench: 1107118
2023-11-04 17:34:35 +01:00
Michael Chaly b4b704e686 Update pawn history based on static eval difference
Use the same logic as in main history but with 1/4 multiplier.

Passed STC:
https://tests.stockfishchess.org/tests/view/653c1282cc309ae8395615bf
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 306624 W: 77811 L: 77094 D: 151719
Ptnml(0-2): 975, 36411, 77830, 37114, 982

Passed LTC:
https://tests.stockfishchess.org/tests/view/654258e2cc309ae83956818d
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 99150 W: 24681 L: 24228 D: 50241
Ptnml(0-2): 56, 11107, 26792, 11568, 52

closes https://github.com/official-stockfish/Stockfish/pull/4859

bench 1330590
2023-11-03 22:50:05 +01:00
Stefan Geschwentner 1cb9afbdc0 Remove razoring history update.
The recently commit 'Rewarding Quiet Moves that Enable Razoring' add a history update if razoring. But its contains also many tuned values all over the search. Following tests shows that the tuned values and not the added history update is responsible for the elo gain. So remove later.

Passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 29376 W: 7641 L: 7410 D: 14325
Ptnml(0-2): 100, 3411, 7451, 3610, 116
https://tests.stockfishchess.org/tests/view/653c9fe1cc309ae839562070

Passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 242922 W: 59879 L: 59885 D: 123158
Ptnml(0-2): 129, 27764, 65688, 27744, 136
https://tests.stockfishchess.org/tests/view/653d06cbcc309ae839562735

closes https://github.com/official-stockfish/Stockfish/pull/4858

Bench: 1286104
2023-11-03 22:47:31 +01:00
FauziAkram 101d2bb8ea Simplifying two formulas
by eliminating two multiplication operations.

Passed STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 60000 W: 15193 L: 14996 D: 29811
Ptnml(0-2): 199, 7100, 15215, 7277, 209
https://tests.stockfishchess.org/tests/view/653beb69cc309ae83956129d

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 122910 W: 30471 L: 30353 D: 62086
Ptnml(0-2): 68, 13961, 33271, 14095, 60
https://tests.stockfishchess.org/tests/view/653c5848cc309ae839561ae7

closes https://github.com/official-stockfish/Stockfish/pull/4857

bench: 1216779
2023-11-03 22:40:43 +01:00
cj5716 e277dda716 Prefetch TT entries in probcut
Passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 101344 W: 25893 L: 25491 D: 49960
Ptnml(0-2): 303, 11350, 26991, 11698, 330
https://tests.stockfishchess.org/tests/view/6540daa6cc309ae83956669b

slight speedup:
```
Result of 100 runs
==================
base (./stockfish.master       ) =    1170705  +/- 3133
test (./stockfish.patch        ) =    1174545  +/- 2895
diff                             =      +3841  +/- 3196

speedup        = +0.0033
P(speedup > 0) =  0.9907
```

closes https://github.com/official-stockfish/Stockfish/pull/4856

No functional change
2023-11-03 22:39:04 +01:00
Muzhen Gaming 908811c24a Introduce asymmetric optimism
Introduce asymmetric optimism for both side to move and opponent. Parameter tuning was done with 200k LTC games.

STC: https://tests.stockfishchess.org/tests/view/653cc08fcc309ae8395622b3
LLR: 2.96 (-2.94,2.94) <0.00,2.00>
Total: 98336 W: 25074 L: 24661 D: 48601
Ptnml(0-2): 339, 11612, 24890, 11951, 376

LTC: https://tests.stockfishchess.org/tests/view/653db595cc309ae839563140
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 83244 W: 20760 L: 20339 D: 42145
Ptnml(0-2): 51, 9306, 22498, 9705, 62

closes https://github.com/official-stockfish/Stockfish/pull/4853

Bench: 1371690
2023-10-30 07:51:27 +01:00
cj5716 38aa70adcf Small cleanups
Corrects some incorrect or outdated comments.
Credit is shared with yaneurao (see 38e830a#commitcomment-131131500) and locutus2

closes #4852

No functional change.
2023-10-30 07:49:15 +01:00
Disservin 347d613b0e remove outdated comment
Bench: 1258079
2023-10-27 18:35:52 +02:00
Disservin 08ed4c90db Format Code
No functional change
2023-10-27 17:33:59 +02:00
FauziAkram d30af4f669 Rewarding Quiet Moves that Enable Razoring
The main idea of the patch comes from @peregrineshahin :
https://tests.stockfishchess.org/tests/view/65205363ac57711436728781

Another small idea (tuning) comes from @Vizvezdenec
https://tests.stockfishchess.org/tests/view/652e071ade6d262d08d318f4 And a long
phases of tuning and tests was done by me in order to make the patch be able to
pass both tests.

The idea, as mentioned by Peregrine is that in our standard code, if no best
move found after searching all moves, we give a bonus to the previous move that
caused the fail high. So in razoring we assume no bestmove will be found so we
might as well do the same.

Passed STC:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 82336 W: 20997 L: 20610 D: 40729
Ptnml(0-2): 288, 9710, 20753, 10161, 256
https://tests.stockfishchess.org/tests/view/6538fafbcc309ae83955d8f0

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 46584 W: 11753 L: 11411 D: 23420
Ptnml(0-2): 21, 5133, 12642, 5475, 21
https://tests.stockfishchess.org/tests/view/653a3f2ccc309ae83955f223

closes https://github.com/official-stockfish/Stockfish/pull/4850

Bench: 1258079

Co-Authored-By: Shahin M. Shahin <41402573+peregrineshahin@users.noreply.github.com>
2023-10-27 17:32:19 +02:00
Michael Chaly b0658f09b9 Introduce pawn structure based history
Original idea by Seer chess engine https://github.com/connormcmonigle/seer-nnue,
coding done by @Disservin, code refactoring done by @locutus2 to match the style
of other histories.

This patch introduces pawn structure based history, which assings moves values
based on last digits of pawn structure hash and piece type of moved piece and
landing square of the move. Idea is that good places for pieces are quite often
determined by pawn structure of position. Used in 3 different places
- sorting of quiet moves, sorting of quiet check evasions and in history based
pruning in search.

Passed STC:
https://tests.stockfishchess.org/tests/view/65391d08cc309ae83955dbaf
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 155488 W: 39408 L: 38913 D: 77167
Ptnml(0-2): 500, 18427, 39408, 18896, 513

Passed LTC:
https://tests.stockfishchess.org/tests/view/653a36a2cc309ae83955f181
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 70110 W: 17548 L: 17155 D: 35407
Ptnml(0-2): 33, 7859, 18889, 8230, 44

closes https://github.com/official-stockfish/Stockfish/pull/4849

Bench: 1257882

Co-Authored-By: Disservin <disservin.social@gmail.com>
Co-Authored-By: Stefan Geschwentner <locutus2@users.noreply.github.com>
2023-10-27 17:24:25 +02:00
Taras Vuk 871ab55f01 Simplify futility pruning formula
closes https://github.com/official-stockfish/Stockfish/pull/4848

No functional change
2023-10-27 17:16:28 +02:00
Linmiao Xu 0024133b08 Update 5 search params for pruning at shallow depth
Found by spsa tuning at 45+0.45 with:

```
int fpcEvalOffset = 188;
int fpcLmrDepthMult = 206;
int histDepthMult = -3232;
int histDenom = 5793;
int fpEvalOffset = 115;
int negSeeDepthMultSq = -27;
TUNE(SetRange(0, 394), fpcEvalOffset);
TUNE(SetRange(0, 412), fpcLmrDepthMult);
TUNE(SetRange(-6464, -1616), histDepthMult);
TUNE(SetRange(2896, 11586), histDenom);
TUNE(SetRange(0, 230), fpEvalOffset);
TUNE(SetRange(-54, 0), negSeeDepthMultSq);
```

Passed STC:
https://tests.stockfishchess.org/tests/view/6535551de746e058e6c0165d
LLR: 2.98 (-2.94,2.94) <0.00,2.00>
Total: 109056 W: 28025 L: 27599 D: 53432
Ptnml(0-2): 357, 12669, 28038, 13119, 345

Passed LTC:
https://tests.stockfishchess.org/tests/view/65364c6ff127f3553505175d
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 61290 W: 15316 L: 14941 D: 31033
Ptnml(0-2): 34, 6849, 16498, 7236, 28

closes https://github.com/official-stockfish/Stockfish/pull/4847

bench 1167412
2023-10-24 17:46:18 +02:00
Joost VandeVondele ec02714b62 Cleanup comments and some code reorg.
passed STC:
https://tests.stockfishchess.org/tests/view/6536dc7dcc309ae83955b04d
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 58048 W: 14693 L: 14501 D: 28854
Ptnml(0-2): 200, 6399, 15595, 6669, 161

closes https://github.com/official-stockfish/Stockfish/pull/4846

No functional change
2023-10-24 17:43:05 +02:00
cj5716 d6a5c2b085 Small formatting improvements
Changes some C style casts to C++ style, and fixes some incorrect comments and variable names.

closes #4845

No functional change
2023-10-24 17:42:13 +02:00
Joost VandeVondele 49ece9f791 Follow up Makefile changes for clang-format
as reported on various OS, these changes help portability

closes https://github.com/official-stockfish/Stockfish/pull/4844

No functional change.
2023-10-23 20:39:48 +02:00
Muzhen Gaming cf3dbcb5ac Time management improvements
1. Tune time management parameters.
2. Scale the optimum time and maximum time parameters based on the amount of
   time left, using a logarithmic scale.

Many acknowledgements to @FauziAkram for tuning the parameters and for the
original idea (see
https://tests.stockfishchess.org/tests/view/652f0356de6d262d08d333c5).

STC: https://tests.stockfishchess.org/tests/view/6533938fde6d262d08d39e4d
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 44320 W: 11301 L: 10982 D: 22037
Ptnml(0-2): 146, 4810, 11920, 5147, 137

LTC: https://tests.stockfishchess.org/tests/view/653477e4de6d262d08d3ae06
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 146442 W: 37338 L: 36811 D: 72293
Ptnml(0-2): 60, 14975, 42645, 15460, 81

Verification runs:
3+0.03: https://tests.stockfishchess.org/tests/view/65364e7ef127f3553505178a
10+0: https://tests.stockfishchess.org/tests/view/65364e9ff127f3553505178f
180+1.8: https://tests.stockfishchess.org/tests/view/65364ec3f127f35535051794

closes https://github.com/official-stockfish/Stockfish/pull/4843

No functional change.

Co-Authored-By: FauziAkram <11150271+FauziAkram@users.noreply.github.com>
2023-10-23 20:39:48 +02:00
Disservin a105978bbd remove blank line between function and it's description
- remove the blank line between the declaration of the function and it's
  comment, leads to better IDE support when hovering over a function to see it's
  description
- remove the unnecessary duplication of the function name in the functions
  description
- slightly refactored code for lsb, msb in bitboard.h There are still a few
  things we can be improved later on, move the description of a function where
  it was declared (instead of implemented) and add descriptions to functions
  which are behind macros ifdefs

closes https://github.com/official-stockfish/Stockfish/pull/4840

No functional change
2023-10-23 20:39:48 +02:00
Disservin b187622233 use expanded variables for shell commands
Performance improvement for the shell commands in the Makefile.
By using expanded variables, the shell commands are only
evaluated once, instead of every time they are used.

closes https://github.com/official-stockfish/Stockfish/pull/4838

No functional change
2023-10-23 20:39:48 +02:00
MinetaS 40c6a84434 Fix a compiler bug on Clang 15+ with AVX-512
fixes https://github.com/official-stockfish/Stockfish/issues/4450
closes https://github.com/official-stockfish/Stockfish/pull/4830

No functional change.
2023-10-23 20:39:48 +02:00
Taras Vuk b7b7800e2b Simplify futilityBase formula
This patch replaces std::min(ss->staticEval, bestValue) with ss->staticEval in the futilityBase formula.
Original idea by Vizvezdenec: https://tests.stockfishchess.org/tests/view/64ce66795b17f7c21c0d85f3

Passed STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 116928 W: 29925 L: 29793 D: 57210
Ptnml(0-2): 399, 13558, 30446, 13634, 427
https://tests.stockfishchess.org/tests/view/653285aade6d262d08d385dd

Passed LTC:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 50868 W: 12947 L: 12757 D: 25164
Ptnml(0-2): 30, 5414, 14355, 5606, 29
https://tests.stockfishchess.org/tests/view/65336ffbde6d262d08d39ba0

closes https://github.com/official-stockfish/Stockfish/pull/4837

bench: 1241996
2023-10-22 16:16:02 +02:00
Disservin 2d0237db3f add clang-format
This introduces clang-format to enforce a consistent code style for Stockfish.

Having a documented and consistent style across the code will make contributing easier
for new developers, and will make larger changes to the codebase easier to make.

To facilitate formatting, this PR includes a Makefile target (`make format`) to format the code,
this requires clang-format (version 17 currently) to be installed locally.

Installing clang-format is straightforward on most OS and distros
(e.g. with https://apt.llvm.org/, brew install clang-format, etc), as this is part of quite commonly
used suite of tools and compilers (llvm / clang).

Additionally, a CI action is present that will verify if the code requires formatting,
and comment on the PR as needed. Initially, correct formatting is not required, it will be
done by maintainers as part of the merge or in later commits, but obviously this is encouraged.

fixes https://github.com/official-stockfish/Stockfish/issues/3608
closes https://github.com/official-stockfish/Stockfish/pull/4790

Co-Authored-By: Joost VandeVondele <Joost.VandeVondele@gmail.com>
2023-10-22 16:06:27 +02:00
mstembera 8366ec48ae Scale down stat bonus
STC https://tests.stockfishchess.org/tests/view/652eff58de6d262d08d33353
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 88224 W: 22618 L: 22228 D: 43378
Ptnml(0-2): 282, 10177, 22783, 10609, 261

LTC https://tests.stockfishchess.org/tests/view/652fd13bde6d262d08d3481a
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 143508 W: 36674 L: 36142 D: 70692
Ptnml(0-2): 92, 15240, 40534, 15820, 68

Reduces the stat bonus by 20%. Maybe future patches can tune the actual bonus calculations for different histories.

closes https://github.com/official-stockfish/Stockfish/pull/4836

bench: 1185932
2023-10-21 10:37:27 +02:00
Taras Vuk e18619d078 Subtract the margin from the value returned by ProbCut
This patch subtracts the margin from the value returned by ProbCut.

Passed STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 46112 W: 11940 L: 11610 D: 22562
Ptnml(0-2): 131, 5318, 11842, 5620, 145
https://tests.stockfishchess.org/tests/view/652ea42ade6d262d08d329dd

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 86880 W: 22192 L: 21776 D: 42912
Ptnml(0-2): 43, 9213, 24510, 9633, 41
https://tests.stockfishchess.org/tests/view/652f70ffde6d262d08d33e8d

closes https://github.com/official-stockfish/Stockfish/pull/4835

bench: 1135313
2023-10-21 10:34:12 +02:00
FauziAkram 90c18b0b50 Removing history condition
Removing the bad history condition from the skip futility pruning formula.

Passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 142688 W: 36420 L: 36317 D: 69951
Ptnml(0-2): 481, 16653, 36970, 16762, 478
https://tests.stockfishchess.org/tests/view/65270a663125598fc7eb8c67

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 435378 W: 110723 L: 110925 D: 213730
Ptnml(0-2): 278, 47251, 122788, 47139, 233
https://tests.stockfishchess.org/tests/view/6528595f3125598fc7eba8f5

closes https://github.com/official-stockfish/Stockfish/pull/4834

Bench: 1110579
2023-10-21 10:31:51 +02:00
mstembera d3d0c69dc1 Remove outdated Tile naming.
cleanup variable naming after  #4816

closes #4833

No functional change
2023-10-21 10:28:55 +02:00
Shahin M. Shahin 057046cc9a Cleanup qsearch continuation histories
Only (ss-1) and (ss-2) are used in qsearch.

closes https://github.com/official-stockfish/Stockfish/pull/4828

No functional change
2023-10-21 10:26:09 +02:00
FauziAkram edb4ab924f Standardize Comments
use double slashes (//) only for comments.

closes #4820

No functional change.
2023-10-21 10:25:03 +02:00
Stéphane Nicolet fe53a18f7a Reformat some comments and conditions
closes https://github.com/official-stockfish/Stockfish/pull/4814

No functional change
2023-10-21 10:15:48 +02:00
Shahin M. Shahin a4fedd8152 Fix greater than TB scores in null move pruning.
This patch is a simplification and a fix to dealing with null moves scores that returns proven mates or TB scores by preventing 'null move pruning' if the nullvalue is in that range.

Current solution downgrades nullValues on the non-PV node but the value can be used in a transposed PV-node to the same position afterwards (Triangulation), the later is prone to propagate a wrong score (96.05) to root that will not be refuted unless we search further.

Score of (96.05) can be obtained be two methods,

maxim static-eval returned on Pv update (mostly qSearch)
this downgrade (clamp) in NMP
and theoretically can happen with or without TBs but the second scenario is more dangerous than the first.
This fixes the reproducible case in very common scenarios with TBs as shown in the debugging at discord.

Fixes: #4699

Passed STC:
https://tests.stockfishchess.org/tests/view/64c1eca8dc56e1650abba6f9
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 670048 W: 171132 L: 171600 D: 327316
Ptnml(0-2): 2134, 75687, 179820, 75279, 2104

Passed LTC:
https://tests.stockfishchess.org/tests/view/64c5e130dc56e1650abc0438
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 92868 W: 23642 L: 23499 D: 45727
Ptnml(0-2): 52, 9509, 27171, 9648, 54

closes https://github.com/official-stockfish/Stockfish/pull/4715

Bench: 1327410
2023-10-21 10:01:26 +02:00
Michael Chaly 38e830af4b Use more continuation histories.
This patch allows stats updates and movepicker bonuses for continuation history 3 plies deep - so counter counter move.
Updates and movepicker usage are done with 1/4 multiplier compared to other histories.

Passed STC:
https://tests.stockfishchess.org/tests/view/6528f28d3125598fc7ebb5a3
LLR: 2.96 (-2.94,2.94) <0.00,2.00>
Total: 161344 W: 41369 L: 40868 D: 79107
Ptnml(0-2): 535, 18720, 41679, 19185, 553

Passed LTC:
https://tests.stockfishchess.org/tests/view/652a397a3125598fc7ebd1d6
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 48564 W: 12556 L: 12215 D: 23793
Ptnml(0-2): 25, 5149, 13595, 5486, 27

closes https://github.com/official-stockfish/Stockfish/pull/4827

bench 1327410
2023-10-14 16:52:35 +02:00
Muzhen Gaming 002636362e Search parameters tune at 180+1.8
Passed VLTC: https://tests.stockfishchess.org/tests/view/65200c58ac577114367280bc
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 146180 W: 37407 L: 36988 D: 71785
Ptnml(0-2): 21, 14474, 43675, 14905, 15

Passed VLTC SMP: https://tests.stockfishchess.org/tests/view/652403da3125598fc7eb4b6d
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 57580 W: 15061 L: 14739 D: 27780
Ptnml(0-2): 2, 5001, 18460, 5327, 0

closes https://github.com/official-stockfish/Stockfish/pull/4826

Bench: 1099336
2023-10-10 17:45:32 +02:00
Taras Vuk 7a4de96159 Skip futility pruning if ttMove has bad history
Passed STC:
LLR: 2.96 (-2.94,2.94) <0.00,2.00>
Total: 52416 W: 13465 L: 13128 D: 25823
Ptnml(0-2): 128, 6024, 13604, 6287, 165
https://tests.stockfishchess.org/tests/view/651fadd4ac577114367277bf

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 87348 W: 22234 L: 21818 D: 43296
Ptnml(0-2): 38, 9240, 24698, 9664, 34
https://tests.stockfishchess.org/tests/view/65201932ac57711436728218

closes https://github.com/official-stockfish/Stockfish/pull/4825

bench: 1246560
2023-10-08 07:56:07 +02:00
gabe f7fbc6880e Avoid recomputing moveCountPruning
In search, when moveCountPruning becomes true, it can never turn false again.

Passed STC https://tests.stockfishchess.org/tests/view/652075ceac57711436728aac
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 136448 W: 34923 L: 34472 D: 67053
Ptnml(0-2): 420, 15094, 36767, 15501, 442

closes https://github.com/official-stockfish/Stockfish/pull/4823

Non functional change
2023-10-08 07:52:16 +02:00
candirufish 25d444ed60 Razor more if ss+1 cutoffCnt > 3
STC:
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 221760 W: 56726 L: 56144 D: 108890
Ptnml(0-2): 655, 25453, 58123, 25953, 696
https://tests.stockfishchess.org/tests/view/651d34dbcff46e538ee05d91

LTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 130326 W: 33188 L: 32681 D: 64457
Ptnml(0-2): 69, 13949, 36620, 14456, 69
https://tests.stockfishchess.org/tests/view/651f844eac577114367273d5

closes https://github.com/official-stockfish/Stockfish/pull/4822

bench: 1291708
2023-10-08 07:50:03 +02:00
Stefan Geschwentner 008d59512a Simplify collection of bad moves for history updates.
1. collect only the first 32 moves searched and ignore the rest. So late bad moves get no further negative history updates.
2. collect now for quiet moves also at most 32 bad moves

STC:
https://tests.stockfishchess.org/tests/view/6517b3aeb3e74811c8af5651
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 51168 W: 13013 L: 12810 D: 25345
Ptnml(0-2): 120, 6006, 13186, 6095, 177

LTC:
https://tests.stockfishchess.org/tests/view/651adafecff46e538ee02734
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 109866 W: 27786 L: 27656 D: 54424
Ptnml(0-2): 52, 11816, 31069, 11942, 54

closes https://github.com/official-stockfish/Stockfish/pull/4818

Bench: 1338617
2023-10-08 07:46:26 +02:00
mstembera c17a657b04 Optimize the most common update accumalator cases w/o tiling
In the most common case where we only update a single state
it's faster to not use temporary accumulation registers and tiling.
(Also includes a couple of small cleanups.)

passed STC
https://tests.stockfishchess.org/tests/view/651918e3cff46e538ee0023b
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 34944 W: 8989 L: 8687 D: 17268
Ptnml(0-2): 88, 3743, 9512, 4037, 92

A simpler version
https://tests.stockfishchess.org/tests/view/65190dfacff46e538ee00155
also passed but this version is stronger still
https://tests.stockfishchess.org/tests/view/6519b95fcff46e538ee00fa2

closes https://github.com/official-stockfish/Stockfish/pull/4816

No functional change
2023-10-08 07:42:39 +02:00
Stéphane Nicolet 040dfedb34 Remove one test in the move loop
Simplification passed STC test:
https://tests.stockfishchess.org/tests/view/6519fc91cff46e538ee014f6
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 191264 W: 48550 L: 48501 D: 94213
Ptnml(0-2): 576, 21529, 51392, 21540, 595

closes #4815

Non functional change
2023-10-08 07:41:16 +02:00
Robert Nurnberg @ elitebook f1ce1cd475 Update links in license
matches https://www.gnu.org/licenses/gpl-3.0.txt

closes https://github.com/official-stockfish/Stockfish/pull/4813

No functional change
2023-10-08 07:38:13 +02:00
mstembera 8a912951de Remove handcrafted MMX code
too small a benefit to maintain this old target

closes https://github.com/official-stockfish/Stockfish/pull/4804

No functional change
2023-10-08 07:37:01 +02:00
Linmiao Xu afe7f4d9b0 Update default net to nn-0000000000a0.nnue
This is a later epoch from the same experiment that led to the previous
master net. In training stage 6, max-epoch was raised to 1,200 near the
end of the first 1,000 epochs.

For more details, see https://github.com/official-stockfish/Stockfish/pull/4795

Local elo at 25k nodes per move (vs. L1-2048 nn-1ee1aba5ed4c.nnue)
ep1079 : 15.6 +/- 1.2

Passed STC:
https://tests.stockfishchess.org/tests/view/651503b3b3e74811c8af1e2a
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 29408 W: 7607 L: 7304 D: 14497
Ptnml(0-2): 97, 3277, 7650, 3586, 94

Passed LTC:
https://tests.stockfishchess.org/tests/view/651585ceb3e74811c8af2a5f
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 73164 W: 18828 L: 18440 D: 35896
Ptnml(0-2): 30, 7749, 20644, 8121, 38

closes https://github.com/official-stockfish/Stockfish/pull/4810

Bench: 1453057
2023-09-29 22:30:27 +02:00
cj5716 660da1ca7b Skip moves-loop pruning in qsearch if we have only pawns
At first my idea was only to cover movecount and futility pruning, but
@peregrineshahin suggested to test it on all moves-loop pruning and it worked.

Passed STC:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 167968 W: 42970 L: 42480 D: 82518
Ptnml(0-2): 444, 18324, 46002, 18726, 488
https://tests.stockfishchess.org/tests/view/6511181a55b420c569d0d54c

Passed LTC:
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 40794 W: 10496 L: 10182 D: 20116
Ptnml(0-2): 12, 4021, 12025, 4319, 20
https://tests.stockfishchess.org/tests/view/6512ccc4b3e74811c8aee86c

closes https://github.com/official-stockfish/Stockfish/pull/4809

Bench: 1338472
2023-09-29 22:28:34 +02:00
Sebastian Buchwald 4f0fecad8a Use C++17 variable templates for type traits
The C++17 variable templates are slightly more readable and allow us to
remove the typename keyword in a few cases.

closes https://github.com/official-stockfish/Stockfish/pull/4806

No functional change
2023-09-29 22:22:40 +02:00
mstembera 31d0b7fe93 Remove unused see_ge() code
closes https://github.com/official-stockfish/Stockfish/pull/4805

No functional change
2023-09-29 22:19:08 +02:00
FauziAkram 243f7b264a Improve grammar of comments
closes https://github.com/official-stockfish/Stockfish/pull/4801

No functional change
2023-09-29 22:18:17 +02:00
FauziAkram 9739ed7a97 Simplify pawn count in evaluation
This simplifies the evaluation by removing the unnecessary pawn count term when
combining nnue and optimism values.

Passed STC
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 61472 W: 15748 L: 15554 D: 30170
Ptnml(0-2): 191, 7123, 15933, 7279, 210
https://tests.stockfishchess.org/tests/view/650c34cf7ca0d3f7bbf264ff

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 81264 W: 20657 L: 20500 D: 40107
Ptnml(0-2): 30, 8713, 22997, 8854, 38
https://tests.stockfishchess.org/tests/view/650cc30efb151d43ae6d5987

closes https://github.com/official-stockfish/Stockfish/pull/4800

Bench: 1530568
2023-09-29 22:12:46 +02:00
Jasper Shovelton ce99b4b2ef Increment minor section number from 3.7.1 to 3.8.1.
Pext has nothing to do with git commit sha/date, so separate the two
sub-sections.

closes https://github.com/official-stockfish/Stockfish/pull/4785

No functional change
2023-09-29 22:07:10 +02:00
Joost VandeVondele 22cdb6c1ea Explicitly invoke shell
in some cases the permission on the script might be incorrect (zip downloads?).
Explicitly invoke the shell

closes https://github.com/official-stockfish/Stockfish/pull/4803

No functional change
2023-09-24 20:04:42 +02:00
Linmiao Xu 70ba9de85c Update NNUE architecture to SFNNv8: L1-2560 nn-ac1dbea57aa3.nnue
Creating this net involved:
- a 6-stage training process from scratch. The datasets used in stages 1-5 were fully minimized.
- permuting L1 weights with https://github.com/official-stockfish/nnue-pytorch/pull/254

A strong epoch after each training stage was chosen for the next. The 6 stages were:

```
1. 400 epochs, lambda 1.0, default LR and gamma
   UHOx2-wIsRight-multinet-dfrc-n5000 (135G)
     nodes5000pv2_UHO.binpack
     data_pv-2_diff-100_nodes-5000.binpack
     wrongIsRight_nodes5000pv2.binpack
     multinet_pv-2_diff-100_nodes-5000.binpack
     dfrc_n5000.binpack

2. 800 epochs, end-lambda 0.75, LR 4.375e-4, gamma 0.995, skip 12
   LeelaFarseer-T78juntoaugT79marT80dec.binpack (141G)
     T60T70wIsRightFarseerT60T74T75T76.binpack
     test78-junjulaug2022-16tb7p.no-db.min.binpack
     test79-mar2022-16tb7p.no-db.min.binpack
     test80-dec2022-16tb7p.no-db.min.binpack

3. 800 epochs, end-lambda 0.725, LR 4.375e-4, gamma 0.995, skip 20
   leela93-v1-dfrc99-v2-T78juntosepT80jan-v6dd-T78janfebT79aprT80aprmay.min.binpack
     leela93-filt-v1.min.binpack
     dfrc99-16tb7p-filt-v2.min.binpack
     test78-juntosep2022-16tb7p-filter-v6-dd.min-mar2023.binpack
     test80-jan2023-3of3-16tb7p-filter-v6-dd.min-mar2023.binpack
     test78-janfeb2022-16tb7p.min.binpack
     test79-apr2022-16tb7p.min.binpack
     test80-apr2022-16tb7p.min.binpack
     test80-may2022-16tb7p.min.binpack

4. 800 epochs, end-lambda 0.7, LR 4.375e-4, gamma 0.995, skip 24
   leela96-dfrc99-v2-T78juntosepT79mayT80junsepnovjan-v6dd-T80mar23-v6-T60novdecT77decT78aprmayT79aprT80may23.min.binpack
     leela96-filt-v2.min.binpack
     dfrc99-16tb7p-filt-v2.min.binpack
     test78-juntosep2022-16tb7p-filter-v6-dd.min-mar2023.binpack
     test79-may2022-16tb7p.filter-v6-dd.min.binpack
     test80-jun2022-16tb7p.filter-v6-dd.min.binpack
     test80-sep2022-16tb7p.filter-v6-dd.min.binpack
     test80-nov2022-16tb7p.filter-v6-dd.min.binpack
     test80-jan2023-3of3-16tb7p-filter-v6-dd.min-mar2023.binpack
     test80-mar2023-2tb7p.v6-sk16.min.binpack
     test60-novdec2021-16tb7p.min.binpack
     test77-dec2021-16tb7p.min.binpack
     test78-aprmay2022-16tb7p.min.binpack
     test79-apr2022-16tb7p.min.binpack
     test80-may2023-2tb7p.min.binpack

5. 960 epochs, end-lambda 0.7, LR 4.375e-4, gamma 0.995, skip 28
   Increased max-epoch to 960 near the end of the first 800 epochs
   5af11540bbfe dataset: https://github.com/official-stockfish/Stockfish/pull/4635

6. 1000 epochs, end-lambda 0.7, LR 4.375e-4, gamma 0.995, skip 28
   Increased max-epoch to 1000 near the end of the first 800 epochs
   1ee1aba5ed dataset: https://github.com/official-stockfish/Stockfish/pull/4782
```

L1 weights permuted with:
```bash
python3 serialize.py $nnue $nnue_permuted \
  --features=HalfKAv2_hm \
  --ft_optimize \
  --ft_optimize_data=/data/fishpack32.binpack \
  --ft_optimize_count=10000
```

Speed measurements from 100 bench runs at depth 13 with profile-build x86-64-avx2:
```
sf_base =  1329051 +/-   2224 (95%)
sf_test =  1163344 +/-   2992 (95%)
diff    =  -165706 +/-   4913 (95%)
speedup = -12.46807% +/- 0.370% (95%)
```

Training data can be found at:
https://robotmoon.com/nnue-training-data/

Local elo at 25k nodes per move (vs. L1-2048 nn-1ee1aba5ed4c.nnue)
ep959 : 16.2 +/- 2.3

Failed 10+0.1 STC:
https://tests.stockfishchess.org/tests/view/6501beee2cd016da89abab21
LLR: -2.92 (-2.94,2.94) <0.00,2.00>
Total: 13184 W: 3285 L: 3535 D: 6364
Ptnml(0-2): 85, 1662, 3334, 1440, 71

Failed 180+1.8 VLTC:
https://tests.stockfishchess.org/tests/view/6505cf9a72620bc881ea908e
LLR: -2.94 (-2.94,2.94) <0.00,2.00>
Total: 64248 W: 16224 L: 16374 D: 31650
Ptnml(0-2): 26, 6788, 18640, 6650, 20

Passed 60+0.6 th 8 VLTC SMP (STC bounds):
https://tests.stockfishchess.org/tests/view/65084a4618698b74c2e541dc
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 90630 W: 23372 L: 23033 D: 44225
Ptnml(0-2): 13, 8490, 27968, 8833, 11

Passed 60+0.6 th 8 VLTC SMP:
https://tests.stockfishchess.org/tests/view/6501d45d2cd016da89abacdb
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 137804 W: 35764 L: 35276 D: 66764
Ptnml(0-2): 31, 13006, 42326, 13522, 17

closes https://github.com/official-stockfish/Stockfish/pull/4795

bench 1246812
2023-09-22 19:26:16 +02:00
Stefan Geschwentner 154b8d3ecb Remove VALUE_KNOWN_WIN.
After removing classic evaluation VALUE_KNOWN_WIN is not anymore returned explicit evaluation. So remove and replace it with VALUE_TB_WIN_IN_MAX_PLY.

Measurement on my big bench (bench 16 1 16 pos1000.fen) verifies that at least with current net the calculated evaluation lies always in the open interval  (-VALUE_KNOWN_WIN, VALUE_KNOWN_WIN).
So i consider this a non-functional change. But to be safe i tested this also at LTC as requested by Stephane Nicolet.

STC:
https://tests.stockfishchess.org/tests/view/64f9db40eaf01be8259a6ed5
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 455296 W: 115981 L: 116217 D: 223098
Ptnml(0-2): 1415, 50835, 123420, 50527, 1451

LTC:
https://tests.stockfishchess.org/tests/view/650bfd867ca0d3f7bbf25feb
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 35826 W: 9170 L: 8973 D: 17683
Ptnml(0-2): 12, 3523, 10645, 3722, 11

closes https://github.com/official-stockfish/Stockfish/pull/4792

Bench: 1603079
2023-09-22 19:25:57 +02:00
mstembera 95fe2b9a9d Reduce SIMD register count from 32 to 16
in the case of avx512 and vnni512 archs.

Up to 17% speedup, depending on the compiler, e.g.

```
AMD pro 7840u (zen4 phoenix apu 4nm)
bash bench_parallel.sh ./stockfish_avx512_gcc13 ./stockfish_avx512_pr_gcc13 20 10
sf_base =  1077737 +/-   8446 (95%)
sf_test =  1264268 +/-   8543 (95%)
diff    =   186531 +/-   4280 (95%)
speedup =  17.308% +/- 0.397% (95%)
```

Prior to this patch, it appears gcc spills registers.

closes https://github.com/official-stockfish/Stockfish/pull/4796

No functional change
2023-09-22 19:15:34 +02:00
cj5716 fce4cc1829 Make casting styles consistent
Make casting styles consistent with the rest of the code.

closes https://github.com/official-stockfish/Stockfish/pull/4793

No functional change
2023-09-22 19:14:29 +02:00
Sebastian Buchwald 952740b36c Let CI check C++ includes
The commit adds a CI workflow that uses the included-what-you-use (IWYU)
tool to check for missing or superfluous includes in .cpp files and
their corresponding .h files. This means that some .h files (especially
in the nnue folder) are not checked yet.

The CI setup looks like this:
- We build IWYU from source to include some yet unreleased fixes.
  This IWYU version targets LLVM 17. Thus, we get the latest release
  candidate of LLVM 17 from LLVM's nightly packages.
- The Makefile now has an analyze target that just build the object
  files (without linking)
- The CI uses the analyze target with the IWYU tool as compiler to
  analyze the compiled .cpp file and its corresponding .h file.
- If IWYU suggests a change the build fails (-Xiwyu --error).
- To avoid false positives we use LLVM's libc++ as standard library
- We have a custom mappings file that adds some mappings that are
  missing in IWYU's default mappings

We also had to add one IWYU pragma to prevent a false positive in
movegen.h.

https://github.com/official-stockfish/Stockfish/pull/4783

No functional change
2023-09-22 19:12:53 +02:00
Stéphane Nicolet e594aa7429 Export makefile ARCH in binary
Example of the `./stockfish compiler` command:

Compiled by                : g++ (GNUC) 10.3.0 on Apple
Compilation architecture   : x86-64-bmi2
Compilation settings       : 64bit BMI2 AVX2 SSE41 SSSE3 SSE2 POPCNT
Compiler __VERSION__ macro : 10.3.0

closes https://github.com/official-stockfish/Stockfish/pull/4789

no functional change
2023-09-22 19:09:20 +02:00
Joost VandeVondele 708319a433 Enable a default native ARCH
uses a posix compatible script to find the native arch.
(based on ppigazzini's https://github.com/ppigazzini/stockfish-downloader )
use that native arch by default, no changes if ARCH is specified explicitly.

SF can now be compiled in an optimal way simply using

make -j profile-build

closes https://github.com/official-stockfish/Stockfish/pull/4777

No functional change
2023-09-22 19:06:37 +02:00
mstembera 97f706ecc1 Sparse impl of affine_transform_non_ssse3()
deal with the general case

About a 8.6% speedup (for general arch)

Results for 200 tests for each version:

            Base      Test      Diff
    Mean    141741    153998    -12257
    StDev   2990      3042      3742

p-value: 0.999
speedup: 0.086

closes https://github.com/official-stockfish/Stockfish/pull/4786

No functional change
2023-09-22 19:03:47 +02:00
peregrineshahin 0e32287af4 Reorder some lines
Now that qsearch has its own repetition detection we can flip the order of lines and remove the guard of depth < 0 which is not needed after reordering (i.e. it was there to prevent checking repetition again at depth ==0).

Passed STC:
https://tests.stockfishchess.org/tests/view/6502ecbb2cd016da89abc3fb
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 69536 W: 17668 L: 17490 D: 34378
Ptnml(0-2): 190, 7652, 18929, 7784, 213

Passed LTC:
https://tests.stockfishchess.org/tests/view/6505ce9072620bc881ea9086
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 52116 W: 13294 L: 13113 D: 25709
Ptnml(0-2): 26, 5176, 15471, 5361, 24

closes https://github.com/official-stockfish/Stockfish/pull/4791

No functional change
2023-09-22 19:02:18 +02:00
Stéphane Nicolet 3f7fb5ac1d Reformat some comments
Also include the bench to make Continuation Integration happy on Github.

Bench: 1603079
2023-09-11 23:19:06 +02:00
Sebastian Buchwald b9319c4fa4 Cleanup code after dropping ICC support in favor of ICX
The commit removes all uses of ICC's __INTEL_COMPILER macro and other
references to ICC. It also adds ICX info to the compiler command and
fixes two typos in Makefile's help output.

closes https://github.com/official-stockfish/Stockfish/pull/4769

No functional change
2023-09-11 22:46:01 +02:00
Linmiao Xu 3d1b067d85 Update default net to nn-1ee1aba5ed4c.nnue
Created by retraining the master net on a dataset composed by:
- adding Leela data from T60 jul-dec 2020, T77 nov 2021, T80 jun-jul 2023
- deduplicating and unminimizing parts of the dataset before interleaving

Trained initially with max epoch 800, then increased near the end of training
twice. First to 960, then 1200. After training, post-processing involved:
- greedy permuting L1 weights with https://github.com/official-stockfish/Stockfish/pull/4620
- greedy 2- and 3- cycle permuting with https://github.com/official-stockfish/Stockfish/pull/4640

  python3 easy_train.py \
    --experiment-name 2048-retrain-S6-sk28 \
    --training-dataset /data/S6.binpack \
    --early-fen-skipping 28 \
    --start-from-engine-test-net True \
    --max_epoch 1200 \
    --lr 4.375e-4 \
    --gamma 0.995 \
    --start-lambda 1.0 \
    --end-lambda 0.7 \
    --tui False \
    --seed $RANDOM \
    --gpus 0

In the list of datasets below, periods in the filename represent the sequence of
steps applied to arrive at the particular binpack. For example:

test77-dec2021-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack
1. test77 dec2021 data rescored with 16 TB of syzygy tablebases during data conversion
2. filtered with csv_filter_v6_dd.py - v6 filtering and deduplication in one step
3. minimized with the original mar2023 implementation of `minimize_binpack` in
   the tools branch
4. unminimized by removing all positions with score == 32002 (`VALUE_NONE`)

Binpacks were:
- filtered with: https://github.com/linrock/nnue-data
- unminimized with: https://github.com/linrock/Stockfish/tree/tools-unminify
- deduplicated with: https://github.com/linrock/Stockfish/tree/tools-dd

  DATASETS=(
    leela96-filt-v2.min.unminimized.binpack
    dfrc99-16tb7p-eval-filt-v2.min.unminimized.binpack

    # most of the 0dd1cebea57 v6-dd dataset (without test80-jul2022)
    # https://github.com/official-stockfish/Stockfish/pull/4606
    test60-novdec2021-12tb7p.filter-v6-dd.min-mar2023.unminimized.binpack
    test77-dec2021-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack
    test78-jantomay2022-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack
    test78-juntosep2022-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack
    test79-apr2022-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack
    test79-may2022-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack
    test80-jun2022-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack
    test80-aug2022-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack
    test80-sep2022-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack
    test80-oct2022-16tb7p.filter-v6-dd.min.binpack
    test80-nov2022-16tb7p.filter-v6-dd.min.binpack
    test80-jan2023-3of3-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack
    test80-feb2023-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack

    # older Leela data, recently converted
    test60-octnovdec2020-2tb7p.min.unminimized.binpack
    test60-julaugsep2020-2tb7p.min.binpack
    test77-nov2021-2tb7p.min.dd.binpack

    # newer Leela data
    test80-mar2023-2tb7p.min.unminimized.binpack
    test80-apr2023-2tb7p.filter-v6-sk16.min.unminimized.binpack
    test80-may2023-2tb7p.min.dd.binpack
    test80-jun2023-2tb7p.min.binpack
    test80-jul2023-2tb7p.binpack
  )
  python3 interleave_binpacks.py ${DATASETS[@]} /data/S6.binpack

Training data can be found at:
https://robotmoon.com/nnue-training-data/

Local elo at 25k nodes per move:
nn-epoch1059 : 2.7 +/- 1.6

Passed STC:
https://tests.stockfishchess.org/tests/view/64fc8d705dab775b5359db42
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 168352 W: 43216 L: 42704 D: 82432
Ptnml(0-2): 599, 19672, 43134, 20160, 611

Passed LTC:
https://tests.stockfishchess.org/tests/view/64fd44a75dab775b5359f065
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 154194 W: 39436 L: 38881 D: 75877
Ptnml(0-2): 78, 16577, 43238, 17120, 84

closes https://github.com/official-stockfish/Stockfish/pull/4782

Bench: 1603079
2023-09-11 22:37:39 +02:00
Michael Chaly ef22829616 Do more futility pruning in qsearch
This patch introduces a third futility pruning heuristic in qsearch. The idea is
that the static exchange evaluation is much worse than the difference between
futility base and alpha. Thus we can assume that the probability of the move
being good enough to beat alpha is low so it can be pruned.

Passed STC:
https://tests.stockfishchess.org/tests/view/64fc982a5dab775b5359dc83
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 36576 W: 9484 L: 9170 D: 17922
Ptnml(0-2): 121, 4119, 9495, 4431, 122

Passed LTC:
https://tests.stockfishchess.org/tests/view/64fcc7935dab775b5359e1a9
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 135408 W: 34556 L: 34041 D: 66811
Ptnml(0-2): 56, 14462, 38165, 14953, 68

closes https://github.com/official-stockfish/Stockfish/pull/4781

Bench: 1330793
2023-09-11 22:36:26 +02:00
cj5716 6d85f43e26 Simplify cutnode depth condition
With this patch, the depth condition for the cutnodes reduction is loosened from
tte->depth() >= depth + 3 to just tte->depth() >= depth.

Passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 101152 W: 25830 L: 25682 D: 49640
Ptnml(0-2): 312, 11788, 26258, 11876, 342
https://tests.stockfishchess.org/tests/view/64fd15635dab775b5359eaa6

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 82542 W: 20980 L: 20824 D: 40738
Ptnml(0-2): 42, 8795, 23440, 8953, 41
https://tests.stockfishchess.org/tests/view/64fda3545dab775b5359fbf1

closes https://github.com/official-stockfish/Stockfish/pull/4780

Bench: 1479029
2023-09-11 22:30:57 +02:00
Sebastian Buchwald 46a5cedc11 Cleanup git checkout actions
We now fetch only the current commit for jobs that don't need the git
history. For the Prerelease job, we don't checkout the code at all.

closes https://github.com/official-stockfish/Stockfish/pull/4779

No functional change
2023-09-11 22:15:22 +02:00
Tomasz Sobczyk 1461d861c8 Prevent usage of AVX-512 for the last layer.
Add more static checks regarding the SIMD width match.

STC: https://tests.stockfishchess.org/tests/view/64f5c568a9bc5a78c669e70e
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 125216 W: 31756 L: 31636 D: 61824
Ptnml(0-2): 327, 13993, 33848, 14113, 327

Fixes a bug introduced in 2f2f45f, where with AVX-512 the weights and input to
the last layer were being read out of bounds. Now AVX-512 is only used for the
layers it can be used for. Additional static assertions have been added to
prevent more errors like this in the future.

closes https://github.com/official-stockfish/Stockfish/pull/4773

No functional change
2023-09-11 22:11:30 +02:00
Sebastian Buchwald a8b4fd1671 Avoid "using namespace std"
This is a cleanup PR that prepares the automatic checking of missing or
superfluous #include directives via the include-what-you-use (IWYU) tool
on the CI. Unfortunately, IWYU proposes additional includes for
"namespace std" although we don't need them.

To avoid the problem, the commit removes all "using namespace std"
statements from the code and directly uses the std:: prefix instead.
Alternatively, we could add specific usings (e.g. "using std::string")
foreach used type. Also, a mix of both approaches would be possible.
I decided for the prefix approach because most of the files were already
using the std:: prefixes despite the "using namespace std".

closes https://github.com/official-stockfish/Stockfish/pull/4772

No functional change
2023-09-11 22:07:55 +02:00
Stéphane Nicolet b25d68f6ee Introduce simple_eval() for lazy evaluations
This patch implements the pure materialistic evaluation called simple_eval()
to gain a speed-up during Stockfish search.

We use the so-called lazy evaluation trick: replace the accurate but slow
NNUE network evaluation by the super-fast simple_eval() if the position
seems to be already won (high material advantage). To guard against some
of the most obvious blunders introduced by this idea, this patch uses the
following features which will raise the lazy evaluation threshold in some
situations:

- avoid lazy evals on shuffling branches in the search tree
- avoid lazy evals if the position at root already has a material imbalance
- avoid lazy evals if the search value at root is already winning/losing.

Moreover, we add a small random noise to the simple_eval() term. This idea
(stochastic mobility in the minimax tree) was worth about 200 Elo in the pure
simple_eval() player on Lichess.

Overall, the current implementation in this patch evaluates about 2% of the
leaves in the search tree lazily.

--------------------------------------------

STC:
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 60352 W: 15585 L: 15234 D: 29533
Ptnml(0-2): 216, 6906, 15578, 7263, 213
https://tests.stockfishchess.org/tests/view/64f1d9bcbd9967ffae366209

LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 35106 W: 8990 L: 8678 D: 17438
Ptnml(0-2): 14, 3668, 9887, 3960, 24
https://tests.stockfishchess.org/tests/view/64f25204f5b0c54e3f04c0e7

verification run at VLTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 74362 W: 19088 L: 18716 D: 36558
Ptnml(0-2): 6, 7226, 22348, 7592, 9
https://tests.stockfishchess.org/tests/view/64f2ecdbf5b0c54e3f04d3ae

All three tests above were run with adjudication off, we also verified that
there was no regression on matetracker (thanks Disservin!).

----------------------------------------------

closes https://github.com/official-stockfish/Stockfish/pull/4771

Bench: 1393714
2023-09-03 09:28:16 +02:00
FauziAkram adf29b3fd6 Rename one variable
To enhance code clarity and prevent potential confusion with the
'r' variable assigned to reduction later in the code, this pull
request renames it to 'reductionScale' when we use the same name
in the reduction() function.

Using distinct variable names for separate functions improves code
readability and maintainability.

closes https://github.com/official-stockfish/Stockfish/pull/4765

No functional change
2023-09-03 09:10:27 +02:00
pb00067 1f7ff8406d Simplify slider_blocker calculation
Now that classical evaluation was removed, we can adapt this method
to the needs of set_check_info.

STC:
2.95 (-2.94,2.94) <-1.75,0.25>
Total: 298176 W: 75802 L: 75868 D: 146506
Ptnml(0-2): 908, 33608, 80192, 33402, 978
https://tests.stockfishchess.org/tests/view/64e70b899009777747557b43

closes https://github.com/official-stockfish/Stockfish/pull/4753

no functional change
2023-09-03 08:57:43 +02:00
Disservin 4f7fe255c7 Simplify README
The UCI protocol is rather technical and has little value in our README. Instead
it should be explained in our wiki. "Contributing" is moved above "Compiling
Stockfish" to make it more prominent.

Also move the CONTRIBUTING.md into the root directory and include it in the
distributed artifacts/releases.

closes https://github.com/official-stockfish/Stockfish/pull/4766

No functional change
2023-09-03 08:40:08 +02:00
Disservin 3c0e86a91e Cleanup includes
Reorder a few includes, include "position.h" where it was previously missing
and apply include-what-you-use suggestions. Also make the order of the includes
consistent, in the following way:

1. Related header (for .cpp files)
2. A blank line
3. C/C++ headers
4. A blank line
5. All other header files

closes https://github.com/official-stockfish/Stockfish/pull/4763
fixes https://github.com/official-stockfish/Stockfish/issues/4707

No functional change
2023-09-03 08:24:51 +02:00
ttruscott 8cd5cbf693 Omit two unneeded tests
These redundant tests were intended as a speed-up, but they do not seem
to provide any speed anymore.

STC: https://tests.stockfishchess.org/tests/view/64e9079c85e3e95030fd8259
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 134688 W: 34338 L: 34226 D: 66124
Ptnml(0-2): 426, 15122, 36124, 15258, 414

closes https://github.com/official-stockfish/Stockfish/pull/4767

No functional change
2023-09-03 08:07:59 +02:00
Stéphane Nicolet 4c4cb185aa Play turbulent when defending, simpler when attacking
This patch decays a little the evaluation (up to a few percent) for
positions which have a large complexity measure (material imbalance,
positional compensations, etc).

This may have nice consequences on the playing style, as it modifies
the search differently for attack and defense, both effects being
desirable:

- to see the effect on positions when Stockfish is defending, let us
suppose for instance that the side to move is Stockfish and the nnue
evaluation on the principal variation is -100 : this patch will decay
positions with an evaluation of -103 (say) to the same level, provided
they have huge material imbalance or huge positional compensation.
In other words, chaotic positions with an evaluation of -103 are now
comparable in our search tree to stable positions with an evaluation
of -100, and chaotic positions with an evaluation of -102 are now
preferred to stable positions with an evaluation of -100.

- the effect on positions when Stockfish is attacking is the opposite.
Let us suppose for instance that the side to move is Stockfish and the
nnue evaluation on the principal variation is +100 : this patch will
decay the evaluation to +97 if the positions on the principal variation
have huge material imbalance or huge positional compensation. In other
words, stable positions with an evaluation of +97 are now comparable
in our search tree to chaotic positions with an evaluation of +100,
and stable positions with an evaluation of +98 are now preferred to
chaotic positions with an evaluation of +100.

So the effect of this small change of evaluation on the playing style
is that Stockfish should now play a little bit more turbulent when
defending, and choose slightly simpler lines when attacking.

passed STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 268448 W: 68713 L: 68055 D: 131680
Ptnml(0-2): 856, 31514, 68943, 31938, 973
https://tests.stockfishchess.org/tests/view/64e252bb99700912526653ed

passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 141060 W: 36066 L: 35537 D: 69457
Ptnml(0-2): 71, 15179, 39522, 15666, 92
https://tests.stockfishchess.org/tests/view/64e4447a9009777747553725

closes https://github.com/official-stockfish/Stockfish/pull/4762

Bench: 1426295
2023-08-24 08:11:17 +02:00
Shahin M. Shahin 440feecb4d Reduce repetitions branches
Increase reduction on retrying a move we just retreated that falls in a repetition:
if current move can be the same move from previous previous turn then we retreated
that move on the previous turn, this patch increases reduction if retrying that move
results in a repetition.

How to continue from there? Maybe we some variants of this idea could bring Elo too
(only testing the destination square, or triangulations, etc.)

Passed STC:
https://tests.stockfishchess.org/tests/view/64e1aede883cbb7cbd9ad976
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 424000 W: 108675 L: 107809 D: 207516
Ptnml(0-2): 1296, 47350, 113896, 48108, 1350

Passed LTC:
https://tests.stockfishchess.org/tests/view/64e32d629970091252666872
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 89682 W: 22976 L: 22569 D: 44137
Ptnml(0-2): 39, 8843, 26675, 9240, 44

closes https://github.com/official-stockfish/Stockfish/pull/4757

bench: 1574347
2023-08-22 11:32:51 +02:00
cj5716 030b87182a Do more full window searches
Remove the value < beta condition for doing full window searches.
As an added bonus the condition for full-window search is now much
more similar to other fail-soft engines.

Passed STC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 244608 W: 62286 L: 62294 D: 120028
Ptnml(0-2): 758, 28772, 63214, 28840, 720
https://tests.stockfishchess.org/tests/view/64d72d365b17f7c21c0e6675

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 311460 W: 78909 L: 78985 D: 153566
Ptnml(0-2): 129, 33959, 87656, 33831, 155
https://tests.stockfishchess.org/tests/view/64dca2265b17f7c21c0ee06c

closes https://github.com/official-stockfish/Stockfish/pull/4755

Bench: 1624221
2023-08-22 11:22:15 +02:00
Gian-Carlo Pascutto c6f62363a6 Simplify Square Clipped ReLU code.
Squared numbers are never negative, so barring any wraparound there
is no need to clamp to 0. From reading the code, there's no obvious
way to get wraparound, so the entire operation can be simplified
away. Updated original truncated code comments to be sensible.

Verified by running ./stockfish bench 128 1 24 and by the following test:

STC: https://tests.stockfishchess.org/tests/view/64da4db95b17f7c21c0eabe7
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 60224 W: 15425 L: 15236 D: 29563
Ptnml(0-2): 195, 6576, 16382, 6763, 196

closes https://github.com/official-stockfish/Stockfish/pull/4751

No functional change
2023-08-22 11:14:19 +02:00
Stéphane Nicolet 4c5919fa95 Fix some tabs in Makefile
Avoid mixing spaces and tabs for indentation in Makefile

closes https://github.com/official-stockfish/Stockfish/pull/4759

No functional change
2023-08-22 10:59:39 +02:00
Joost VandeVondele fe7353f702 Update links to fishtest
Fishtest has moved to https://github.com/official-stockfish/fishtest/

closes https://github.com/official-stockfish/Stockfish/pull/4758

No functional change
2023-08-22 10:53:49 +02:00
Matthies 9abef246a9 Allow compilation on Raspi (for ARMv8)
Current master fails to compile for ARMv8 on Raspi cause gcc (version 10.2.1)
does not like to cast between signed and unsigned vector types. This patch
fixes it by using unsigned vector pointer for ARM to avoid implicite cast.

closes https://github.com/official-stockfish/Stockfish/pull/4752

No functional change
2023-08-22 10:43:51 +02:00
Stéphane Nicolet a9a0dbbcd0 Fix some 'possible loss of data' warnings
Patch by Maxim Masiutin

closes https://github.com/official-stockfish/Stockfish/pull/4440

No functional change
2023-08-22 10:39:03 +02:00
Disservin 46756996e7 Add -funroll-loops to CXXFLAGS
Optimize profiling data accuracy by enabling -funroll-loops during the profile
generation phase, in addition to its default activation by -fprofile-use.
This seems to produce a slightly faster binary, for most compilers.

make -j profile-build ARCH=x86-64-avx2

sf_base =  1392875 +/-   5905 (95%)
sf_test =  1402332 +/-   7303 (95%)
diff    =     9457 +/-   4413 (95%)
speedup = 0.67896% +/- 0.317% (95%)

STC:
LLR: 2.93 (-2.94,2.94) <0.00,2.00>
Total: 34784 W: 8970 L: 8665 D: 17149
Ptnml(0-2): 115, 3730, 9405, 4019, 123
https://tests.stockfishchess.org/tests/view/64d944815b17f7c21c0e92e1

closes https://github.com/official-stockfish/Stockfish/pull/4750

No functional change
2023-08-16 21:25:42 +02:00
Muzhen Gaming fe0dca12f1 Simplify PvNode Reduction
Remove the depth condition for PvNode reduction.

Simplification STC:
https://tests.stockfishchess.org/tests/view/64d308fa5b17f7c21c0e0303
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 38976 W: 10106 L: 9889 D: 18981
Ptnml(0-2): 129, 4479, 10040, 4726, 114

Simplification LTC:
https://tests.stockfishchess.org/tests/view/64d457db5b17f7c21c0e236f
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 156402 W: 39727 L: 39645 D: 77030
Ptnml(0-2): 71, 17143, 43696, 17215, 76

closes https://github.com/official-stockfish/Stockfish/pull/4747

Bench: 1493904
2023-08-16 21:24:58 +02:00
SzilBalazs 3e5a817fd2 Fix dead link to compression algorithm in tbprobe
closes https://github.com/official-stockfish/Stockfish/pull/4746

No functional change
2023-08-16 21:24:58 +02:00
Disservin a77a8448ff Add CONTRIBUTING.md
closes https://github.com/official-stockfish/Stockfish/pull/4741

No functional change
2023-08-16 21:24:54 +02:00
mstembera 9b80897657 Simplify material difference in evaluate
STC: https://tests.stockfishchess.org/tests/view/64d166235b17f7c21c0ddc15
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 100032 W: 25698 L: 25547 D: 48787
Ptnml(0-2): 308, 11748, 25771, 11863, 326

LTC: https://tests.stockfishchess.org/tests/view/64d28c085b17f7c21c0df775
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 123870 W: 31463 L: 31348 D: 61059
Ptnml(0-2): 63, 13487, 34719, 13604, 62

Besides rebasing I replaced PawnValueMg w/ 126 explicitly to decouple from https://tests.stockfishchess.org/tests/view/64d212de5b17f7c21c0debbb by @peregrineshahin which also passed. #4734

closes https://github.com/official-stockfish/Stockfish/pull/4731

Bench: 1447866
2023-08-13 11:59:06 +02:00
Shahin M. Shahin 3322349c1a Simplify pieceValue to one phase.
Simplifies the usage of pieceValues to mg values with the exception of pawnValues, After the removal of PSQT.

passed STC:
https://tests.stockfishchess.org/tests/view/64d147845b17f7c21c0dd86c
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 197248 W: 50168 L: 50125 D: 96955
Ptnml(0-2): 651, 23029, 51222, 23070, 652

passed LTC:
https://tests.stockfishchess.org/tests/view/64d212de5b17f7c21c0debbb
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 181170 W: 45949 L: 45893 D: 89328
Ptnml(0-2): 84, 19656, 51052, 19706, 87

closes https://github.com/official-stockfish/Stockfish/pull/4734

Bench: 1494401
2023-08-13 11:58:08 +02:00
Stéphane Nicolet 495852fecd Simplify SEE pruning for captures
It seems that the current search is smart enough to allow us to remove
(again) the block of code that checks for discovered attacks after the
first pruning condition for captures.

STC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 278848 W: 70856 L: 70903 D: 137089
Ptnml(0-2): 960, 32829, 71894, 32780, 961
https://tests.stockfishchess.org/tests/view/64d0af095b17f7c21c0dc440

LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 100704 W: 25564 L: 25425 D: 49715
Ptnml(0-2): 56, 10858, 28381, 11005, 52
https://tests.stockfishchess.org/tests/view/64d293e85b17f7c21c0df844

closes https://github.com/official-stockfish/Stockfish/pull/4736

Bench: 1470572
2023-08-13 11:52:47 +02:00
FauziAkram c02ee70927 Simplify prior countermove bonus
Swapping a multiplication operation between two terms with a simple constant

Passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 60512 W: 15424 L: 15231 D: 29857
Ptnml(0-2): 200, 6985, 15712, 7140, 219
https://tests.stockfishchess.org/tests/view/64d2addf5b17f7c21c0dfae6

Passed LTC:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 108456 W: 27545 L: 27414 D: 53497
Ptnml(0-2): 63, 11629, 30698, 11790, 48
https://tests.stockfishchess.org/tests/view/64d3ab6e5b17f7c21c0e1188

closes https://github.com/official-stockfish/Stockfish/pull/4738

Bench: 1636213
2023-08-13 11:48:32 +02:00
ppigazzini 796d9df643 Check compiler for docker builds in CI
closes https://github.com/official-stockfish/Stockfish/pull/4739

No functional change
2023-08-13 11:47:52 +02:00
Gabrik 84e97a38a3 Remove the unused enum ScaleFactor
closes https://github.com/official-stockfish/Stockfish/pull/4740

No functional change
2023-08-13 11:46:48 +02:00
Shahin M. Shahin b7b7a3f3fa Detect repetitions before they happen in qsearch
Passed STC:
https://tests.stockfishchess.org/tests/view/64d495995b17f7c21c0e29ed
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 340288 W: 86664 L: 85910 D: 167714
Ptnml(0-2): 1030, 38855, 89697, 39455, 1107

Passed LTC:
https://tests.stockfishchess.org/tests/view/64d5e1085b17f7c21c0e4ab5
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 193230 W: 49235 L: 48606 D: 95389
Ptnml(0-2): 98, 20432, 54921, 21071, 93

closes https://github.com/official-stockfish/Stockfish/pull/4742

Bench: 1579576
2023-08-13 11:44:17 +02:00
cj5716 222f3ea55b Simplify a depth condition
As the negative extension term has sensitive scaling, it would make more sense to allow more negative extension also at lower depth, and not just a region between low and high depth.

Passed STC:
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 124096 W: 31611 L: 31485 D: 61000
Ptnml(0-2): 422, 14437, 32205, 14561, 423
https://tests.stockfishchess.org/tests/view/64d205d75b17f7c21c0dea82

Passed LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 387882 W: 97840 L: 97993 D: 192049
Ptnml(0-2): 198, 42004, 109668, 41895, 176
https://tests.stockfishchess.org/tests/view/64d333f85b17f7c21c0e06c6

closes https://github.com/official-stockfish/Stockfish/pull/4743

Bench: 1542357
2023-08-13 11:40:35 +02:00
Michael Chaly d97a02ea2b Give extra bonus to main history for moves that caused a fail low. #4744
Current master gives this type of bonuses to continuation history, this patch also gives them to main history.

Passed STC:
https://tests.stockfishchess.org/tests/view/64d4802a5b17f7c21c0e27b3
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 480768 W: 122767 L: 121798 D: 236203
Ptnml(0-2): 1563, 56190, 123834, 57309, 1488

Passed LTC:
https://tests.stockfishchess.org/tests/view/64d7e4c05b17f7c21c0e7456
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 32052 W: 8329 L: 8022 D: 15701
Ptnml(0-2): 19, 3335, 9015, 3634, 23

closes https://github.com/official-stockfish/Stockfish/pull/4744

Bench: 1711793
2023-08-13 11:38:24 +02:00
Joost VandeVondele 4be94f41a6 Update sanitizer CI to ubuntu 22.04
might fix the tsan errors

closes https://github.com/official-stockfish/Stockfish/pull/4745

No functional change
2023-08-13 11:34:00 +02:00
Joost VandeVondele 8192945870 Improve testing coverage, remove unused code
a) Add further tests to CI to cover most features. This uncovered a potential race
in case setoption was sent between two searches. As the UCI protocol requires
this sent to be went the engine is not searching, setoption now ensures that
this is the case.

b) Remove some unused code

closes https://github.com/official-stockfish/Stockfish/pull/4730

No functional change
2023-08-11 19:27:46 +02:00
Tomasz Sobczyk 0d2ddb81ef Fix Makefile for incorrect nnue file
If an incorrect network file is present at the start of the compilation stage, the
Makefile script now correctly removes it before trying to download a clean version.

closes https://github.com/official-stockfish/Stockfish/pull/4726

No functional change
2023-08-11 19:20:29 +02:00
Cody Ho e64b817e0a Remove all references to Score type
Score is obsolete with the removal of psqt.

No functional change.

Signed-off-by: Cody Ho <codyho@stanford.edu>

closes https://github.com/official-stockfish/Stockfish/pull/4724
2023-08-09 18:27:16 +02:00
Michael Chaly 5c2111cc30 Adjust futility pruning base in qsearch
Current master used value from transposition table there if it existed,
this patch uses minimum between this tt value and the static eval instead
(this thus is closer to the main search function, which uses the static eval).

Passed STC:
https://tests.stockfishchess.org/tests/view/64cd57285b17f7c21c0d6a8c
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 252544 W: 64671 L: 64039 D: 123834
Ptnml(0-2): 839, 29207, 65575, 29785, 866

Passed LTC:
https://tests.stockfishchess.org/tests/view/64cf6c915b17f7c21c0d9fcb
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 60150 W: 15374 L: 15012 D: 29764
Ptnml(0-2): 24, 6321, 17024, 6681, 25

closes https://github.com/official-stockfish/Stockfish/pull/4725

Bench: 1573024
2023-08-07 07:24:11 +02:00
FauziAkram a26f8d37e1 Tweak formula for pruning moves losing material
Simplify the "Prune moves with negative SEE" formula,
by removing one multiplication and subtraction operation.

Passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 214272 W: 54596 L: 54572 D: 105104
Ptnml(0-2): 741, 25160, 55320, 25164, 751
https://tests.stockfishchess.org/tests/view/64c430d1dc56e1650abbdbf6

Passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 238380 W: 60600 L: 60601 D: 117179
Ptnml(0-2): 132, 26069, 66791, 26064, 134
https://tests.stockfishchess.org/tests/view/64c81f155b17f7c21c0cee2b

closes https://github.com/official-stockfish/Stockfish/pull/4721

bench: 1655337
2023-08-06 22:25:58 +02:00
Linmiao Xu 0ad9b51dea Remove classical psqt
Based on vondele's deletepsqt branch:
https://github.com/vondele/Stockfish/commit/369f5b051

This huge simplification uses a weighted material differences instead of
the positional piece square tables (psqt) in the semi-classical complexity
calculation. Tuned weights using spsa at 45+0.45 with:

int pawnMult = 100;
int knightMult = 325;
int bishopMult = 350;
int rookMult = 500;
int queenMult = 900;
TUNE(SetRange(0, 200), pawnMult);
TUNE(SetRange(0, 650), knightMult);
TUNE(SetRange(0, 700), bishopMult);
TUNE(SetRange(200, 800), rookMult);
TUNE(SetRange(600, 1200), queenMult);

The values obtained via this tuning session were for a model where
the psqt replacement formula was always from the point of view of White,
even if the side to move was Black. We re-used the same values for an
implementation with a psqt replacement from the point of view of the side
to move, testing the result both on our standard book on positions with
a strong White bias, and an alternate book with positions with a strong
Black bias.

We note that with the patch the last use of the venerable "Score" type
disappears in Stockfish codebase (the Score type was used in classical
evaluation to get a tampered eval interpolating values smoothly from the
early midgame stage to the endgame stage). We leave it to another commit
to clean all occurrences of Score in the code and the comments.

-------

Passed non-regression LTC:
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 142542 W: 36264 L: 36168 D: 70110
Ptnml(0-2): 76, 15578, 39856, 15696, 65
https://tests.stockfishchess.org/tests/view/64c8cb495b17f7c21c0cf9f8

Passed non-regression LTC (with a book with Black bias):
https://tests.stockfishchess.org/tests/view/64c8f9295b17f7c21c0cfdaf
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 494814 W: 125565 L: 125827 D: 243422
Ptnml(0-2): 244, 53926, 139346, 53630, 261

------

closes https://github.com/official-stockfish/Stockfish/pull/4713

Bench: 1655985
2023-08-06 22:16:52 +02:00
AndrovT a6d9a302b8 Implement AffineTransformSparseInput for armv8
Implements AffineTransformSparseInput layer for the NNUE evaluation
for the armv8 and armv8-dotprod architectures. We measured some nice
speed improvements via 10 runs of our benchmark:

armv8, Cortex-X1                  :   18.5% speed-up
armv8, Cortex-A76                 :   13.2% speed-up
armv8-dotprod, Cortex-X1          :   27.1% speed-up
armv8-dotprod, Cortex-A76         :   12.1% speed-up
armv8, Cortex-A72, Raspberry Pi 4 :    8.2% speed-up (thanks Torom!)

closes https://github.com/official-stockfish/Stockfish/pull/4719

No functional change
2023-08-06 21:22:37 +02:00
ppigazzini 4c43e1e27c Add new CPU archs in CI Tests workflow
Add CPU archs: armv8-dotprod, riscv64 and ppc64le.
The last two archs are built using QEMU multiarch docker container.

References:
https://docs.docker.com/build/building/multi-platform/
https://github.com/docker/setup-buildx-action
https://github.com/docker/setup-qemu-action
https://github.com/tonistiigi/binfmt
https://stackoverflow.com/questions/72444103/what-does-running-the-multiarch-qemu-user-static-does-before-building-a-containe

closes https://github.com/official-stockfish/Stockfish/pull/4718

No functional change
2023-08-06 21:17:33 +02:00
Niklas Fiekas 002a50457c Identify NEON_DOTPROD in compiler_info()
closes https://github.com/official-stockfish/Stockfish/pull/4712

No functional change
2023-08-06 21:14:39 +02:00
rn5f107s2 65ece7d985 Malus during move ordering for putting pieces en prise
The original idea is the reverse of a previous patch [1] which added bonuses
in our move picker to moves escaping threats. In this patch, in addition to
bonuses for evading threats, we apply penalties to moves moving to threatened
squares.

Further tweaks of that basic idea resulted in this specific version which
further increases the penalty of moves moving to squares threatend depending
on the piece threatening it. So for example a queen moving to a square attacked
by a pawn would receive a larger penalty than a queen moving to square attacked
by a rook.

[1]: https://github.com/official-stockfish/Stockfish/commit/08e0f52b77edb929989c68c49e954b9bc5d7d67e

--------

Passed STC:
https://tests.stockfishchess.org/tests/live_elo/64c11269dc56e1650abb935d
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 95552 W: 24654 L: 24250 D: 46648
Ptnml(0-2): 322, 11098, 24562, 11442, 352

Passed LTC:
https://tests.stockfishchess.org/tests/live_elo/64c2004ddc56e1650abba8b3
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 190230 W: 48806 L: 48178 D: 93246
Ptnml(0-2): 90, 20439, 53453, 21019, 114

-------

closes https://github.com/official-stockfish/Stockfish/pull/4711

Bench: 1350831
2023-07-29 00:56:26 +02:00
Stéphane Nicolet f84eb1f3ef Improve some comments
- clarify the examples for the bench command
- typo  in search.cpp

closes https://github.com/official-stockfish/Stockfish/pull/4710

No functional change
2023-07-28 23:38:49 +02:00
mstembera cb22520a9c Remove unused return type from propagate()
Also make two get_weight_index() static methods constexpr, for
consistency with the other static get_hash_value() method right above.
Tested for speed by user Torom (thanks).

closes https://github.com/official-stockfish/Stockfish/pull/4708

No functional change
2023-07-28 23:24:42 +02:00
FauziAkram 2667316ffc Simplify one multicut extension
Simplify away the ttValue <= alpha extension in the multicut block.

Passed STC:
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 318336 W: 81307 L: 81398 D: 155631
Ptnml(0-2): 1088, 37291, 82469, 37264, 1056
https://tests.stockfishchess.org/tests/view/64b8589fdc56e1650abad61d

Passed LTC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 89388 W: 22925 L: 22775 D: 43688
Ptnml(0-2): 34, 9635, 25210, 9777, 38
https://tests.stockfishchess.org/tests/view/64bc41d0dc56e1650abb29cb

closes https://github.com/official-stockfish/Stockfish/pull/4709

bench: 1604592
2023-07-25 16:43:35 +02:00
Stephen Touset 027713c4b4 Remove Zobrist::noPawns
Zobrist::noPawns is no longer used.

closes https://github.com/official-stockfish/Stockfish/pull/4344

no functional change
2023-07-25 00:13:38 +02:00
Stefan Geschwentner 76e1e8fd39 Simplify TT cutoff
Remove the exact bound condition from TT depth check.

STC:
https://tests.stockfishchess.org/tests/view/64b30b320cdec37b957359e9
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 332928 W: 84895 L: 85003 D: 163030
Ptnml(0-2): 1242, 39200, 85604, 39260, 1158

LTC:
https://tests.stockfishchess.org/tests/view/64b74e2adc56e1650abac0b6
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 92946 W: 23628 L: 23482 D: 45836
Ptnml(0-2): 38, 10033, 26192, 10165, 45

closes https://github.com/official-stockfish/Stockfish/pull/4702

Bench: 1601764
2023-07-24 02:23:43 +02:00
windfishballad 78e3d2ad78 Simplify some qsearch conditions
Use the assert which ensures that beta == alpha+1 at PVNodes
to simplify a little bit the conditions further down in the code.

passed STC:
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 56160 W: 14370 L: 14173 D: 27617
Ptnml(0-2): 210, 6192, 15076, 6395, 207
https://tests.stockfishchess.org/tests/view/64bc769cdc56e1650abb2e26

closes https://tests.stockfishchess.org/tests/view/64bc769cdc56e1650abb2e26

No functional change
2023-07-24 02:09:44 +02:00
Joost VandeVondele 4b2979760f Check clock more often
This patch changes the frequency with which the time is checked, changing
frequency from every 1024 counted nodes to every 512 counted nodes. The
master value was tuned for the old classical eval, the patch takes the
roughly 2x slowdown in nps with SFNNUEv7 into account. This could reduce
a bit the losses on time on fishtest, but they are probably unrelated.

passed STC:
https://tests.stockfishchess.org/tests/view/64bb8ae5dc56e1650abb1b11
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 76576 W: 19677 L: 19501 D: 37398
Ptnml(0-2): 274, 8592, 20396, 8736, 290

closes https://github.com/official-stockfish/Stockfish/pull/4704

No functional change
2023-07-24 01:56:20 +02:00
Michael Chaly 5ea1cbc778 Do more futility pruning for cutNodes that are not in TT
This is somewhat similar to IIR for cutnodes but instead of reducing
depth for cutnodes that don't have tt move we reduce margin multiplier
in futility pruning for cutnodes that are not in TT.

Passed STC:
https://tests.stockfishchess.org/tests/view/64b244b90cdec37b95734c5b
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 75552 W: 19404 L: 19029 D: 37119
Ptnml(0-2): 233, 8806, 19378, 9071, 288

Passed LTC:
https://tests.stockfishchess.org/tests/view/64b3ae5a0cdec37b95736516
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 104988 W: 27152 L: 26697 D: 51139
Ptnml(0-2): 41, 11259, 29446, 11700, 48

closes https://github.com/official-stockfish/Stockfish/pull/4700

bench 1727577
2023-07-19 21:40:38 +02:00
mstembera 1444837887 Remove inline assembly
closes https://github.com/official-stockfish/Stockfish/pull/4698

No functional change
2023-07-19 21:39:51 +02:00
Cody Ho 3fe0d5c533 Unused code cleanup
closes https://github.com/official-stockfish/Stockfish/pull/4696

No functional change
2023-07-19 21:38:39 +02:00
Jorge 6abd0bd9fb Add CodeQL workflow
add CI tooling to detect security bugs.

closes https://github.com/official-stockfish/Stockfish/pull/4659

No functional change
2023-07-19 21:36:38 +02:00
rn5f107s2 42d28424bc Removes a few Bitboards and functions
No longer used

closes https://github.com/official-stockfish/Stockfish/pull/4695

No functional change
2023-07-18 08:10:11 +02:00
Robert Nurnberg @ elitebook 6a31f54d3c remove evalType from bench
no longer used

closes https://github.com/official-stockfish/Stockfish/pull/4694

No functional change
2023-07-18 08:09:52 +02:00
Joost VandeVondele 34d0c1b527 Switch to macos 13 for CI
allows for building x86-64-avx2 and x86-64-bmi2 binaries for mac

install coreutils
show hardware capabilities as seen by the compilers
move some tests from sse41 to avx2 as platforms support it

closes https://github.com/official-stockfish/Stockfish/pull/4692

No functional change
2023-07-16 22:27:13 +02:00
Joost VandeVondele d70a905ce3 Deprecate the x86-64-modern arch
Explicitly describe the architecture as deprecated,
it remains available as its current alias x86-64-sse41-popcnt

CPUs that support just this instruction set are now years old,
any few years old Intel or AMD CPU supports x86-64-avx2. However,
naming things 'modern' doesn't age well, so instead use explicit names.

Adjust CI accordingly. Wiki, fishtest, downloader done as well.

closes https://github.com/official-stockfish/Stockfish/pull/4691

No functional change.
2023-07-16 17:47:25 +02:00
Michael Chaly 913574f421 Remove improvement variable
No longer used in a meaningful way.
Improve comments.

Closes https://github.com/official-stockfish/Stockfish/pull/4688

No functional change
2023-07-16 15:18:41 +02:00
maxim 18fdc2df3c Remove pawnKey from StateInfo
Remove pawnKey since it is not used anymore.

Passed Non-regression STC:
https://tests.stockfishchess.org/tests/view/64b023110cdec37b9573265c
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 334848 W: 85440 L: 85545 D: 163863
Ptnml(0-2): 1134, 38101, 89075, 37964, 1150

closes https://github.com/official-stockfish/Stockfish/pull/4687

No functional change
2023-07-16 15:16:14 +02:00
Joost VandeVondele e8a5c64988 Consolidate to centipawns conversion
avoid doing this calculations in multiple places.

closes https://github.com/official-stockfish/Stockfish/pull/4686

No functional change
2023-07-16 15:14:50 +02:00
Joost VandeVondele e89469925d Remove some CI parts not yet working
downgrading compiler didn't work for windows build. Stick to gcc 13 for now.
Windows x86-32 not a 32bit binary, remove.

closes https://github.com/official-stockfish/Stockfish/pull/4685

No functional change
2023-07-16 15:13:24 +02:00
AndrovT a42ab95e1f remove large input specialization
Removes unused large input specialization for dense affine transform. It has been obsolete since #4612 was merged.

closes https://github.com/official-stockfish/Stockfish/pull/4684

No functional change
2023-07-16 15:12:21 +02:00
Linmiao Xu ee53f8ed2f Reintroduce nnue eval pawn count multipliers again
With separate multipliers for nnue eval and optimism scaling.
This patch used 4 out of 7 params tuned with spsa at 30+0.3
using this tuning config:

Value LazyThreshold1 = Value(3622);
Value LazyThreshold2 = Value(1962);
int psqThresh = 2048;
int nnueNpmBase = 945;
int nnuePcMult = 0;
int optNpmBase = 150;
int optPcMult = 0;
TUNE(SetRange(3322, 3922), LazyThreshold1);
TUNE(SetRange(1662, 2262), LazyThreshold2);
TUNE(SetRange(1748, 2348), psqThresh);
TUNE(SetRange(745, 1145), nnueNpmBase);
TUNE(SetRange(-16, 16), nnuePcMult);
TUNE(SetRange(0, 300), optNpmBase);
TUNE(SetRange(-16, 16), optPcMult);

Passed STC:
https://tests.stockfishchess.org/tests/view/64a5a9b402cd07745c60ed07
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 173632 W: 44417 L: 43903 D: 85312
Ptnml(0-2): 547, 20025, 45068, 20719, 457

Passed LTC:
https://tests.stockfishchess.org/tests/view/64a972a302cd07745c6136af
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 277644 W: 70955 L: 70147 D: 136542
Ptnml(0-2): 193, 29902, 77787, 30784, 156

closes https://github.com/official-stockfish/Stockfish/pull/4681

bench 1556301
2023-07-15 09:16:09 +02:00
Joost VandeVondele a3a91f3f9f Build and test more binaries in CI
use a fixed compiler on Linux and Windows (right now gcc 11).
build avxvnni on Windows (Linux needs updated core utils)
build x86-32 on Linux (Windows needs other mingw)
fix a Makefile issue where a failed PGOBENCH would not stop the build
reuse the WINE_PATH for SDE as we do for QEMU
use WINE_PATH variable also for the signature
verify the bench for each of the binaries
do not build x86-64-avx2 on macos

closes https://github.com/official-stockfish/Stockfish/pull/4682

No functional change
2023-07-15 09:15:16 +02:00
FauziAkram acdbf45171 Use more expressive names for bonus1 and bonus2
closes https://github.com/official-stockfish/Stockfish/pull/4683

No functional change
2023-07-15 09:13:02 +02:00
Joost VandeVondele f5ab5832c6 Generate binaries for more advanced architectures
use intel's Software Development Emulator (SDE) in the actions that build the binaries.
This allows for building on Windows and Linux binaries for
- x86-64-avx512
- x86-64-vnni256
- x86-64-vnni512

(x86-64-avxvnni needs more recent gcc in the actions)

also build x86-64-avx2 on macos.

closes https://github.com/official-stockfish/Stockfish/pull/4679

No functional change
2023-07-13 08:21:17 +02:00
mstembera 529d3be8e2 More simplifications and cleanup in affine_transform_sparse_input.h
closes https://github.com/official-stockfish/Stockfish/pull/4677

No functional change
2023-07-13 08:20:33 +02:00
Sebastian Buchwald f972947492 Cleanup code after removal of classical evaluation
This includes the following changes:
- Remove declaration of removed global variable
- Adapt string that mentions removed UCI option

closes https://github.com/official-stockfish/Stockfish/pull/4675

No functional change
2023-07-13 08:19:37 +02:00
Joost VandeVondele af110e02ec Remove classical evaluation
since the introduction of NNUE (first released with Stockfish 12), we
have maintained the classical evaluation as part of SF in frozen form.
The idea that this code could lead to further inputs to the NN or
search did not materialize. Now, after five releases, this PR removes
the classical evaluation from SF. Even though this evaluation is
probably the best of its class, it has become unimportant for the
engine's strength, and there is little need to maintain this
code (roughly 25% of SF) going forward, or to expend resources on
trying to improve its integration in the NNUE eval.

Indeed, it had still a very limited use in the current SF, namely
for the evaluation of positions that are nearly decided based on
material difference, where the speed of the classical evaluation
outweights its inaccuracies. This impact on strength is small,
roughly 2Elo, and probably decreasing in importance as the TC grows.

Potentially, removal of this code could lead to the development of
techniques to have faster, but less accurate NN evaluation,
for certain positions.

STC
https://tests.stockfishchess.org/tests/view/64a320173ee09aa549c52157
Elo: -2.35 ± 1.1 (95%) LOS: 0.0%
Total: 100000 W: 24916 L: 25592 D: 49492
Ptnml(0-2): 287, 12123, 25841, 11477, 272
nElo: -4.62 ± 2.2 (95%) PairsRatio: 0.95

LTC
https://tests.stockfishchess.org/tests/view/64a320293ee09aa549c5215b
 Elo: -1.74 ± 1.0 (95%) LOS: 0.0%
Total: 100000 W: 25010 L: 25512 D: 49478
Ptnml(0-2): 44, 11069, 28270, 10579, 38
nElo: -3.72 ± 2.2 (95%) PairsRatio: 0.96

VLTC SMP
https://tests.stockfishchess.org/tests/view/64a3207c3ee09aa549c52168
 Elo: -1.70 ± 0.9 (95%) LOS: 0.0%
Total: 100000 W: 25673 L: 26162 D: 48165
Ptnml(0-2): 8, 9455, 31569, 8954, 14
nElo: -3.95 ± 2.2 (95%) PairsRatio: 0.95

closes https://github.com/official-stockfish/Stockfish/pull/4674

Bench: 1444646
2023-07-11 22:56:49 +02:00
Muzhen Gaming 6a8767a0d5 Simplify PvNode reduction
Simplification STC: https://tests.stockfishchess.org/tests/view/64a415803ee09aa549c539c3
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 37856 W: 9719 L: 9504 D: 18633
Ptnml(0-2): 98, 4277, 9977, 4464, 112

Simplification LTC: https://tests.stockfishchess.org/tests/view/64a5ffe202cd07745c60f360
LLR: 2.96 (-2.94,2.94) <-1.75,0.25>
Total: 55878 W: 14323 L: 14138 D: 27417
Ptnml(0-2): 21, 5993, 15732, 6166, 27

closes https://github.com/official-stockfish/Stockfish/pull/4673

Bench: 2604965
2023-07-11 22:55:00 +02:00
Joost VandeVondele ee023d7fd7 Fix CI output
closes https://github.com/official-stockfish/Stockfish/pull/4669

No functional change
2023-07-11 22:53:15 +02:00
Linmiao Xu e699fee513 Update default net to nn-c38c3d8d3920.nnue
This was a later epoch from the same experiment that led to the
previous master net. After training, it was prepared the same way:

1. greedy permuting L1 weights with https://github.com/official-stockfish/Stockfish/pull/4620
2. leb128 compression with https://github.com/glinscott/nnue-pytorch/pull/251
3. greedy 2- and 3- cycle permuting with https://github.com/official-stockfish/Stockfish/pull/4640

Local elo at 25k nodes per move (vs. L1-1536 nn-fdc1d0fe6455.nnue):
nn-epoch739.nnue : 20.2 +/- 1.7

Passed STC:
https://tests.stockfishchess.org/tests/view/64a050b33ee09aa549c4e4c8
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 195552 W: 49977 L: 49430 D: 96145
Ptnml(0-2): 556, 22775, 50607, 23242, 596

Passed LTC:
https://tests.stockfishchess.org/tests/view/64a127bd3ee09aa549c4f60c
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 235452 W: 60327 L: 59609 D: 115516
Ptnml(0-2): 119, 25173, 66426, 25887, 121

closes https://github.com/official-stockfish/Stockfish/pull/4666

bench 2427629
2023-07-06 23:03:58 +02:00
Joost VandeVondele 9ba24912c1 Add armv8-dotprod to CI binaries
also generate binaries for more recent Android hardware.

closes https://github.com/official-stockfish/Stockfish/pull/4663

No functional change
2023-07-06 23:03:16 +02:00
mstembera f8e65d82eb Simplify away lookup_count.
https://tests.stockfishchess.org/tests/view/64a3c1a93ee09aa549c53167
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 32832 W: 8497 L: 8280 D: 16055
Ptnml(0-2): 80, 3544, 8967, 3729, 96

closes https://github.com/official-stockfish/Stockfish/pull/4662

No functional change
2023-07-06 23:02:11 +02:00
Joost VandeVondele 19e2a88504 Revise extract bench from git log in CI
order commits differently

closes https://github.com/official-stockfish/Stockfish/pull/4668

No functional change
2023-07-06 23:01:27 +02:00
Joost VandeVondele 8c4ac26c8e Merge pull request #4661 from vondele/clusterMergeMaster17
Merge SF 16 in the cluster branch
2023-07-05 12:11:22 +02:00
Joost VandeVondele a2b24e0030 Merge branch 'sf16branch' into clusterMergeMaster17 2023-07-03 20:35:36 +02:00
ppigazzini e87e103ca9 Remove leftover braces for if conditional in CI
closes https://github.com/official-stockfish/Stockfish/pull/4660

No functional change
2023-07-03 20:17:14 +02:00
ppigazzini ca5d9a5ff0 Extract bench according to wiki instructions
- loop through the commits starting from the latest one
- read the bench value from the last match, if any, of the template
  in the commit body text

closes https://github.com/official-stockfish/Stockfish/pull/4627

No functional change
2023-07-03 19:07:06 +02:00
rn5f107s2 95ce443aaa simplified gives check castling
tested verifying perft and bench is unchanged
on a larger set of epds for both standard and FRC chess.

Passed non-regression STC:
https://tests.stockfishchess.org/tests/live_elo/648587be65ffe077ca123d78
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 153632 W: 41015 L: 40928 D: 71689
Ptnml(0-2): 377, 16077, 43816, 16174, 372

closes https://github.com/official-stockfish/Stockfish/pull/4628

No functional change
2023-07-03 18:54:22 +02:00
FauziAkram 9cd563cb54 Improving grammar and readability of comments
closes https://github.com/official-stockfish/Stockfish/pull/4643

No functional change
2023-07-03 18:38:41 +02:00
Muzhen Gaming 5f8480a730 Simplify score improvement reduction
Reduce depth by 2 based on score improvement, only for depths 3 to 11.

Simplification STC: https://tests.stockfishchess.org/tests/view/64929a53dc7002ce609c7807
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 238912 W: 63466 L: 63468 D: 111978
Ptnml(0-2): 564, 26262, 65805, 26262, 563

Simplification LTC: https://tests.stockfishchess.org/tests/view/64942e47dc7002ce609c9e07
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 64452 W: 17485 L: 17320 D: 29647
Ptnml(0-2): 19, 6161, 19706, 6316, 24

closes https://github.com/official-stockfish/Stockfish/pull/4637

Bench: 2740142
2023-07-03 18:33:27 +02:00
Muzhen Gaming eb9aaf9489 Simplify away improvement term in null move search
passed STC:
https://tests.stockfishchess.org/tests/view/649c0d2edc7002ce609d33b5
LLR: 2.95 (-2.94,2.94) <-1.75,0.25>
Total: 271104 W: 72181 L: 72217 D: 126706
Ptnml(0-2): 691, 30042, 74129, 29992, 698

passed LTC:
https://tests.stockfishchess.org/tests/view/649d0dd7dc7002ce609d4efa
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 183120 W: 49469 L: 49418 D: 84233
Ptnml(0-2): 84, 17636, 56063, 17699, 78

closes https://github.com/official-stockfish/Stockfish/pull/4650

Bench: 2642851
2023-07-03 18:27:33 +02:00
peregrineshahin fa143922ae Fix pruning to (in TB loss) in Null move pruning.
Current logic can apply Null move pruning
on a dead-lost position returning an unproven loss
(i.e. in TB loss score or mated in losing score) on nonPv nodes.

on a default bench, this can be observed by adding this debugging line:
```
if (nullValue >= beta)
{
    // Do not return unproven mate or TB scores
    nullValue = std::min(nullValue, VALUE_TB_WIN_IN_MAX_PLY-1);
    dbg_hit_on(nullValue <= VALUE_TB_LOSS_IN_MAX_PLY); // Hit #0: Total 73983 Hits 1 Hit Rate (%) 0.00135166
    if (thisThread->nmpMinPly || depth < 14)
        return nullValue;
```

This fixes this very rare issue (happens at ~0.00135166% of the time) by
eliminating the need to try Null Move Pruning with dead-lost positions
and leaving it to be determined by a normal searching flow.

The previous try to fix was not as safe enough because it was capping
the returned value to (out of TB range) thus reviving the dead-lost position
based on an artificial clamp (i.e. the in TB score/mate score can be lost on that nonPv node):
https://tests.stockfishchess.org/tests/view/649756d5dc7002ce609cd794

Final fix:
Passed STC:
https://tests.stockfishchess.org/tests/view/649a5446dc7002ce609d1049
LLR: 2.93 (-2.94,2.94) <-1.75,0.25>
Total: 577280 W: 153613 L: 153965 D: 269702
Ptnml(0-2): 1320, 60594, 165190, 60190, 1346

Passed LTC:
https://tests.stockfishchess.org/tests/view/649cd048dc7002ce609d4801
LLR: 2.94 (-2.94,2.94) <-1.75,0.25>
Total: 246432 W: 66769 L: 66778 D: 112885
Ptnml(0-2): 83, 22105, 78847, 22100, 81

closes https://github.com/official-stockfish/Stockfish/pull/4649

Bench: 2425978
2023-07-03 18:24:41 +02:00
mstembera 80564bcfcd Simplify lookup_count and clean up pieces().
https://github.com/official-stockfish/Stockfish/pull/4656

No functional change
2023-07-03 18:20:10 +02:00
disservin 8634717c64 Add bmi2 to CI generated binaries
verify bench for avx2 and bmi2 as well

closes https://github.com/official-stockfish/Stockfish/pull/4658

No functional change
2023-07-03 18:17:20 +02:00
ppigazzini 9a2d50eccc Make posix and msys2 shells consistent in CI
In CI, it is typical for the process to halt immediately when an error
is encountered. However, with our `shell: bash {0}` configuration,
the process continues despite errors for posix shells.
This commit updates the behavior of posix and msys2 shells to ensure
consistency in terms of pipeline exit codes and stop conditions.
We adopt the most appropriate default behavior as recommended
by the GitHub documentation.

Update the code that searches for the bench value in the git log:
- to be compatible with the new shell settings
- to retry the value from the first line that contains
  only the template and spaces/tabs/newlines

see also

https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell
https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference
https://github.com/msys2/setup-msys2/blob/main/main.js

closes https://github.com/official-stockfish/Stockfish/pull/4653

No functional change
2023-07-02 10:32:36 +02:00
Linmiao Xu 915532181f Update NNUE architecture to SFNNv7 with larger L1 size of 2048
Creating this net involved:
- a 5-step training process from scratch
- greedy permuting L1 weights with https://github.com/official-stockfish/Stockfish/pull/4620
- leb128 compression with https://github.com/glinscott/nnue-pytorch/pull/251
- greedy 2- and 3- cycle permuting with https://github.com/official-stockfish/Stockfish/pull/4640

The 5 training steps were:

1. 400 epochs, lambda 1.0, lr 9.75e-4
   UHOx2-wIsRight-multinet-dfrc-n5000-largeGensfen-d9.binpack (178G)
     nodes5000pv2_UHO.binpack
     data_pv-2_diff-100_nodes-5000.binpack
     wrongIsRight_nodes5000pv2.binpack
     multinet_pv-2_diff-100_nodes-5000.binpack
     dfrc_n5000.binpack
     large_gensfen_multipvdiff_100_d9.binpack
   ep399 chosen as start model for step2

2. 800 epochs, end-lambda 0.75, skip 16
   LeelaFarseer-T78juntoaugT79marT80dec.binpack (141G)
     T60T70wIsRightFarseerT60T74T75T76.binpack
     test78-junjulaug2022-16tb7p.no-db.min.binpack
     test79-mar2022-16tb7p.no-db.min.binpack
     test80-dec2022-16tb7p.no-db.min.binpack
   ep559 chosen as start model for step3

3. 800 epochs, end-lambda 0.725, skip 20
   leela96-dfrc99-v2-T80dectofeb-sk20-mar-v6-T77decT78janfebT79apr.binpack (223G)
     leela96-filt-v2.min.binpack
     dfrc99-16tb7p-eval-filt-v2.min.binpack
     test80-dec2022-16tb7p-filter-v6-sk20.min-mar2023.binpack
     test80-jan2023-16tb7p-filter-v6-sk20.min-mar2023.binpack
     test80-feb2023-16tb7p-filter-v6-sk20.min-mar2023.binpack
     test80-mar2023-2tb7p-filter-v6.min.binpack
     test77-dec2021-16tb7p.no-db.min.binpack
     test78-janfeb2022-16tb7p.no-db.min.binpack
     test79-apr2022-16tb7p.no-db.min.binpack
   ep499 chosen as start model for step4

4. 800 epochs, end-lambda 0.7, skip 24
   0dd1cebea57 dataset https://github.com/official-stockfish/Stockfish/pull/4606
   ep599 chosen as start model for step5

5. 800 epochs, end-lambda 0.7, skip 28
   same dataset as step4
   ep619 became nn-1b951f8b449d.nnue

For the final step5 training:

python3 easy_train.py \
  --experiment-name L1-2048-S5-sameData-sk28-S4-0dd1cebea57-shuffled-S3-leela96-dfrc99-v2-T80dectofeb-sk20-mar-v6-T77decT78janfebT79apr-sk20-S2-LeelaFarseerT78T79T80-ep399-S1-UHOx2-wIsRight-multinet-dfrc-n5000-largeGensfen-d9 \
  --training-dataset /data/leela96-dfrc99-T60novdec-v2-T80juntonovjanfebT79aprmayT78jantosepT77dec-v6dd-T80apr.binpack \
  --early-fen-skipping 28 \
  --nnue-pytorch-branch linrock/nnue-pytorch/misc-fixes-L1-2048 \
  --engine-test-branch linrock/Stockfish/L1-2048 \
  --start-from-engine-test-net False \
  --start-from-model /data/experiments/experiment_L1-2048-S4-0dd1cebea57-shuffled-S3-leela96-dfrc99-v2-T80dectofeb-sk20-mar-v6-T77decT78janfebT79apr-sk20-S2-LeelaFarseerT78T79T80-ep399-S1-UHOx2-wIsRight-multinet-dfrc-n5000-largeGensfen-d9/training/run_0/nn-epoch599.nnue
  --max_epoch 800 \
  --lr 4.375e-4 \
  --gamma 0.995 \
  --start-lambda 1.0 \
  --end-lambda 0.7 \
  --tui False \
  --seed $RANDOM \
  --gpus 0

SF training data components for the step1 dataset:
https://drive.google.com/drive/folders/1yLCEmioC3Xx9KQr4T7uB6GnLm5icAYGU

Leela training data for steps 2-5 can be found at:
https://robotmoon.com/nnue-training-data/

Due to larger L1 size and slower inference, the speed penalty loses elo
at STC. Measurements from 100 bench runs at depth 13 with x86-64-modern
on Intel Core i5-1038NG7 2.00GHz:

sf_base =  1240730  +/-   3443 (95%)
sf_test =  1153341  +/-   2832 (95%)
diff    =   -87388  +/-   1616 (95%)
speedup = -7.04330% +/- 0.130% (95%)

Local elo at 25k nodes per move (vs. L1-1536 nn-fdc1d0fe6455.nnue):
nn-epoch619.nnue : 21.1 +/- 3.2

Failed STC:
https://tests.stockfishchess.org/tests/view/6498ee93dc7002ce609cf979
LLR: -2.95 (-2.94,2.94) <0.00,2.00>
Total: 11680 W: 3058 L: 3299 D: 5323
Ptnml(0-2): 44, 1422, 3149, 1181, 44

LTC:
https://tests.stockfishchess.org/tests/view/649b32f5dc7002ce609d20cf
Elo: 0.68 ± 1.5 (95%) LOS: 80.5%
Total: 40000 W: 10887 L: 10809 D: 18304
Ptnml(0-2): 36, 3938, 11958, 4048, 20
nElo: 1.50 ± 3.4 (95%) PairsRatio: 1.02

Passed VLTC 180+1.8:
https://tests.stockfishchess.org/tests/view/64992b43dc7002ce609cfd20
LLR: 3.06 (-2.94,2.94) <0.00,2.00>
Total: 38086 W: 10612 L: 10338 D: 17136
Ptnml(0-2): 9, 3316, 12115, 3598, 5

Passed VLTC SMP 60+0.6 th 8:
https://tests.stockfishchess.org/tests/view/649a21fedc7002ce609d0c7d
LLR: 2.95 (-2.94,2.94) <0.50,2.50>
Total: 38936 W: 11091 L: 10820 D: 17025
Ptnml(0-2): 1, 2948, 13305, 3207, 7

closes https://github.com/official-stockfish/Stockfish/pull/4646

Bench: 2505168
2023-07-01 13:34:30 +02:00
cj5716 e551964ef6 Negative extension on cutNodes based on depth
This patch was inspired by candirufish original attempt at negative extensions
here that failed yellow: https://tests.stockfishchess.org/tests/view/6486529065ffe077ca124f32

I tested some variations of the idea and tuned a depth condition for
a modified version of it here https://tests.stockfishchess.org/tests/view/648db80a91c58631ce31fe00
after noticing abnormal scaling (ie many passed STC but not LTC)
After some small tweaks I got the final version here

Passed STC:
LLR: 2.98 (-2.94,2.94) <0.00,2.00>
Total: 122208 W: 32776 L: 32350 D: 57082
Ptnml(0-2): 310, 13250, 33553, 13686, 305
https://tests.stockfishchess.org/tests/view/64997934dc7002ce609d01e3

Passed LTC:
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 145092 W: 39617 L: 39115 D: 66360
Ptnml(0-2): 54, 13691, 44552, 14197, 52
https://tests.stockfishchess.org/tests/view/649a1c5ddc7002ce609d0bff

closes https://github.com/official-stockfish/Stockfish/pull/4644

Bench: 2637784
2023-07-01 13:06:49 +02:00
Stéphane Nicolet e355c70594 Document the LEB128 patch
Add some comments and harmonize style for the LEB128 patch.

closes https://github.com/official-stockfish/Stockfish/pull/4642

No functional change
2023-07-01 13:01:28 +02:00
Daniel Monroe ef94f77f8c Update default net to nn-a3d1bfca1672.nnue
faster permutation of master net weights

Activation data taken from https://drive.google.com/drive/folders/1Ec9YuuRx4N03GPnVPoQOW70eucOKngQe?usp=sharing
Permutation found using https://github.com/Ergodice/nnue-pytorch/blob/836387a0e5e690431d404158c46648710f13904d/ftperm.py
See also https://github.com/glinscott/nnue-pytorch/pull/254

The algorithm greedily selects 2- and 3-cycles that can be permuted to increase the number of runs of zeroes. The percent of zero runs from the master net increased from 68.46 to 70.11 from 2-cycles and only increased to 70.32 when considering 3-cycles. Interestingly, allowing both halves of L1 to intermix when creating zero runs can give another 0.5% zero-run density increase with this method.

Measured speedup:

```
CPU: 16 x AMD Ryzen 9 3950X 16-Core Processor
Result of 50 runs

base (./stockfish.master ) = 1561556 +/- 5439
test (./stockfish.patch ) = 1575788 +/- 5427
diff = +14231 +/- 2636

speedup = +0.0091
P(speedup > 0) = 1.0000
```

closes https://github.com/official-stockfish/Stockfish/pull/4640

No functional change
2023-07-01 12:59:28 +02:00
Joost VandeVondele 0fd186fb28 Restore development
closes https://github.com/official-stockfish/Stockfish/pull/4651

No functional change
2023-07-01 12:52:31 +02:00
Joost VandeVondele 66b4e7f080 Merge pull request #4349 from vondele/clusterMergeMaster16
Cluster: merge master up to SF15.1
2023-01-21 16:57:10 +01:00
Joost VandeVondele 04a0be956d Merge branch 'master' into clusterMergeMaster16 2022-12-04 16:43:04 +01:00
Joost VandeVondele f327096cfb Merge branch 'master' into clusterMergeMaster15
fix small merge conflicts, lightly tested:
Score of cluster vs master: 1 - 0 - 59  [0.508] 60
Elo difference: 5.8 +/- 11.2, LOS: 84.1 %, DrawRatio: 98.3 %
2022-10-30 16:12:47 +01:00
Joost VandeVondele e592dcb8e3 Merge branch 'master' into clusterMergeMaster14
closes https://github.com/official-stockfish/Stockfish/pull/3980
2022-04-16 08:31:47 +02:00
Joost VandeVondele 80eae02603 Merge pull request #3789 from vondele/fixNodeCount
[cluster] fix node count in bench command
2021-11-13 17:17:17 +01:00
Joost VandeVondele 0ad7de3182 [cluster] fix node count in bench command
bugfix use the cluster wide node count in bench for reporting

No function change
2021-11-13 17:14:27 +01:00
Joost VandeVondele e0b4dc24c8 Merge pull request #3771 from vondele/clusterMergeMaster13
Cluster: merge SF 14.1
2021-10-31 21:38:23 +01:00
Joost VandeVondele b79ec2e0b2 Merge branch 'master' into clusterMergeMaster13
brings the cluster branch to SF 14.1

Fixes minor conflicts

local testing cluster 4x4T vs master 4T, 10+0.1s, noob_3moves:

Score of cluster vs master: 6 - 0 - 94  [0.530] 100
Elo difference: 20.9 +/- 16.2, LOS: 99.3 %, DrawRatio: 94.0 %

No functional change
2021-10-31 16:17:24 +01:00
Stéphane Nicolet 43c887d367 Merge branch 'vondele-clusterMergeMaster12' into cluster 2021-03-17 11:22:02 +01:00
Joost VandeVondele 3a187b863b Merge branch 'master' into clusterMergeMaster12
fixes minor merge conflicts

looks good:

Score of cluster vs master: 15 - 1 - 134  [0.547] 150
Elo difference: 32.5 +/- 17.5, LOS: 100.0 %, DrawRatio: 89.3 %

for 4 threads against 4x4 threads at 10+0.1s
2021-03-16 20:49:19 +01:00
Joost VandeVondele 5fcd0e6f2a Merge branch 'master' into clusterMergeMaster11
fixes minor conflicts.
2020-09-17 19:17:37 +02:00
Joost VandeVondele 19129473f2 Merge NNUE (master) in the cluster branch.
fixes minor merge conflicts, and a first quick testing looks OK:

4mpix4th vs 4th at 30+0.3s:

Score of cluster vs master: 3 - 0 - 37  [0.537] 40
Elo difference: 26.1 +/- 28.5, LOS: 95.8 %, DrawRatio: 92.5 %

No functional change.
2020-08-19 23:30:37 +02:00
Joost VandeVondele b706b91bb1 Update cluster branch to latest master
Fixes a few merge conflicts.

Verified equal bench for 1 rank, and expected performance master vs cluster with 2 ranks.

Score of cluster vs master: 196 - 54 - 400  [0.609] 650
Elo difference: 77.1 +/- 16.3, LOS: 100.0 %, DrawRatio: 61.5 %

No functional change.
2020-06-28 13:04:21 +02:00
Joost VandeVondele 8ec5faa46e Merge branch 'master' into clusterMergeMaster8 2020-01-18 08:26:43 +01:00
Joost VandeVondele 8a9d269855 Merge remote-tracking branch 'upstream/master' into clusterMergeMaster7 2019-10-20 09:20:34 +02:00
Joost VandeVondele 0b3c13107a Merge branch 'master' into clusterMergeMaster6 2019-07-11 15:26:46 +02:00
Joost VandeVondele 669074672c Some doc changes. No functional change. 2019-07-05 14:47:33 +02:00
Joost VandeVondele 0fd0e4e849 Merge branch 'master' into clusterMergeMaster6 2019-07-01 16:36:58 +02:00
Joost VandeVondele 85327828c9 Merge branch 'master' into clusterMergeMaster5 2019-05-01 08:23:22 +02:00
Joost VandeVondele 4cdb6386d8 Merge branch 'master' into clusterMergeMaster5 2019-03-21 07:13:00 +01:00
Joost VandeVondele 982880bd70 Merge remote-tracking branch 'upstream/master' into clusterMergeMaster4 2019-02-17 13:18:08 +01:00
Joost VandeVondele bf17a410ec [Cluster] Use a sendrecv ring instead of allgather
Using point to point instead of a collective improves performance, and might be more flexible for future improvements.
Also corrects the condition for the number elements required to fill the send buffer.

The actual Elo gains depends a bit on the setup used for testing.

8mpi x 32t yields 141 - 102 - 957 ~ 11 Elo
8mpi x 1t yields 70 +- 9 Elo.
2019-01-24 10:39:24 +01:00
Joost VandeVondele 5e7777e9d0 [Cluster] adds missing line
one-liner fixes a merge error, resulting in a garbage output line. No influence on play.
2019-01-17 08:06:25 +01:00
Joost VandeVondele 10a920d7d7 [cluster] Improve user documentation
- add cluster info line
- provides basic info on positions received/stored in a cluster run,
  useful to judge performance.
- document most cluster functionality in the readme.md

No functional change
2019-01-14 09:11:33 +01:00
Joost VandeVondele 21819b7bf8 Merge branch 'master' into clusterMergeMaster3 2019-01-09 21:52:30 +01:00
Joost VandeVondele 8c4338ae49 [Cluster] Param tweak.
Small tweak of parameters, yielding some Elo.

The cluster branch can now be considered to be in good shape. In local testing, it runs stable for >30k games. Performance benefits from an MPI implementation that is able to make asynchronous progress. The code should be run with 1 MPI rank per node, and threaded on the node.

Performance against master has now been measured. Master has been given 1 node with 32 cores/threads in standard SMP, the cluster branch has been given N=2..20 of those nodes, running the corresponding number of MPI processes, each with 32 threads. Time control has been 10s+0.1s, Hash 8MB/core, the book 8moves_v3.pgn, the number of games 400.

```
Score of cluster-2mpix32t vs master-32t: 96 - 27 - 277  [0.586] 400
Elo difference: 60.54 +/- 18.49

Score of cluster-3mpix32t vs master-32t: 101 - 18 - 281  [0.604] 400
Elo difference: 73.16 +/- 17.94

Score of cluster-4mpix32t vs master-32t: 126 - 18 - 256  [0.635] 400
Elo difference: 96.19 +/- 19.68

Score of cluster-5mpix32t vs master-32t: 110 - 5 - 285  [0.631] 400
Elo difference: 93.39 +/- 17.09

Score of cluster-6mpix32t vs master-32t: 117 - 9 - 274  [0.635] 400
Elo difference: 96.19 +/- 18.06

Score of cluster-7mpix32t vs master-32t: 142 - 10 - 248  [0.665] 400
Elo difference: 119.11 +/- 19.89

Score of cluster-8mpix32t vs master-32t: 125 - 14 - 261  [0.639] 400
Elo difference: 99.01 +/- 19.18

Score of cluster-9mpix32t vs master-32t: 137 - 7 - 256  [0.662] 400
Elo difference: 117.16 +/- 19.20

Score of cluster-10mpix32t vs master-32t: 145 - 8 - 247  [0.671] 400
Elo difference: 124.01 +/- 19.86

Score of cluster-16mpix32t vs master-32t: 153 - 6 - 241  [0.684] 400
Elo difference: 133.95 +/- 20.17

Score of cluster-20mpix32t vs master-32t: 134 - 8 - 258  [0.657] 400
Elo difference: 113.29 +/- 19.11
```

As the cluster parallelism is essentially lazyMPI, the nodes per second has been verified to scale perfectly to large node counts. Unfortunately, that is not necessarily indicative of playing strength. In the following 2min search from startPos, we reach about 4.8Gnps (128 nodes).

```
info depth 38 seldepth 51 multipv 1 score cp 53 nodes 576165794092 nps 4801341606 hashfull 1000 tbhits 0 time 120001 pv e2e4 c7c5 g1f3 d7d6 f1b5 c8d7 b5d7 d8d7 c2c4 b8c6 b1c3 g8f6 d2d4 d7g4 d4d5 c6d4 f3d4 g4d1 e1d1 c5d4 c3b5 a8c8 b2b3 a7a6 b5d4 f6e4 d1e2 g7g6 c1e3 f8g7 a1c1 e4c5 f2f3 f7f5 h1d1 e8g8 d4c2 c5d7 a2a4 a6a5 e3d4 f5f4 d4f2 f8f7 h2h3 d7c5
```
2019-01-06 15:38:31 +01:00
Joost VandeVondele 8a3f8e21ae [Cluster] Move IO to the root.
Fixes one TODO, by moving the IO related to bestmove to the root, even if this move is found by a different rank.

This is needed to make sure IO from different ranks is ordered properly. If this is not done it is possible that e.g. a bestmove arrives before all info lines have been received, leading to output that confuses tools and humans alike (see e.g. https://github.com/cutechess/cutechess/issues/472)
2019-01-04 14:56:04 +01:00
Joost VandeVondele 267ca781cd Always wait before posting the next call in _sync. 2019-01-02 11:16:24 +01:00
Joost VandeVondele ac43bef5c5 [Cluster] Improve message passing part.
This rewrites in part the message passing part, using in place gather, and collecting, rather than merging, the data of all threads.

neutral with a single thread per rank:
Score of new-2mpi-1t vs old-2mpi-1t: 789 - 787 - 2615  [0.500] 4191
Elo difference: 0.17 +/- 6.44

likely progress with multiple threads per rank:
Score of new-2mpi-36t vs old-2mpi-36t: 76 - 53 - 471  [0.519] 600
Elo difference: 13.32 +/- 12.85
2019-01-02 11:16:24 +01:00
Joost VandeVondele 7a32d26d5f [cluster] keep track of TB hits cluster-wide. 2018-12-29 15:34:57 +01:00
Joost VandeVondele fb5c1f5bf5 Fix comment 2018-12-29 15:34:57 +01:00
Joost VandeVondele 87f0fa55a0 [cluster] keep track of node counts cluster-wide.
This generalizes exchange of signals between the ranks using a non-blocking all-reduce. It is now used for the stop signal and the node count, but should be easily generalizable (TB hits, and ponder still missing). It avoids having long-lived outstanding non-blocking collectives (removes an early posted Ibarrier). A bit too short a test, but not worse than before:

Score of new-r4-1t vs old-r4-1t: 459 - 401 - 1505  [0.512] 2365
Elo difference: 8.52 +/- 8.43
2018-12-29 15:34:57 +01:00
Joost VandeVondele 2f882309d5 fixup 2018-12-29 15:34:57 +01:00
Joost VandeVondele 86953b9392 [cluster] Fix non-mpi compile
fix compile of the cluster branch in the non-mpi case.

Add a TODO as a reminder for the new voting scheme.

No functional changes
2018-12-29 15:34:56 +01:00
Joost VandeVondele ba1c639836 [cluster] fill sendbuffer better
use a counter to track available elements.

Some elo gain, on 4 ranks:

Score of old-r4-1t vs new-r4-1t: 422 - 508 - 1694  [0.484] 2624
Elo difference: -11.39 +/- 7.90
2018-12-29 15:34:56 +01:00
Joost VandeVondele e526c5aa52 [cluster] Make bench compatible
Fix one TODO.

Takes care of output from bench.
Sum nodes over ranks.
2018-12-29 15:34:56 +01:00
Joost VandeVondele 9cd2c817db Add one more TODO 2018-12-29 15:34:56 +01:00
Joost VandeVondele 54a0a228f6 [cluster] Some formatting cleanup
standarize whitespace a bit.
Also adds two TODOs for follow up work.

No functional change.
2018-12-29 15:34:56 +01:00
Joost VandeVondele 1cd2c7861a [cluster] avoid creating MPI data type.
there is no need to make an MPI data type for the sendbuffer, simpler and faster.

No functional change
2018-12-29 15:34:56 +01:00
Joost VandeVondele 7af3f4da7a [cluster] Avoid TT saving our own TT entries.
avoid saving to TT the part of the receive buffer that actually originates from the same rank.

Now, on 1 mpi rank, we have the same bench as the non-mpi code on 1 thread.
2018-12-29 15:34:56 +01:00
Joost VandeVondele 271181bb31 [cluster] Add depth condition to cluster TT saves.
since the logic for saving moves in the sendbuffer and the associated rehashing is expensive, only do it for TT stores of sufficient depth.

quite some gain in local testing with 4 ranks against the previous version.
Elo difference: 288.84 +/- 21.98

This starts to make the branch useful, but for on-node runs, difference remains to the standard threading.
2018-12-29 15:34:56 +01:00
noobpwnftw 66b2c6b9f1 Implement best move voting system for cluster
This implements the cluster version of https://github.com/official-stockfish/Stockfish/commit/d96c1c32a2fa109e7cc6cd07f6029cd13977121e
2018-12-29 15:34:56 +01:00
Joost VandeVondele 2559c20c6e [cluster] Fix oversight in TT key reuse
In the original code, the position key stored in the TT is used to probe&store TT entries after message passing. Since we only store part of the bits in the TT, this leads to incorrect rehashing. This is fixed in this patch storing also the full key in the send buffers, and using that for hashing after message arrival.

Short testing with 4 ranks (old vs new) shows this is effective:
Score of mpiold vs mpinew: 84 - 275 - 265  [0.347] 624
Elo difference: -109.87 +/- 20.88
2018-12-29 15:34:55 +01:00
Joost VandeVondele 2659c407c4 Fix segfault.
the wrong data type was passed to an MPI call, leading to occasional segfaults. This patch fixes this.

No functional change.
2018-12-29 15:34:55 +01:00
noobpwnftw 3730ae1efb Small simplifications and code cleanup
Non-functional simplifications.
2018-12-29 15:34:55 +01:00
noobpwnftw 0d6cdc0c6d Implement yielding loop while waiting for input
Some MPI implementations use busy-wait pooling, which will turn MPI_Bcast into busy-wait loop, workaround with our own yielding loop.
2018-12-29 15:34:55 +01:00
noobpwnftw 80afeb0d3b Fix consistency between PV and bestmove output
In case that a non-root mainThread on a node is the new best thread in the cluster, it should always output its PV.
2018-12-29 15:34:55 +01:00
noobpwnftw 2405b38165 Fix search result aggregation
This reverts my earlier change that only the root node gets to output best move after fixing problem with MPI_Allreduce by our custom operator(BestMoveOp). This function is not commutable and we must ensure that its output is consistent among all nodes.
2018-12-29 15:34:55 +01:00
noobpwnftw 8a95d269eb Implement proper stop signalling from root node
Previous behavior was to wait on all nodes to finish their search on their own TM and aggregate to root node via a blocking MPI_Allreduce call. This seems to be problematic.

In this commit a proper non-blocking signalling barrier was implemented to use TM from root node to control the cluster search, and disable TM on all non-root nodes.

Also includes some cosmetic fix to the nodes/NPS display.
2018-12-29 15:34:55 +01:00
noobpwnftw 3b7b632aa5 Fix a bug of outputting multiple lines of bestmove 2018-12-29 15:34:55 +01:00
Omri Mor 29c166a072 MPI/Cluster implementation for Stockfish
Based on Peter Österlund's "Lazy Cluster" algorithm,
but with some simplifications.
To compile, point COMPCXX to the MPI C++ compiler wrapper (mpicxx).
2018-12-29 15:34:55 +01:00
90 changed files with 11940 additions and 12960 deletions
+44
View File
@@ -0,0 +1,44 @@
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: Consecutive
AlignConsecutiveDeclarations: Consecutive
AlignEscapedNewlines: DontAlign
AlignOperands: AlignAfterOperator
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AlwaysBreakTemplateDeclarations: Yes
BasedOnStyle: WebKit
BitFieldColonSpacing: After
BinPackParameters: false
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: false
AfterClass: false
AfterControlStatement: true
BeforeElse: true
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: AfterColon
BreakStringLiterals: false
ColumnLimit: 100
ContinuationIndentWidth: 2
Cpp11BracedListStyle: true
IndentGotoLabels: false
IndentPPDirectives: BeforeHash
IndentWidth: 4
MaxEmptyLinesToKeep: 2
NamespaceIndentation: None
PackConstructorInitializers: Never
ReflowComments: false
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: false
SpaceBeforeCaseColon: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeInheritanceColon: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 2
+7
View File
@@ -0,0 +1,7 @@
# .git-blame-ignore-revs
# Ignore commit which added clang-format
2d0237db3f0e596fb06e3ffbadba84dcc4e018f6
# Post commit formatting fixes
0fca5605fa2e5e7240fde5e1aae50952b2612231
08ed4c90db31959521b7ef3186c026edd1e90307
+1 -1
View File
@@ -2,7 +2,7 @@ blank_issues_enabled: false
contact_links: contact_links:
- name: Discord server - name: Discord server
url: https://discord.gg/GWDRS3kU6R url: https://discord.gg/GWDRS3kU6R
about: Feel free to ask for support or have a chat with us in our Discord server! about: Feel free to ask for support or have a chat with us on our Discord server!
- name: Discussions, Q&A, ideas, show us something... - name: Discussions, Q&A, ideas, show us something...
url: https://github.com/official-stockfish/Stockfish/discussions/new url: https://github.com/official-stockfish/Stockfish/discussions/new
about: Do you have an idea for Stockfish? Do you want to show something that you made? Please open a discussion about it! about: Do you have an idea for Stockfish? Do you want to show something that you made? Please open a discussion about it!
+51
View File
@@ -0,0 +1,51 @@
{
"config": [
{
"name": "Android NDK aarch64",
"os": "ubuntu-22.04",
"simple_name": "android",
"compiler": "aarch64-linux-android21-clang++",
"emu": "qemu-aarch64",
"comp": "ndk",
"shell": "bash",
"archive_ext": "tar"
},
{
"name": "Android NDK arm",
"os": "ubuntu-22.04",
"simple_name": "android",
"compiler": "armv7a-linux-androideabi21-clang++",
"emu": "qemu-arm",
"comp": "ndk",
"shell": "bash",
"archive_ext": "tar"
}
],
"binaries": ["armv8-dotprod", "armv8", "armv7", "armv7-neon"],
"exclude": [
{
"binaries": "armv8-dotprod",
"config": {
"compiler": "armv7a-linux-androideabi21-clang++"
}
},
{
"binaries": "armv8",
"config": {
"compiler": "armv7a-linux-androideabi21-clang++"
}
},
{
"binaries": "armv7",
"config": {
"compiler": "aarch64-linux-android21-clang++"
}
},
{
"binaries": "armv7-neon",
"config": {
"compiler": "aarch64-linux-android21-clang++"
}
}
]
}
+21
View File
@@ -0,0 +1,21 @@
[
# Mappings for libcxx's internal headers
{ include: [ "<__fwd/fstream.h>", private, "<iosfwd>", public ] },
{ include: [ "<__fwd/ios.h>", private, "<iosfwd>", public ] },
{ include: [ "<__fwd/istream.h>", private, "<iosfwd>", public ] },
{ include: [ "<__fwd/ostream.h>", private, "<iosfwd>", public ] },
{ include: [ "<__fwd/sstream.h>", private, "<iosfwd>", public ] },
{ include: [ "<__fwd/streambuf.h>", private, "<iosfwd>", public ] },
{ include: [ "<__fwd/string_view.h>", private, "<string_view>", public ] },
# Mappings for includes between public headers
{ include: [ "<ios>", public, "<iostream>", public ] },
{ include: [ "<streambuf>", public, "<iostream>", public ] },
{ include: [ "<istream>", public, "<iostream>", public ] },
{ include: [ "<ostream>", public, "<iostream>", public ] },
{ include: [ "<iosfwd>", public, "<iostream>", public ] },
# Missing mappings in include-what-you-use's libcxx.imp
{ include: ["@<__condition_variable/.*>", private, "<condition_variable>", public ] },
{ include: ["@<__mutex/.*>", private, "<mutex>", public ] },
]
+160
View File
@@ -0,0 +1,160 @@
{
"config": [
{
"name": "Ubuntu 20.04 GCC",
"os": "ubuntu-20.04",
"simple_name": "ubuntu",
"compiler": "g++",
"comp": "gcc",
"shell": "bash",
"archive_ext": "tar",
"sde": "/home/runner/work/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.27.0-2023-09-13-lin/sde -future --"
},
{
"name": "MacOS 13 Apple Clang",
"os": "macos-13",
"simple_name": "macos",
"compiler": "clang++",
"comp": "clang",
"shell": "bash",
"archive_ext": "tar"
},
{
"name": "MacOS 14 Apple Clang M1",
"os": "macos-14",
"simple_name": "macos-m1",
"compiler": "clang++",
"comp": "clang",
"shell": "bash",
"archive_ext": "tar"
},
{
"name": "Windows 2022 Mingw-w64 GCC x86_64",
"os": "windows-2022",
"simple_name": "windows",
"compiler": "g++",
"comp": "mingw",
"msys_sys": "mingw64",
"msys_env": "x86_64-gcc",
"shell": "msys2 {0}",
"ext": ".exe",
"sde": "/d/a/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.27.0-2023-09-13-win/sde.exe -future --",
"archive_ext": "zip"
}
],
"binaries": [
"x86-64",
"x86-64-sse41-popcnt",
"x86-64-avx2",
"x86-64-bmi2",
"x86-64-avxvnni",
"x86-64-avx512",
"x86-64-vnni256",
"x86-64-vnni512",
"apple-silicon"
],
"exclude": [
{
"binaries": "x86-64",
"config": {
"os": "macos-14"
}
},
{
"binaries": "x86-64-sse41-popcnt",
"config": {
"os": "macos-14"
}
},
{
"binaries": "x86-64-avx2",
"config": {
"os": "macos-14"
}
},
{
"binaries": "x86-64-bmi2",
"config": {
"os": "macos-14"
}
},
{
"binaries": "x86-64-avxvnni",
"config": {
"os": "macos-14"
}
},
{
"binaries": "x86-64-avxvnni",
"config": {
"os": "macos-14"
}
},
{
"binaries": "x86-64-avx512",
"config": {
"os": "macos-14"
}
},
{
"binaries": "x86-64-vnni256",
"config": {
"os": "macos-14"
}
},
{
"binaries": "x86-64-vnni512",
"config": {
"os": "macos-14"
}
},
{
"binaries": "x86-64-avxvnni",
"config": {
"ubuntu-20.04": null
}
},
{
"binaries": "x86-64-avxvnni",
"config": {
"os": "macos-13"
}
},
{
"binaries": "x86-64-avx512",
"config": {
"os": "macos-13"
}
},
{
"binaries": "x86-64-vnni256",
"config": {
"os": "macos-13"
}
},
{
"binaries": "x86-64-vnni512",
"config": {
"os": "macos-13"
}
},
{
"binaries": "apple-silicon",
"config": {
"os": "windows-2022"
}
},
{
"binaries": "apple-silicon",
"config": {
"os": "macos-13"
}
},
{
"binaries": "apple-silicon",
"config": {
"os": "ubuntu-20.04"
}
}
]
}
+94
View File
@@ -0,0 +1,94 @@
name: Compilation
on:
workflow_call:
inputs:
matrix:
type: string
required: true
jobs:
Compilation:
name: ${{ matrix.config.name }} ${{ matrix.binaries }}
runs-on: ${{ matrix.config.os }}
env:
COMPILER: ${{ matrix.config.compiler }}
COMP: ${{ matrix.config.comp }}
EMU: ${{ matrix.config.emu }}
EXT: ${{ matrix.config.ext }}
BINARY: ${{ matrix.binaries }}
strategy:
fail-fast: false
matrix: ${{ fromJson(inputs.matrix) }}
defaults:
run:
working-directory: src
shell: ${{ matrix.config.shell }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download required linux packages
if: runner.os == 'Linux'
run: |
sudo apt update
sudo apt install qemu-user
- name: Install NDK
if: runner.os == 'Linux'
run: |
if [ $COMP == ndk ]; then
NDKV="21.4.7075529"
ANDROID_ROOT=/usr/local/lib/android
ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk
SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager
echo "y" | $SDKMANAGER "ndk;$NDKV"
ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$NDKV
ANDROID_NDK_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin
echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV
fi
- name: Extract the bench number from the commit history
run: |
for hash in $(git rev-list -100 HEAD); do
benchref=$(git show -s $hash | tac | grep -m 1 -o -x '[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*' | sed 's/[^0-9]//g') && break || true
done
[[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $hash" && echo "Reference bench: $benchref" || echo "No bench found"
- name: Download the used network from the fishtest framework
run: make net
- name: Check compiler
run: |
if [ $COMP == ndk ]; then
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
fi
$COMPILER -v
- name: Test help target
run: make help
- name: Check git
run: git --version
# Compile profile guided builds
- name: Compile ${{ matrix.binaries }} build
run: |
if [ $COMP == ndk ]; then
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument"
fi
make clean
make -j4 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH=$EMU
make strip ARCH=$BINARY COMP=$COMP
WINE_PATH=$EMU ../tests/signature.sh $benchref
mv ./stockfish$EXT ../stockfish-android-$BINARY$EXT
- name: Remove non src files
run: git clean -fx
- name: Upload artifact for (pre)-release
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }}
path: .
+51
View File
@@ -0,0 +1,51 @@
# This workflow will run clang-format and comment on the PR.
# Because of security reasons, it is crucial that this workflow
# executes no shell script nor runs make.
# Read this before editing: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
name: Clang-Format
on:
pull_request_target:
branches:
- "master"
paths:
- "**.cpp"
- "**.h"
jobs:
Clang-Format:
name: Clang-Format
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Run clang-format style check
uses: jidicula/clang-format-action@f62da5e3d3a2d88ff364771d9d938773a618ab5e # @v4.11.0
id: clang-format
continue-on-error: true
with:
clang-format-version: "17"
exclude-regex: "incbin"
- name: Comment on PR
if: steps.clang-format.outcome == 'failure'
uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # @v2.5.0
with:
message: |
clang-format 17 needs to be run on this PR.
If you do not have clang-format installed, the maintainer will run it when merging.
For the exact version please see https://packages.ubuntu.com/mantic/clang-format-17.
_(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_
comment_tag: execution
- name: Comment on PR
if: steps.clang-format.outcome != 'failure'
uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # @v2.5.0
with:
message: |
_(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_
create_if_not_exists: false
comment_tag: execution
mode: delete
+53
View File
@@ -0,0 +1,53 @@
name: "CodeQL"
on:
push:
branches: ["master"]
pull_request:
# The branches below must be a subset of the branches above
branches: ["master"]
schedule:
- cron: "17 18 * * 1"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: ["cpp"]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin, or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
- name: Build
working-directory: src
run: make -j build ARCH=x86-64-modern
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
+89
View File
@@ -0,0 +1,89 @@
name: Compilation
on:
workflow_call:
inputs:
matrix:
type: string
required: true
jobs:
Compilation:
name: ${{ matrix.config.name }} ${{ matrix.binaries }}
runs-on: ${{ matrix.config.os }}
env:
COMPILER: ${{ matrix.config.compiler }}
COMP: ${{ matrix.config.comp }}
EXT: ${{ matrix.config.ext }}
NAME: ${{ matrix.config.simple_name }}
BINARY: ${{ matrix.binaries }}
SDE: ${{ matrix.config.sde }}
strategy:
fail-fast: false
matrix: ${{ fromJson(inputs.matrix) }}
defaults:
run:
working-directory: src
shell: ${{ matrix.config.shell }}
steps:
- uses: actions/checkout@v4
- name: Install fixed GCC on Linux
if: runner.os == 'Linux'
uses: egor-tensin/setup-gcc@eaa888eb19115a521fa72b65cd94fe1f25bbcaac # @v1.3
with:
version: 11
- name: Setup msys and install required packages
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.config.msys_sys }}
install: mingw-w64-${{ matrix.config.msys_env }} make git zip
- name: Download SDE package
if: runner.os == 'Linux' || runner.os == 'Windows'
uses: petarpetrovt/setup-sde@91a1a03434384e064706634125a15f7446d2aafb # @v2.3
with:
environmentVariableName: SDE_DIR
sdeVersion: 9.27.0
- name: Download the used network from the fishtest framework
run: make net
- name: Check compiler
run: $COMPILER -v
- name: Test help target
run: make help
- name: Check git
run: git --version
- name: Check compiler
run: $COMPILER -v
- name: Show g++ cpu info
if: runner.os != 'macOS'
run: g++ -Q -march=native --help=target
- name: Show clang++ cpu info
if: runner.os == 'macOS'
run: clang++ -E - -march=native -###
# x86-64 with newer extensions tests
- name: Compile ${{ matrix.config.binaries }} build
run: |
make clean
make -j4 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH="$SDE"
make strip ARCH=$BINARY COMP=$COMP
WINE_PATH="$SDE" ../tests/signature.sh $benchref
mv ./stockfish$EXT ../stockfish-$NAME-$BINARY$EXT
- name: Remove non src files
run: git clean -fx
- name: Upload artifact for (pre)-release
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }}
path: .
+47
View File
@@ -0,0 +1,47 @@
name: IWYU
on:
workflow_call:
jobs:
Analyzers:
name: Check includes
runs-on: ubuntu-22.04
defaults:
run:
working-directory: Stockfish/src
shell: bash
steps:
- name: Checkout Stockfish
uses: actions/checkout@v4
with:
path: Stockfish
- name: Checkout include-what-you-use
uses: actions/checkout@v4
with:
repository: include-what-you-use/include-what-you-use
ref: f25caa280dc3277c4086ec345ad279a2463fea0f
path: include-what-you-use
- name: Download required linux packages
run: |
sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main'
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt update
sudo apt install -y libclang-17-dev clang-17 libc++-17-dev
- name: Set up include-what-you-use
run: |
mkdir build && cd build
cmake -G "Unix Makefiles" -DCMAKE_PREFIX_PATH="/usr/lib/llvm-17" ..
sudo make install
working-directory: include-what-you-use
- name: Check include-what-you-use
run: include-what-you-use --version
- name: Check includes
run: >
make analyze
COMP=clang
CXX=include-what-you-use
CXXFLAGS="-stdlib=libc++ -Xiwyu --comment_style=long -Xiwyu --mapping='${{ github.workspace }}/Stockfish/.github/ci/libcxx17.imp' -Xiwyu --error"
@@ -1,8 +1,8 @@
name: Stockfish name: Sanitizers
on: on:
workflow_call: workflow_call:
jobs: jobs:
Stockfish: Test-under-sanitizers:
name: ${{ matrix.sanitizers.name }} name: ${{ matrix.sanitizers.name }}
runs-on: ${{ matrix.config.os }} runs-on: ${{ matrix.config.os }}
env: env:
@@ -10,13 +10,14 @@ jobs:
COMP: ${{ matrix.config.comp }} COMP: ${{ matrix.config.comp }}
CXXFLAGS: "-Werror" CXXFLAGS: "-Werror"
strategy: strategy:
fail-fast: false
matrix: matrix:
config: config:
- name: Ubuntu 20.04 GCC - name: Ubuntu 22.04 GCC
os: ubuntu-20.04 os: ubuntu-22.04
compiler: g++ compiler: g++
comp: gcc comp: gcc
shell: bash {0} shell: bash
sanitizers: sanitizers:
- name: Run with thread sanitizer - name: Run with thread sanitizer
make_option: sanitize=thread make_option: sanitize=thread
@@ -35,9 +36,7 @@ jobs:
working-directory: src working-directory: src
shell: ${{ matrix.config.shell }} shell: ${{ matrix.config.shell }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download required linux packages - name: Download required linux packages
run: | run: |
@@ -62,5 +61,5 @@ jobs:
run: | run: |
export CXXFLAGS="-O1 -fno-inline" export CXXFLAGS="-O1 -fno-inline"
make clean make clean
make -j2 ARCH=x86-64-modern ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null make -j4 ARCH=x86-64-sse41-popcnt ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null
../tests/instrumented.sh --${{ matrix.sanitizers.instrumented_option }} ../tests/instrumented.sh --${{ matrix.sanitizers.instrumented_option }}
+71 -18
View File
@@ -2,7 +2,7 @@ name: Stockfish
on: on:
push: push:
tags: tags:
- '*' - "*"
branches: branches:
- master - master
- tools - tools
@@ -13,12 +13,10 @@ on:
- tools - tools
jobs: jobs:
Prerelease: Prerelease:
if: github.ref == 'refs/heads/master' if: github.repository == 'official-stockfish/Stockfish' && (github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag'))
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with:
fetch-depth: 0
# returns null if no pre-release exists # returns null if no pre-release exists
- name: Get Commit SHA of Latest Pre-release - name: Get Commit SHA of Latest Pre-release
@@ -27,25 +25,80 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install -y curl jq sudo apt-get install -y curl jq
echo "COMMIT_SHA=$(jq -r 'map(select(.prerelease)) | first | .tag_name' <<< $(curl -s https://api.github.com/repos/${{ github.repository_owner }}/Stockfish/releases))" >> $GITHUB_ENV echo "COMMIT_SHA_TAG=$(jq -r 'map(select(.prerelease)) | first | .tag_name' <<< $(curl -s https://api.github.com/repos/${{ github.repository_owner }}/Stockfish/releases))" >> $GITHUB_ENV
# delete old previous pre-release and tag # delete old previous pre-release and tag
- uses: dev-drprasad/delete-tag-and-release@v0.2.1 - run: gh release delete ${{ env.COMMIT_SHA_TAG }} --cleanup-tag
if: env.COMMIT_SHA != 'null' if: env.COMMIT_SHA_TAG != 'null'
with:
tag_name: ${{ env.COMMIT_SHA }}
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Make sure that an old ci that still runs on master doesn't recreate a prerelease
- name: Check Pullable Commits
id: check_commits
run: |
git fetch
CHANGES=$(git rev-list HEAD..origin/master --count)
echo "CHANGES=$CHANGES" >> $GITHUB_ENV
- name: Get last commit SHA
id: last_commit
run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV
- name: Get commit date
id: commit_date
run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV
# Create a new pre-release, the other upload_binaries.yml will upload the binaries
# to this pre-release.
- name: Create Prerelease
if: github.ref_name == 'master' && env.CHANGES == '0'
uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
with:
name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }}
tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }}
prerelease: true
Matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
arm_matrix: ${{ steps.set-arm-matrix.outputs.arm_matrix }}
steps:
- uses: actions/checkout@v4
- id: set-matrix
run: |
TASKS=$(echo $(cat .github/ci/matrix.json) )
echo "MATRIX=$TASKS" >> $GITHUB_OUTPUT
- id: set-arm-matrix
run: |
TASKS_ARM=$(echo $(cat .github/ci/arm_matrix.json) )
echo "ARM_MATRIX=$TASKS_ARM" >> $GITHUB_OUTPUT
Compilation:
needs: [Matrix]
uses: ./.github/workflows/compilation.yml
with:
matrix: ${{ needs.Matrix.outputs.matrix }}
ARMCompilation:
needs: [Matrix]
uses: ./.github/workflows/arm_compilation.yml
with:
matrix: ${{ needs.Matrix.outputs.arm_matrix }}
IWYU:
uses: ./.github/workflows/iwyu.yml
Sanitizers: Sanitizers:
uses: ./.github/workflows/stockfish_sanitizers.yml uses: ./.github/workflows/sanitizers.yml
Tests: Tests:
uses: ./.github/workflows/stockfish_test.yml uses: ./.github/workflows/tests.yml
Compiles:
uses: ./.github/workflows/stockfish_compile_test.yml
Binaries: Binaries:
if: github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag') if: github.repository == 'official-stockfish/Stockfish'
uses: ./.github/workflows/stockfish_binaries.yml needs: [Matrix, Prerelease, Compilation]
uses: ./.github/workflows/upload_binaries.yml
with:
matrix: ${{ needs.Matrix.outputs.matrix }}
ARM_Binaries: ARM_Binaries:
if: github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag') if: github.repository == 'official-stockfish/Stockfish'
uses: ./.github/workflows/stockfish_arm_binaries.yml needs: [Matrix, Prerelease, ARMCompilation]
uses: ./.github/workflows/upload_binaries.yml
with:
matrix: ${{ needs.Matrix.outputs.arm_matrix }}
@@ -1,158 +0,0 @@
name: Stockfish
on:
workflow_call:
jobs:
Stockfish:
name: ${{ matrix.config.name }} ${{ matrix.binaries }}
runs-on: ${{ matrix.config.os }}
env:
COMPILER: ${{ matrix.config.compiler }}
COMP: ${{ matrix.config.comp }}
EMU: ${{ matrix.config.emu }}
EXT: ${{ matrix.config.ext }}
OS: ${{ matrix.config.os }}
BINARY: ${{ matrix.binaries }}
strategy:
matrix:
config:
- name: Android NDK aarch64
os: ubuntu-22.04
compiler: aarch64-linux-android21-clang++
emu: qemu-aarch64
comp: ndk
shell: bash {0}
- name: Android NDK arm
os: ubuntu-22.04
compiler: armv7a-linux-androideabi21-clang++
emu: qemu-arm
comp: ndk
shell: bash {0}
binaries:
- armv8
- armv7
- armv7-neon
exclude:
- binaries: armv8
config: {compiler: armv7a-linux-androideabi21-clang++}
- binaries: armv7
config: {compiler: aarch64-linux-android21-clang++}
- binaries: armv7-neon
config: {compiler: aarch64-linux-android21-clang++}
defaults:
run:
working-directory: src
shell: ${{ matrix.config.shell }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Download required linux packages
if: runner.os == 'Linux'
run: |
sudo apt update
sudo apt install qemu-user
- name: Install NDK
if: runner.os == 'Linux'
run: |
if [ $COMP == ndk ]; then
NDKV="21.4.7075529"
ANDROID_ROOT=/usr/local/lib/android
ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk
SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager
echo "y" | $SDKMANAGER "ndk;$NDKV"
ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$NDKV
ANDROID_NDK_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin
echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV
fi
- name: Download the used network from the fishtest framework
run: make net
- name: Check compiler
run: |
if [ $COMP == ndk ]; then
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
fi
$COMPILER -v
- name: Test help target
run: make help
- name: Check git
run: git --version
# Compile profile guided builds
- name: Compile ${{ matrix.binaries }} build
run: |
if [ $COMP == ndk ]; then
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument"
fi
make clean
make -j2 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH=$EMU
make strip ARCH=$BINARY COMP=$COMP
mv ./stockfish$EXT ../stockfish-android-$BINARY$EXT
- name: Remove non src files
run: rm -f *.o .depend *.nnue
- name: Download wiki
run: |
git clone https://github.com/official-stockfish/Stockfish.wiki.git ../wiki
cd ../wiki
rm -rf .git
- name: Create tar archive.
run: |
cd ..
mkdir stockfish
cp -r wiki stockfish/
cp -r src stockfish/
cp stockfish-android-$BINARY$EXT stockfish/
cp "Top CPU Contributors.txt" stockfish/
cp Copying.txt stockfish/
cp AUTHORS stockfish/
cp CITATION.cff stockfish/
cp README.md stockfish/
tar -cvf stockfish-android-$BINARY.tar stockfish
- name: Upload binaries
uses: actions/upload-artifact@v3
with:
name: stockfish-android-${{ matrix.binaries }}
path: stockfish-android-${{ matrix.binaries }}.tar
- name: Release
if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag'
uses: softprops/action-gh-release@v1
with:
files: stockfish-android-${{ matrix.binaries }}.tar
- name: Get last commit sha
id: last_commit
run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV
- name: Get commit date
id: commit_date
run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV
# Make sure that an old ci which still runs on master doesn't recreate a prerelease
- name: Check Pullable Commits
id: check_commits
run: |
git fetch
CHANGES=$(git rev-list HEAD..origin/master --count)
echo "CHANGES=$CHANGES" >> $GITHUB_ENV
- name: Prerelease
if: github.ref_name == 'master' && env.CHANGES == '0'
continue-on-error: true
uses: softprops/action-gh-release@v1
with:
name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }}
tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }}
prerelease: true
files: stockfish-android-${{ matrix.binaries }}.tar
-168
View File
@@ -1,168 +0,0 @@
name: Stockfish
on:
workflow_call:
jobs:
Stockfish:
name: ${{ matrix.config.name }} ${{ matrix.binaries }}
runs-on: ${{ matrix.config.os }}
env:
COMPILER: ${{ matrix.config.compiler }}
COMP: ${{ matrix.config.comp }}
EXT: ${{ matrix.config.ext }}
NAME: ${{ matrix.config.simple_name }}
BINARY: ${{ matrix.binaries }}
strategy:
matrix:
config:
- name: Ubuntu 20.04 GCC
os: ubuntu-20.04
simple_name: ubuntu
compiler: g++
comp: gcc
shell: bash {0}
archive_ext: tar
- name: MacOS 12 Apple Clang
os: macos-12
simple_name: macos
compiler: clang++
comp: clang
shell: bash {0}
archive_ext: tar
- name: Windows 2022 Mingw-w64 GCC x86_64
os: windows-2022
simple_name: windows
compiler: g++
comp: mingw
msys_sys: mingw64
msys_env: x86_64-gcc
shell: msys2 {0}
ext: .exe
archive_ext: zip
binaries:
- x86-64
- x86-64-modern
- x86-64-avx2
exclude:
- binaries: x86-64-avx2
config: {os: macos-12}
defaults:
run:
working-directory: src
shell: ${{ matrix.config.shell }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Download required linux packages
if: runner.os == 'Linux'
run: sudo apt update
- name: Setup msys and install required packages
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.config.msys_sys }}
install: mingw-w64-${{ matrix.config.msys_env }} make git zip
- name: Download the used network from the fishtest framework
run: make net
- name: Check compiler
run: $COMPILER -v
- name: Test help target
run: make help
- name: Check git
run: git --version
# Compile profile guided builds
- name: Compile ${{ matrix.binaries }} build
run: |
make -j2 profile-build ARCH=$BINARY COMP=$COMP
make strip ARCH=$BINARY COMP=$COMP
mv ./stockfish$EXT ../stockfish-$NAME-$BINARY$EXT
- name: Remove non src files
run: git clean -fx
- name: Download wiki
run: |
git clone https://github.com/official-stockfish/Stockfish.wiki.git ../wiki
rm -rf ../wiki/.git
- name: Create directory.
run: |
cd ..
mkdir stockfish
cp -r wiki stockfish/
cp -r src stockfish/
cp stockfish-$NAME-$BINARY$EXT stockfish/
cp "Top CPU Contributors.txt" stockfish/
cp Copying.txt stockfish/
cp AUTHORS stockfish/
cp CITATION.cff stockfish/
cp README.md stockfish/
- name: Create tar
if: runner.os != 'Windows'
run: |
cd ..
tar -cvf stockfish-$NAME-$BINARY.tar stockfish
- name: Create zip
if: runner.os == 'Windows'
run: |
cd ..
zip -r stockfish-$NAME-$BINARY.zip stockfish
- name: Upload binaries
if: runner.os != 'Windows'
uses: actions/upload-artifact@v3
with:
name: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }}
path: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.tar
# Artifacts automatically get zipped
# to avoid double zipping, we use the unzipped directory
- name: Upload binaries
if: runner.os == 'Windows'
uses: actions/upload-artifact@v3
with:
name: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }}
path: stockfish
- name: Release
if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag'
uses: softprops/action-gh-release@v1
with:
files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }}
- name: Get last commit sha
id: last_commit
run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV
- name: Get commit date
id: commit_date
run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV
# Make sure that an old ci which still runs on master doesn't recreate a prerelease
- name: Check Pullable Commits
id: check_commits
run: |
git fetch
CHANGES=$(git rev-list HEAD..origin/master --count)
echo "CHANGES=$CHANGES" >> $GITHUB_ENV
- name: Prerelease
if: github.ref_name == 'master' && env.CHANGES == '0'
continue-on-error: true
uses: softprops/action-gh-release@v1
with:
name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }}
tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }}
prerelease: true
files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }}
@@ -1,102 +0,0 @@
name: Stockfish
on:
workflow_call:
jobs:
Stockfish:
name: ${{ matrix.config.name }}
runs-on: ${{ matrix.config.os }}
env:
COMPILER: ${{ matrix.config.compiler }}
COMP: ${{ matrix.config.comp }}
strategy:
matrix:
config:
- name: Ubuntu 20.04 GCC
os: ubuntu-20.04
compiler: g++
comp: gcc
shell: bash {0}
- name: Ubuntu 20.04 Clang
os: ubuntu-20.04
compiler: clang++
comp: clang
shell: bash {0}
- name: MacOS 12 Apple Clang
os: macos-12
compiler: clang++
comp: clang
shell: bash {0}
- name: MacOS 12 GCC 11
os: macos-12
compiler: g++-11
comp: gcc
shell: bash {0}
- name: Windows 2022 Mingw-w64 GCC x86_64
os: windows-2022
compiler: g++
comp: mingw
msys_sys: mingw64
msys_env: x86_64-gcc
shell: msys2 {0}
- name: Windows 2022 Mingw-w64 Clang x86_64
os: windows-2022
compiler: clang++
comp: clang
msys_sys: clang64
msys_env: clang-x86_64-clang
shell: msys2 {0}
defaults:
run:
working-directory: src
shell: ${{ matrix.config.shell }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup msys and install required packages
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: ${{matrix.config.msys_sys}}
install: mingw-w64-${{matrix.config.msys_env}} make git
- name: Download the used network from the fishtest framework
run: make net
- name: Check compiler
run: $COMPILER -v
- name: Test help target
run: make help
- name: Check git
run: git --version
# x86-64 with newer extensions tests
- name: Compile x86-64-avx2 build
run: |
make clean
make -j2 ARCH=x86-64-avx2 build
- name: Compile x86-64-bmi2 build
run: |
make clean
make -j2 ARCH=x86-64-bmi2 build
- name: Compile x86-64-avx512 build
run: |
make clean
make -j2 ARCH=x86-64-avx512 build
- name: Compile x86-64-vnni512 build
run: |
make clean
make -j2 ARCH=x86-64-vnni512 build
- name: Compile x86-64-vnni256 build
run: |
make clean
make -j2 ARCH=x86-64-vnni256 build
@@ -1,8 +1,8 @@
name: Stockfish name: Tests
on: on:
workflow_call: workflow_call:
jobs: jobs:
Stockfish: Test-Targets:
name: ${{ matrix.config.name }} name: ${{ matrix.config.name }}
runs-on: ${{ matrix.config.os }} runs-on: ${{ matrix.config.os }}
env: env:
@@ -10,6 +10,7 @@ jobs:
COMP: ${{ matrix.config.comp }} COMP: ${{ matrix.config.comp }}
CXXFLAGS: "-Werror" CXXFLAGS: "-Werror"
strategy: strategy:
fail-fast: false
matrix: matrix:
config: config:
- name: Ubuntu 20.04 GCC - name: Ubuntu 20.04 GCC
@@ -18,38 +19,61 @@ jobs:
comp: gcc comp: gcc
run_32bit_tests: true run_32bit_tests: true
run_64bit_tests: true run_64bit_tests: true
shell: bash {0} shell: bash
- name: Ubuntu 20.04 Clang - name: Ubuntu 20.04 Clang
os: ubuntu-20.04 os: ubuntu-20.04
compiler: clang++ compiler: clang++
comp: clang comp: clang
run_32bit_tests: true run_32bit_tests: true
run_64bit_tests: true run_64bit_tests: true
shell: bash {0} shell: bash
- name: Android NDK aarch64 - name: Android NDK aarch64
os: ubuntu-22.04 os: ubuntu-22.04
compiler: aarch64-linux-android21-clang++ compiler: aarch64-linux-android21-clang++
comp: ndk comp: ndk
run_armv8_tests: true run_armv8_tests: true
shell: bash {0} shell: bash
- name: Android NDK arm - name: Android NDK arm
os: ubuntu-22.04 os: ubuntu-22.04
compiler: armv7a-linux-androideabi21-clang++ compiler: armv7a-linux-androideabi21-clang++
comp: ndk comp: ndk
run_armv7_tests: true run_armv7_tests: true
shell: bash {0} shell: bash
- name: MacOS 12 Apple Clang - name: Linux GCC riscv64
os: macos-12 os: ubuntu-22.04
compiler: g++
comp: gcc
run_riscv64_tests: true
base_image: "riscv64/alpine:edge"
platform: linux/riscv64
shell: bash
- name: Linux GCC ppc64
os: ubuntu-22.04
compiler: g++
comp: gcc
run_ppc64_tests: true
base_image: "ppc64le/alpine:latest"
platform: linux/ppc64le
shell: bash
- name: MacOS 13 Apple Clang
os: macos-13
compiler: clang++ compiler: clang++
comp: clang comp: clang
run_64bit_tests: true run_64bit_tests: true
shell: bash {0} shell: bash
- name: MacOS 12 GCC 11 - name: MacOS 14 Apple Clang M1
os: macos-12 os: macos-14
compiler: clang++
comp: clang
run_64bit_tests: false
run_m1_tests: true
shell: bash
- name: MacOS 13 GCC 11
os: macos-13
compiler: g++-11 compiler: g++-11
comp: gcc comp: gcc
run_64bit_tests: true run_64bit_tests: true
shell: bash {0} shell: bash
- name: Windows 2022 Mingw-w64 GCC x86_64 - name: Windows 2022 Mingw-w64 GCC x86_64
os: windows-2022 os: windows-2022
compiler: g++ compiler: g++
@@ -79,7 +103,7 @@ jobs:
working-directory: src working-directory: src
shell: ${{ matrix.config.shell }} shell: ${{ matrix.config.shell }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
@@ -87,7 +111,7 @@ jobs:
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: | run: |
sudo apt update sudo apt update
sudo apt install expect valgrind g++-multilib qemu-user sudo apt install expect valgrind g++-multilib qemu-user-static
- name: Install NDK - name: Install NDK
if: runner.os == 'Linux' if: runner.os == 'Linux'
@@ -103,6 +127,28 @@ jobs:
echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV
fi fi
- name: Set up QEMU
if: matrix.config.base_image
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
if: matrix.config.base_image
uses: docker/setup-buildx-action@v3
- name: Build Docker container
if: matrix.config.base_image
run: |
docker buildx build --load -t sf_builder - << EOF
FROM ${{ matrix.config.base_image }}
WORKDIR /app
RUN apk update && apk add make g++
CMD ["sh", "script.sh"]
EOF
- name: Download required macOS packages
if: runner.os == 'macOS'
run: brew install coreutils
- name: Setup msys and install required packages - name: Setup msys and install required packages
if: runner.os == 'Windows' if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2 uses: msys2/setup-msys2@v2
@@ -115,15 +161,22 @@ jobs:
- name: Extract the bench number from the commit history - name: Extract the bench number from the commit history
run: | run: |
git log HEAD | grep -o "\b[Bb]ench[ :]\+[1-9][0-9]\{5,9\}\b" | head -n 1 | sed "s/[^0-9]//g" > git_sig for hash in $(git rev-list -100 HEAD); do
[ -s git_sig ] && echo "benchref=$(cat git_sig)" >> $GITHUB_ENV && echo "Reference bench:" $(cat git_sig) || echo "No bench found" benchref=$(git show -s $hash | tac | grep -m 1 -o -x '[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*' | sed 's/[^0-9]//g') && break || true
done
[[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $hash" && echo "Reference bench: $benchref" || echo "No bench found"
- name: Check compiler - name: Check compiler
run: | run: |
if [ -z "${{ matrix.config.base_image }}" ]; then
if [ $COMP == ndk ]; then if [ $COMP == ndk ]; then
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
fi fi
$COMPILER -v $COMPILER -v
else
echo "$COMPILER -v" > script.sh
docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}/src:/app sf_builder
fi
- name: Test help target - name: Test help target
run: make help run: make help
@@ -134,123 +187,179 @@ jobs:
# x86-32 tests # x86-32 tests
- name: Test debug x86-32 build - name: Test debug x86-32 build
if: ${{ matrix.config.run_32bit_tests }} if: matrix.config.run_32bit_tests
run: | run: |
export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG"
make clean make clean
make -j2 ARCH=x86-32 optimize=no debug=yes build make -j4 ARCH=x86-32 optimize=no debug=yes build
../tests/signature.sh $benchref ../tests/signature.sh $benchref
- name: Test x86-32 build - name: Test x86-32 build
if: ${{ matrix.config.run_32bit_tests }} if: matrix.config.run_32bit_tests
run: | run: |
make clean make clean
make -j2 ARCH=x86-32 build make -j4 ARCH=x86-32 build
../tests/signature.sh $benchref ../tests/signature.sh $benchref
- name: Test x86-32-sse41-popcnt build - name: Test x86-32-sse41-popcnt build
if: ${{ matrix.config.run_32bit_tests }} if: matrix.config.run_32bit_tests
run: | run: |
make clean make clean
make -j2 ARCH=x86-32-sse41-popcnt build make -j4 ARCH=x86-32-sse41-popcnt build
../tests/signature.sh $benchref ../tests/signature.sh $benchref
- name: Test x86-32-sse2 build - name: Test x86-32-sse2 build
if: ${{ matrix.config.run_32bit_tests }} if: matrix.config.run_32bit_tests
run: | run: |
make clean make clean
make -j2 ARCH=x86-32-sse2 build make -j4 ARCH=x86-32-sse2 build
../tests/signature.sh $benchref ../tests/signature.sh $benchref
- name: Test general-32 build - name: Test general-32 build
if: ${{ matrix.config.run_32bit_tests }} if: matrix.config.run_32bit_tests
run: | run: |
make clean make clean
make -j2 ARCH=general-32 build make -j4 ARCH=general-32 build
../tests/signature.sh $benchref ../tests/signature.sh $benchref
# x86-64 tests # x86-64 tests
- name: Test debug x86-64-modern build - name: Test debug x86-64-avx2 build
if: ${{ matrix.config.run_64bit_tests }} if: matrix.config.run_64bit_tests
run: | run: |
export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG"
make clean make clean
make -j2 ARCH=x86-64-modern optimize=no debug=yes build make -j4 ARCH=x86-64-avx2 optimize=no debug=yes build
../tests/signature.sh $benchref ../tests/signature.sh $benchref
- name: Test x86-64-modern build - name: Test x86-64-bmi2 build
if: ${{ matrix.config.run_64bit_tests }} if: matrix.config.run_64bit_tests
run: | run: |
make clean make clean
make -j2 ARCH=x86-64-modern build make -j4 ARCH=x86-64-bmi2 build
../tests/signature.sh $benchref
- name: Test x86-64-avx2 build
if: matrix.config.run_64bit_tests
run: |
make clean
make -j4 ARCH=x86-64-avx2 build
../tests/signature.sh $benchref
# Test a deprecated arch
- name: Test x86-64-modern build
if: matrix.config.run_64bit_tests
run: |
make clean
make -j4 ARCH=x86-64-modern build
../tests/signature.sh $benchref
- name: Test x86-64-sse41-popcnt build
if: matrix.config.run_64bit_tests
run: |
make clean
make -j4 ARCH=x86-64-sse41-popcnt build
../tests/signature.sh $benchref ../tests/signature.sh $benchref
- name: Test x86-64-ssse3 build - name: Test x86-64-ssse3 build
if: ${{ matrix.config.run_64bit_tests }} if: matrix.config.run_64bit_tests
run: | run: |
make clean make clean
make -j2 ARCH=x86-64-ssse3 build make -j4 ARCH=x86-64-ssse3 build
../tests/signature.sh $benchref ../tests/signature.sh $benchref
- name: Test x86-64-sse3-popcnt build - name: Test x86-64-sse3-popcnt build
if: ${{ matrix.config.run_64bit_tests }} if: matrix.config.run_64bit_tests
run: | run: |
make clean make clean
make -j2 ARCH=x86-64-sse3-popcnt build make -j4 ARCH=x86-64-sse3-popcnt build
../tests/signature.sh $benchref ../tests/signature.sh $benchref
- name: Test x86-64 build - name: Test x86-64 build
if: ${{ matrix.config.run_64bit_tests }} if: matrix.config.run_64bit_tests
run: | run: |
make clean make clean
make -j2 ARCH=x86-64 build make -j4 ARCH=x86-64 build
../tests/signature.sh $benchref ../tests/signature.sh $benchref
- name: Test general-64 build - name: Test general-64 build
if: matrix.config.run_64bit_tests if: matrix.config.run_64bit_tests
run: | run: |
make clean make clean
make -j2 ARCH=general-64 build make -j4 ARCH=general-64 build
../tests/signature.sh $benchref
- name: Test apple-silicon build
if: matrix.config.run_m1_tests
run: |
make clean
make -j4 ARCH=apple-silicon build
../tests/signature.sh $benchref ../tests/signature.sh $benchref
# armv8 tests # armv8 tests
- name: Test armv8 build - name: Test armv8 build
if: ${{ matrix.config.run_armv8_tests }} if: matrix.config.run_armv8_tests
run: | run: |
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument" export LDFLAGS="-static -Wno-unused-command-line-argument"
make clean make clean
make -j2 ARCH=armv8 build make -j4 ARCH=armv8 build
../tests/signature.sh $benchref
- name: Test armv8-dotprod build
if: matrix.config.run_armv8_tests
run: |
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument"
make clean
make -j4 ARCH=armv8-dotprod build
../tests/signature.sh $benchref ../tests/signature.sh $benchref
# armv7 tests # armv7 tests
- name: Test armv7 build - name: Test armv7 build
if: ${{ matrix.config.run_armv7_tests }} if: matrix.config.run_armv7_tests
run: | run: |
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument" export LDFLAGS="-static -Wno-unused-command-line-argument"
make clean make clean
make -j2 ARCH=armv7 build make -j4 ARCH=armv7 build
../tests/signature.sh $benchref ../tests/signature.sh $benchref
- name: Test armv7-neon build - name: Test armv7-neon build
if: ${{ matrix.config.run_armv7_tests }} if: matrix.config.run_armv7_tests
run: | run: |
export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH
export LDFLAGS="-static -Wno-unused-command-line-argument" export LDFLAGS="-static -Wno-unused-command-line-argument"
make clean make clean
make -j2 ARCH=armv7-neon build make -j4 ARCH=armv7-neon build
../tests/signature.sh $benchref
# riscv64 tests
- name: Test riscv64 build
if: matrix.config.run_riscv64_tests
run: |
echo "export LDFLAGS='-static' && make clean && make -j4 ARCH=riscv64 build" > script.sh
docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}/src:/app sf_builder
../tests/signature.sh $benchref
# ppc64 tests
- name: Test ppc64 build
if: matrix.config.run_ppc64_tests
run: |
echo "export LDFLAGS='-static' && make clean && make -j4 ARCH=ppc-64 build" > script.sh
docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}/src:/app sf_builder
../tests/signature.sh $benchref ../tests/signature.sh $benchref
# Other tests # Other tests
- name: Check perft and search reproducibility - name: Check perft and search reproducibility
if: ${{ matrix.config.run_64bit_tests }} if: matrix.config.run_64bit_tests
run: | run: |
make clean make clean
make -j2 ARCH=x86-64-modern build make -j4 ARCH=x86-64-avx2 build
../tests/perft.sh ../tests/perft.sh
../tests/reprosearch.sh ../tests/reprosearch.sh
+106
View File
@@ -0,0 +1,106 @@
name: Upload Binaries
on:
workflow_call:
inputs:
matrix:
type: string
required: true
jobs:
Artifacts:
name: ${{ matrix.config.name }} ${{ matrix.binaries }}
runs-on: ${{ matrix.config.os }}
env:
COMPILER: ${{ matrix.config.compiler }}
COMP: ${{ matrix.config.comp }}
EXT: ${{ matrix.config.ext }}
NAME: ${{ matrix.config.simple_name }}
BINARY: ${{ matrix.binaries }}
SDE: ${{ matrix.config.sde }}
strategy:
fail-fast: false
matrix: ${{ fromJson(inputs.matrix) }}
defaults:
run:
shell: ${{ matrix.config.shell }}
steps:
- uses: actions/checkout@v4
- name: Download artifact from compilation
uses: actions/download-artifact@v4
with:
name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }}
path: ${{ matrix.config.simple_name }} ${{ matrix.binaries }}
- name: Setup msys and install required packages
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.config.msys_sys }}
install: mingw-w64-${{ matrix.config.msys_env }} make git zip
- name: Create Package
run: |
mkdir stockfish
- name: Download wiki
run: |
git clone https://github.com/official-stockfish/Stockfish.wiki.git wiki
rm -rf wiki/.git
mv wiki stockfish/
- name: Copy files
run: |
mv "${{ matrix.config.simple_name }} ${{ matrix.binaries }}" stockfish-workflow
cd stockfish-workflow
cp -r src ../stockfish/
cp stockfish-$NAME-$BINARY$EXT ../stockfish/
cp "Top CPU Contributors.txt" ../stockfish/
cp Copying.txt ../stockfish/
cp AUTHORS ../stockfish/
cp CITATION.cff ../stockfish/
cp README.md ../stockfish/
cp CONTRIBUTING.md ../stockfish/
- name: Create tar
if: runner.os != 'Windows'
run: |
chmod +x ./stockfish/stockfish-$NAME-$BINARY$EXT
tar -cvf stockfish-$NAME-$BINARY.tar stockfish
- name: Create zip
if: runner.os == 'Windows'
run: |
zip -r stockfish-$NAME-$BINARY.zip stockfish
- name: Release
if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag'
uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
with:
files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }}
- name: Get last commit sha
id: last_commit
run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV
- name: Get commit date
id: commit_date
run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV
# Make sure that an old ci that still runs on master doesn't recreate a prerelease
- name: Check Pullable Commits
id: check_commits
run: |
git fetch
CHANGES=$(git rev-list HEAD..origin/master --count)
echo "CHANGES=$CHANGES" >> $GITHUB_ENV
- name: Prerelease
if: github.ref_name == 'master' && env.CHANGES == '0'
continue-on-error: true
uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
with:
name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }}
tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }}
prerelease: true
files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }}
+18 -2
View File
@@ -12,6 +12,7 @@ Hisayori Noda (nodchip)
# All other authors of Stockfish code (in alphabetical order) # All other authors of Stockfish code (in alphabetical order)
Aditya (absimaldata) Aditya (absimaldata)
Adrian Petrescu (apetresc) Adrian Petrescu (apetresc)
Ahmed Kerimov (wcdbmv)
Ajith Chandy Jose (ajithcj) Ajith Chandy Jose (ajithcj)
Alain Savard (Rocky640) Alain Savard (Rocky640)
Alayan Feh (Alayan-stk-2) Alayan Feh (Alayan-stk-2)
@@ -29,6 +30,7 @@ Aram Tumanian (atumanian)
Arjun Temurnikar Arjun Temurnikar
Artem Solopiy (EntityFX) Artem Solopiy (EntityFX)
Auguste Pop Auguste Pop
Balazs Szilagyi
Balint Pfliegel Balint Pfliegel
Ben Chaney (Chaneybenjamini) Ben Chaney (Chaneybenjamini)
Ben Koshy (BKSpurgeon) Ben Koshy (BKSpurgeon)
@@ -45,9 +47,12 @@ candirufish
Chess13234 Chess13234
Chris Cain (ceebo) Chris Cain (ceebo)
clefrks clefrks
Clemens L. (rn5f107s2)
Cody Ho (aesrentai)
Dale Weiler (graphitemaster) Dale Weiler (graphitemaster)
Daniel Axtens (daxtens) Daniel Axtens (daxtens)
Daniel Dugovic (ddugovic) Daniel Dugovic (ddugovic)
Daniel Monroe (Ergodice)
Dan Schmidt (dfannius) Dan Schmidt (dfannius)
Dariusz Orzechowski (dorzechowski) Dariusz Orzechowski (dorzechowski)
David (dav1312) David (dav1312)
@@ -69,9 +74,11 @@ Fabian Beuke (madnight)
Fabian Fichter (ianfab) Fabian Fichter (ianfab)
Fanael Linithien (Fanael) Fanael Linithien (Fanael)
fanon fanon
Fauzi Akram Dabat (FauziAkram) Fauzi Akram Dabat (fauzi2)
Felix Wittmann Felix Wittmann
gamander gamander
Gabriele Lombardo (gabe)
Gahtan Nahdi
Gary Heckman (gheckman) Gary Heckman (gheckman)
George Sobala (gsobala) George Sobala (gsobala)
gguliash gguliash
@@ -94,6 +101,7 @@ Jake Senne (w1wwwwww)
Jan Ondruš (hxim) Jan Ondruš (hxim)
Jared Kish (Kurtbusch, kurt22i) Jared Kish (Kurtbusch, kurt22i)
Jarrod Torriero (DU-jdto) Jarrod Torriero (DU-jdto)
Jasper Shovelton (Beanie496)
Jean-Francois Romang (jromang) Jean-Francois Romang (jromang)
Jean Gauthier (OuaisBla) Jean Gauthier (OuaisBla)
Jekaa Jekaa
@@ -178,6 +186,7 @@ Raminder Singh
renouve renouve
Reuven Peleg (R-Peleg) Reuven Peleg (R-Peleg)
Richard Lloyd (Richard-Lloyd) Richard Lloyd (Richard-Lloyd)
Robert Nürnberg (robertnurnberg)
Rodrigo Exterckötter Tjäder Rodrigo Exterckötter Tjäder
Rodrigo Roim (roim) Rodrigo Roim (roim)
Ronald de Man (syzygy1, syzygy) Ronald de Man (syzygy1, syzygy)
@@ -195,16 +204,22 @@ sf-x
Shahin M. Shahin (peregrine) Shahin M. Shahin (peregrine)
Shane Booth (shane31) Shane Booth (shane31)
Shawn Varghese (xXH4CKST3RXx) Shawn Varghese (xXH4CKST3RXx)
Shawn Xu (xu-shawn)
Siad Daboul (Topologist) Siad Daboul (Topologist)
Stefan Geschwentner (locutus2) Stefan Geschwentner (locutus2)
Stefano Cardanobile (Stefano80) Stefano Cardanobile (Stefano80)
Stefano Di Martino (StefanoD) Stefano Di Martino (StefanoD)
Steinar Gunderson (sesse) Steinar Gunderson (sesse)
Stéphane Nicolet (snicolet) Stéphane Nicolet (snicolet)
Stephen Touset (stouset)
Syine Mineta (MinetaS) Syine Mineta (MinetaS)
Taras Vuk (TarasVuk)
Thanar2 Thanar2
thaspel thaspel
theo77186 theo77186
TierynnB
Ting-Hsuan Huang (fffelix-huang)
Tobias Steinmann
Tomasz Sobczyk (Sopel97) Tomasz Sobczyk (Sopel97)
Tom Truscott Tom Truscott
Tom Vijlbrief (tomtor) Tom Vijlbrief (tomtor)
@@ -218,9 +233,10 @@ Vince Negri (cuddlestmonkey)
Viren Viren
windfishballad windfishballad
xefoci7612 xefoci7612
Xiang Wang (KatyushaScarlet)
zz4032 zz4032
# Additionally, we acknowledge the authors and maintainers of fishtest, # Additionally, we acknowledge the authors and maintainers of fishtest,
# an amazing and essential framework for Stockfish development! # an amazing and essential framework for Stockfish development!
# #
# https://github.com/glinscott/fishtest/blob/master/AUTHORS # https://github.com/official-stockfish/fishtest/blob/master/AUTHORS
+97
View File
@@ -0,0 +1,97 @@
# Contributing to Stockfish
Welcome to the Stockfish project! We are excited that you are interested in
contributing. This document outlines the guidelines and steps to follow when
making contributions to Stockfish.
## Table of Contents
- [Building Stockfish](#building-stockfish)
- [Making Contributions](#making-contributions)
- [Reporting Issues](#reporting-issues)
- [Submitting Pull Requests](#submitting-pull-requests)
- [Code Style](#code-style)
- [Community and Communication](#community-and-communication)
- [License](#license)
## Building Stockfish
In case you do not have a C++ compiler installed, you can follow the
instructions from our wiki.
- [Ubuntu][ubuntu-compiling-link]
- [Windows][windows-compiling-link]
- [macOS][macos-compiling-link]
## Making Contributions
### Reporting Issues
If you find a bug, please open an issue on the
[issue tracker][issue-tracker-link]. Be sure to include relevant information
like your operating system, build environment, and a detailed description of the
problem.
_Please note that Stockfish's development is not focused on adding new features.
Thus any issue regarding missing features will potentially be closed without
further discussion._
### Submitting Pull Requests
- Functional changes need to be tested on fishtest. See
[Creating my First Test][creating-my-first-test] for more details.
The accompanying pull request should include a link to the test results and
the new bench.
- Non-functional changes (e.g. refactoring, code style, documentation) do not
need to be tested on fishtest, unless they might impact performance.
- Provide a clear and concise description of the changes in the pull request
description.
_First time contributors should add their name to [AUTHORS](../AUTHORS)._
_Stockfish's development is not focused on adding new features. Thus any pull
request introducing new features will potentially be closed without further
discussion._
## Code Style
Changes to Stockfish C++ code should respect our coding style defined by
[.clang-format](.clang-format). You can format your changes by running
`make format`. This requires clang-format version 17 to be installed on your system.
## Navigate
For experienced Git users who frequently use git blame, it is recommended to
configure the blame.ignoreRevsFile setting.
This setting is useful for excluding noisy formatting commits.
```bash
git config blame.ignoreRevsFile .git-blame-ignore-revs
```
## Community and Communication
- Join the [Stockfish discord][discord-link] to discuss ideas, issues, and
development.
- Participate in the [Stockfish GitHub discussions][discussions-link] for
broader conversations.
## License
By contributing to Stockfish, you agree that your contributions will be licensed
under the GNU General Public License v3.0. See [Copying.txt][copying-link] for
more details.
Thank you for contributing to Stockfish and helping us make it even better!
[copying-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt
[discord-link]: https://discord.gg/GWDRS3kU6R
[discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new
[creating-my-first-test]: https://github.com/official-stockfish/fishtest/wiki/Creating-my-first-test#create-your-test
[issue-tracker-link]: https://github.com/official-stockfish/Stockfish/issues
[ubuntu-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Developers#user-content-installing-a-compiler-1
[windows-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Developers#user-content-installing-a-compiler
[macos-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Developers#user-content-installing-a-compiler-2
+4 -4
View File
@@ -1,7 +1,7 @@
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007 Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. of this license document, but changing it is not allowed.
@@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found.
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper mail.
@@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school, You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary. if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>. <https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>. <https://www.gnu.org/licenses/why-not-lgpl.html>.
+48 -31
View File
@@ -59,39 +59,37 @@ This distribution of Stockfish consists of the following files:
* a file with the .nnue extension, storing the neural network for the NNUE * a file with the .nnue extension, storing the neural network for the NNUE
evaluation. Binary distributions will have this file embedded. evaluation. Binary distributions will have this file embedded.
## The UCI protocol ## Stockfish on distributed memory systems
The [Universal Chess Interface][uci-link] (UCI) is a standard text-based protocol
used to communicate with a chess engine and is the recommended way to do so for
typical graphical user interfaces (GUI) or chess tools. Stockfish implements the
majority of its options.
Developers can see the default values for the UCI options available in Stockfish
by typing `./stockfish uci` in a terminal, but most users should typically use a
chess GUI to interact with Stockfish.
For more information on UCI or debug commands, see our [documentation][wiki-commands-link].
## Compiling Stockfish
Stockfish has support for 32 or 64-bit CPUs, certain hardware instructions,
big-endian machines such as Power PC, and other platforms.
On Unix-like systems, it should be easy to compile Stockfish directly from the
source code with the included Makefile in the folder `src`. In general, it is
recommended to run `make help` to see a list of make targets with corresponding
descriptions.
The cluster branch allows for running Stockfish on a cluster of servers (nodes)
that are connected with a high-speed and low-latency network, using the message
passing interface (MPI). In this case, one MPI process should be run per node,
and UCI options can be used to set the number of threads/hash per node as usual.
Typically, the engine will be invoked as
``` ```
cd src mpirun -np N /path/to/stockfish
make -j build ARCH=x86-64-modern
``` ```
where ```N``` stands for the number of MPI processes used (alternatives to ```mpirun```,
Detailed compilation instructions for all platforms can be found in our include ```mpiexec```, ```srun```). Use 1 mpi rank per node, and employ threading
[documentation][wiki-compile-link]. according to the cores per node. To build the cluster
branch, it is sufficient to specify ```COMPCXX=mpicxx``` (or e.g. CC depending on the name
of the compiler providing MPI support) on the make command line, and do a clean build:
```
make -j ARCH=x86-64-modern clean build COMPCXX=mpicxx mpi=yes
```
Make sure that the MPI installation is configured to support ```MPI_THREAD_MULTIPLE```,
this might require adding system specific compiler options to the Makefile. Stockfish employs
non-blocking (asynchronous) communication, and benefits from an MPI
implementation that efficiently supports this. Some MPI implentations might benefit
from leaving 1 core/thread free for these asynchronous communications, and might require
setting additional environment variables. ```mpirun``` should forward stdin/stdout
to ```rank 0``` only (e.g. ```srun --input=0 --output=0```).
Refer to your MPI documentation for more info.
## Contributing ## Contributing
__See [Contributing Guide](CONTRIBUTING.md).__
### Donating hardware ### Donating hardware
Improving Stockfish requires a massive amount of testing. You can donate your Improving Stockfish requires a massive amount of testing. You can donate your
@@ -114,6 +112,25 @@ Discussions about Stockfish take place these days mainly in the Stockfish
[Discord server][discord-link]. This is also the best place to ask questions [Discord server][discord-link]. This is also the best place to ask questions
about the codebase and how to improve it. about the codebase and how to improve it.
## Compiling Stockfish
Stockfish has support for 32 or 64-bit CPUs, certain hardware instructions,
big-endian machines such as Power PC, and other platforms.
On Unix-like systems, it should be easy to compile Stockfish directly from the
source code with the included Makefile in the folder `src`. In general, it is
recommended to run `make help` to see a list of make targets with corresponding
descriptions. An example suitable for most Intel and AMD chips:
```
cd src
make -j profile-build ARCH=x86-64-avx2
```
Detailed compilation instructions for all platforms can be found in our
[documentation][wiki-compile-link]. Our wiki also has information about
the [UCI commands][wiki-uci-link] supported by Stockfish.
## Terms of use ## Terms of use
Stockfish is free and distributed under the Stockfish is free and distributed under the
@@ -138,7 +155,7 @@ also be made available under GPL v3.
[issue-link]: https://github.com/official-stockfish/Stockfish/issues/new?assignees=&labels=&template=BUG-REPORT.yml [issue-link]: https://github.com/official-stockfish/Stockfish/issues/new?assignees=&labels=&template=BUG-REPORT.yml
[discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new [discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new
[fishtest-link]: https://tests.stockfishchess.org/tests [fishtest-link]: https://tests.stockfishchess.org/tests
[guideline-link]: https://github.com/glinscott/fishtest/wiki/Creating-my-first-test [guideline-link]: https://github.com/official-stockfish/fishtest/wiki/Creating-my-first-test
[license-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt [license-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt
[programming-link]: https://www.chessprogramming.org/Main_Page [programming-link]: https://www.chessprogramming.org/Main_Page
[programmingsf-link]: https://www.chessprogramming.org/Stockfish [programmingsf-link]: https://www.chessprogramming.org/Stockfish
@@ -150,10 +167,10 @@ also be made available under GPL v3.
[website-link]: https://stockfishchess.org [website-link]: https://stockfishchess.org
[website-blog-link]: https://stockfishchess.org/blog/ [website-blog-link]: https://stockfishchess.org/blog/
[wiki-link]: https://github.com/official-stockfish/Stockfish/wiki [wiki-link]: https://github.com/official-stockfish/Stockfish/wiki
[wiki-usage-link]: https://github.com/official-stockfish/Stockfish/wiki/Download-and-usage
[wiki-compile-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source [wiki-compile-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source
[wiki-commands-link]: https://github.com/official-stockfish/Stockfish/wiki/Commands [wiki-uci-link]: https://github.com/official-stockfish/Stockfish/wiki/UCI-&-Commands
[worker-link]: https://github.com/glinscott/fishtest/wiki/Running-the-worker [wiki-usage-link]: https://github.com/official-stockfish/Stockfish/wiki/Download-and-usage
[worker-link]: https://github.com/official-stockfish/fishtest/wiki/Running-the-worker
[build-badge]: https://img.shields.io/github/actions/workflow/status/official-stockfish/Stockfish/stockfish.yml?branch=master&style=for-the-badge&label=stockfish&logo=github [build-badge]: https://img.shields.io/github/actions/workflow/status/official-stockfish/Stockfish/stockfish.yml?branch=master&style=for-the-badge&label=stockfish&logo=github
[commits-badge]: https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge [commits-badge]: https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge
+104 -85
View File
@@ -1,139 +1,146 @@
Contributors to Fishtest with >10,000 CPU hours, as of 2023-06-20. Contributors to Fishtest with >10,000 CPU hours, as of 2024-02-24.
Thank you! Thank you!
Username CPU Hours Games played Username CPU Hours Games played
------------------------------------------------------------------ ------------------------------------------------------------------
noobpwnftw 37457426 2850540907 noobpwnftw 39302472 3055513453
technologov 14135647 742892808 technologov 20845762 994893444
linrock 4423514 303254809 linrock 8616428 560281417
mlang 3026000 200065824 mlang 3026000 200065824
okrout 2332151 222639518
pemo 1800019 60274069
dew 1689162 100033738 dew 1689162 100033738
okrout 1578136 148855886 TueRens 1474943 75121774
pemo 1508508 48814305 grandphish2 1463002 91616949
grandphish2 1461406 91540343 JojoM 1109702 72927902
TueRens 1194790 70400852 olafm 978631 71037944
JojoM 947612 61773190 sebastronomy 939955 44920556
tvijlbrief 796125 51897690 tvijlbrief 796125 51897690
sebastronomy 742434 38218524 gvreuls 711320 49142318
mibere 703840 46867607 mibere 703840 46867607
gvreuls 651026 42988582 oz 646268 46293638
oz 543438 39314736 rpngn 572571 38928563
cw 517858 34869755 leszek 531858 39316505
cw 518116 34894291
fastgm 503862 30260818 fastgm 503862 30260818
leszek 467278 33514883 CSU_Dynasty 468784 31385034
CSU_Dynasty 464940 31177118 ctoks 434591 28520597
ctoks 434416 28506889 maximmasiutin 429983 27066286
crunchy 427035 27344275 crunchy 427414 27371625
maximmasiutin 424795 26577722 bcross 415724 29061187
bcross 415722 29060963 velislav 342588 22140902
olafm 395922 32268020 mgrabiak 338763 23999170
rpngn 348378 24560289
velislav 342567 22138992
Fisherman 327231 21829379 Fisherman 327231 21829379
mgrabiak 300612 20608380 robal 299836 20213182
Dantist 296386 18031762 Dantist 296386 18031762
nordlandia 246201 16189678 ncfish1 267604 17881149
robal 241300 15656382 nordlandia 249322 16420192
marrco 234581 17714473 marrco 234581 17714473
ncfish1 227517 15233777 tolkki963 233490 19773930
glinscott 208125 13277240 glinscott 208125 13277240
drabel 204167 13930674 drabel 204167 13930674
mhoram 202894 12601997 mhoram 202894 12601997
bking_US 198894 11876016 bking_US 198894 11876016
Calis007 188631 12795784
Thanar 179852 12365359 Thanar 179852 12365359
Fifis 176209 10638245
vdv 175544 9904472 vdv 175544 9904472
spams 157128 10319326 spams 157128 10319326
DesolatedDodo 156659 10210328
armo9494 155355 10566898
sqrt2 147963 9724586 sqrt2 147963 9724586
DesolatedDodo 146350 9536172 jcAEie 140086 10603658
Calis007 143165 9478764 vdbergh 139746 9172061
vdbergh 138650 9064413
CoffeeOne 137100 5024116 CoffeeOne 137100 5024116
armo9494 136191 9460264
malala 136182 8002293 malala 136182 8002293
xoto 133759 9159372 xoto 133759 9159372
davar 129023 8376525 davar 129023 8376525
DMBK 122960 8980062 DMBK 122960 8980062
dsmith 122059 7570238 dsmith 122059 7570238
javran 121564 10144656
amicic 119661 7938029 amicic 119661 7938029
sschnee 118107 7389266
Wolfgang 114616 8070494
Data 113305 8220352 Data 113305 8220352
BrunoBanani 112960 7436849 BrunoBanani 112960 7436849
Wencey 111502 5991676
cuistot 108503 7006992
CypressChess 108331 7759788 CypressChess 108331 7759788
skiminki 107583 7218170 skiminki 107583 7218170
jcAEie 105675 8238962
MaZePallas 102823 6633619 MaZePallas 102823 6633619
sterni1971 100532 5880772 sterni1971 100532 5880772
sunu 100167 7040199 sunu 100167 7040199
zeryl 99331 6221261 zeryl 99331 6221261
thirdlife 99124 2242380 thirdlife 99156 2245320
ElbertoOne 99028 7023771 ElbertoOne 99028 7023771
cuistot 98853 6069816 Dubslow 98600 6903242
markkulix 97010 7643900
bigpen0r 94809 6529203 bigpen0r 94809 6529203
brabos 92118 6186135 brabos 92118 6186135
Wolfgang 91939 6105872 Maxim 90818 3283364
psk 89957 5984901 psk 89957 5984901
sschnee 88235 5268000 megaman7de 88822 6052132
racerschmacer 85805 6122790 racerschmacer 85805 6122790
Fifis 85722 5709729 maposora 85710 7778146
Dubslow 84986 6042456
Vizvezdenec 83761 5344740 Vizvezdenec 83761 5344740
0x3C33 82614 5271253 0x3C33 82614 5271253
BRAVONE 81239 5054681 BRAVONE 81239 5054681
nssy 76497 5259388 nssy 76497 5259388
jromang 76106 5236025 jromang 76106 5236025
teddybaer 75125 5407666 teddybaer 75125 5407666
tolkki963 74762 5149662
megaman7de 74351 4940352
Wencey 74181 4711488
Pking_cda 73776 5293873 Pking_cda 73776 5293873
yurikvelo 73150 5004382 yurikvelo 73516 5036928
markkulix 72607 5304642 MarcusTullius 71053 4803477
Bobo1239 70579 4794999 Bobo1239 70579 4794999
solarlight 70517 5028306 solarlight 70517 5028306
dv8silencer 70287 3883992 dv8silencer 70287 3883992
Spprtr 69646 4806763
Mineta 66325 4537742
manap 66273 4121774 manap 66273 4121774
szupaw 65468 5669742
tinker 64333 4268790 tinker 64333 4268790
qurashee 61208 3429862 qurashee 61208 3429862
Mineta 59357 4418202 woutboat 59496 4906352
Spprtr 58723 3911011 AGI 58195 4329580
AGI 58147 4325994
robnjr 57262 4053117 robnjr 57262 4053117
Freja 56938 3733019 Freja 56938 3733019
MaxKlaxxMiner 56879 3423958 MaxKlaxxMiner 56879 3423958
MarcusTullius 56746 3762951
ttruscott 56010 3680085 ttruscott 56010 3680085
rkl 55132 4164467 rkl 55132 4164467
jmdana 54697 4012593
renouve 53811 3501516 renouve 53811 3501516
javran 53785 4627608 notchris 52433 4044590
finfish 51360 3370515 finfish 51360 3370515
eva42 51272 3599691 eva42 51272 3599691
eastorwest 51117 3454811 eastorwest 51117 3454811
Goatminola 51004 4432492
rap 49985 3219146 rap 49985 3219146
pb00067 49733 3298934 pb00067 49733 3298934
GPUex 48686 3684998
OuaisBla 48626 3445134 OuaisBla 48626 3445134
ronaldjerum 47654 3240695 ronaldjerum 47654 3240695
biffhero 46564 3111352 biffhero 46564 3111352
oryx 45533 3539290
VoyagerOne 45476 3452465 VoyagerOne 45476 3452465
jmdana 44893 3065205
maposora 44597 4039578
oryx 44570 3454238
speedycpu 43842 3003273 speedycpu 43842 3003273
jbwiebe 43305 2805433 jbwiebe 43305 2805433
GPUex 42378 3133332
Antihistamine 41788 2761312 Antihistamine 41788 2761312
mhunt 41735 2691355 mhunt 41735 2691355
homyur 39893 2850481 homyur 39893 2850481
gri 39871 2515779 gri 39871 2515779
Garf 37741 2999686 Garf 37741 2999686
SC 37299 2731694 SC 37299 2731694
Sylvain27 36520 1467082
csnodgrass 36207 2688994 csnodgrass 36207 2688994
Gaster319 35655 3149442
strelock 34716 2074055 strelock 34716 2074055
szupaw 34102 2880346
EthanOConnor 33370 2090311 EthanOConnor 33370 2090311
slakovv 32915 2021889 slakovv 32915 2021889
gopeto 31884 2076712
Gelma 31771 1551204 Gelma 31771 1551204
gopeto 31671 2060990
kdave 31157 2198362 kdave 31157 2198362
manapbk 30987 1810399 manapbk 30987 1810399
ZacHFX 30551 2238078
Prcuvu 30377 2170122 Prcuvu 30377 2170122
anst 30301 2190091 anst 30301 2190091
jkiiski 30136 1904470 jkiiski 30136 1904470
@@ -142,27 +149,31 @@ hyperbolic.tom 29840 2017394
chuckstablers 29659 2093438 chuckstablers 29659 2093438
Pyafue 29650 1902349 Pyafue 29650 1902349
belzedar94 28846 1811530 belzedar94 28846 1811530
votoanthuan 27978 2285818
shawnxu 27438 2465810
chriswk 26902 1868317 chriswk 26902 1868317
xwziegtm 26897 2124586 xwziegtm 26897 2124586
achambord 26582 1767323 achambord 26582 1767323
Patrick_G 26276 1801617 Patrick_G 26276 1801617
yorkman 26193 1992080 yorkman 26193 1992080
Ulysses 25288 1689730 Ulysses 25397 1701264
Jopo12321 25227 1652482
SFTUser 25182 1675689 SFTUser 25182 1675689
nabildanial 24942 1519409 nabildanial 25068 1531665
Sharaf_DG 24765 1786697 Sharaf_DG 24765 1786697
Maxim 24705 1502062
rodneyc 24376 1416402 rodneyc 24376 1416402
jsys14 24297 1721230
agg177 23890 1395014 agg177 23890 1395014
Goatminola 23763 1956036 srowen 23842 1342508
Ente 23639 1671638 Ente 23752 1678188
Jopo12321 23467 1483172 jojo2357 23479 2061238
JanErik 23408 1703875 JanErik 23408 1703875
Isidor 23388 1680691 Isidor 23388 1680691
Norabor 23371 1603244 Norabor 23371 1603244
cisco2015 22920 1763301 cisco2015 22920 1763301
jsys14 22824 1591906
Zirie 22542 1472937 Zirie 22542 1472937
Nullvalue 22490 1970374
AndreasKrug 22485 1769491
team-oh 22272 1636708 team-oh 22272 1636708
Roady 22220 1465606 Roady 22220 1465606
MazeOfGalious 21978 1629593 MazeOfGalious 21978 1629593
@@ -173,79 +184,83 @@ dex 21612 1467203
nesoneg 21494 1463031 nesoneg 21494 1463031
user213718 21454 1404128 user213718 21454 1404128
sphinx 21211 1384728 sphinx 21211 1384728
AndreasKrug 21097 1634811 qoo_charly_cai 21135 1514907
jjoshua2 21001 1423089 jjoshua2 21001 1423089
Zake9298 20938 1565848 Zake9298 20938 1565848
horst.prack 20878 1465656 horst.prack 20878 1465656
0xB00B1ES 20590 1208666 0xB00B1ES 20590 1208666
Serpensin 20487 1729674
Dinde 20440 1292390
j3corre 20405 941444 j3corre 20405 941444
Adrian.Schmidt123 20316 1281436 Adrian.Schmidt123 20316 1281436
wei 19973 1745989 wei 19973 1745989
notchris 19958 1800128
Serpensin 19840 1697528
Gaster319 19712 1677310
fishtester 19617 1257388 fishtester 19617 1257388
rstoesser 19569 1293588 rstoesser 19569 1293588
eudhan 19274 1283717 eudhan 19274 1283717
votoanthuan 19108 1609992
vulcan 18871 1729392 vulcan 18871 1729392
Karpovbot 18766 1053178 Karpovbot 18766 1053178
qoo_charly_cai 18543 1284937 WoodMan777 18556 1628264
jundery 18445 1115855 jundery 18445 1115855
ville 17883 1384026 ville 17883 1384026
chris 17698 1487385 chris 17698 1487385
purplefishies 17595 1092533 purplefishies 17595 1092533
dju 17414 981289 dju 17414 981289
ols 17291 1042003
iisiraider 17275 1049015 iisiraider 17275 1049015
Skiff84 17111 950248
DragonLord 17014 1162790 DragonLord 17014 1162790
redstone59 16842 1461780 redstone59 16842 1461780
Alb11747 16787 1213926 Karby 16839 1010124
Alb11747 16787 1213990
pirt 16493 1237199
Naven94 16414 951718
wizardassassin 16392 1148672
IgorLeMasson 16064 1147232 IgorLeMasson 16064 1147232
Karby 15982 979610
scuzzi 15757 968735 scuzzi 15757 968735
ako027ako 15671 1173203 ako027ako 15671 1173203
Nikolay.IT 15154 1068349 Nikolay.IT 15154 1068349
Andrew Grant 15114 895539 Andrew Grant 15114 895539
Naven94 15054 834762
OssumOpossum 14857 1007129 OssumOpossum 14857 1007129
ZacHFX 14783 1021842 LunaticBFF57 14525 1190310
enedene 14476 905279 enedene 14476 905279
IslandLambda 14393 958196
bpfliegel 14233 882523 bpfliegel 14233 882523
YELNAMRON 14230 1128094
mpx86 14019 759568 mpx86 14019 759568
jpulman 13982 870599 jpulman 13982 870599
Skiff84 13826 721996 getraideBFF 13871 1172846
Nesa92 13806 1116101
crocogoat 13803 1117422 crocogoat 13803 1117422
Nesa92 13786 1114691
joster 13710 946160 joster 13710 946160
mbeier 13650 1044928 mbeier 13650 1044928
Hjax 13535 915487 Hjax 13535 915487
Nullvalue 13468 1140498
Dark_wizzie 13422 1007152 Dark_wizzie 13422 1007152
Rudolphous 13244 883140 Rudolphous 13244 883140
pirt 13100 1009897
Machariel 13010 863104 Machariel 13010 863104
infinigon 12991 943216 infinigon 12991 943216
mabichito 12903 749391 mabichito 12903 749391
thijsk 12886 722107 thijsk 12886 722107
AdrianSA 12860 804972 AdrianSA 12860 804972
Flopzee 12698 894821 Flopzee 12698 894821
mschmidt 12644 863193
korposzczur 12606 838168 korposzczur 12606 838168
tsim67 12570 890180
Jackfish 12553 836958
fatmurphy 12547 853210 fatmurphy 12547 853210
Oakwen 12503 853105
SapphireBrand 12416 969604 SapphireBrand 12416 969604
Oakwen 12399 844109
deflectooor 12386 579392 deflectooor 12386 579392
modolief 12386 896470 modolief 12386 896470
TataneSan 12358 609332
Farseer 12249 694108 Farseer 12249 694108
Jackfish 12213 805008
pgontarz 12151 848794 pgontarz 12151 848794
dbernier 12103 860824 dbernier 12103 860824
getraideBFF 12072 1024966 FormazChar 11989 907809
stocky 11954 699440 stocky 11954 699440
mschmidt 11941 803401 somethingintheshadows 11940 989472
MooTheCow 11870 773598 MooTheCow 11892 776126
FormazChar 11766 885707 3cho 11842 1036786
whelanh 11557 245188 whelanh 11557 245188
3cho 11494 1031076
infinity 11470 727027 infinity 11470 727027
aga 11412 695127 aga 11412 695127
torbjo 11395 729145 torbjo 11395 729145
@@ -253,15 +268,19 @@ Thomas A. Anderson 11372 732094
savage84 11358 670860 savage84 11358 670860
d64 11263 789184 d64 11263 789184
ali-al-zhrani 11245 779246 ali-al-zhrani 11245 779246
ckaz 11170 680866
snicolet 11106 869170 snicolet 11106 869170
dapper 11032 771402 dapper 11032 771402
ols 10947 624903 Ethnikoi 10993 945906
Karmatron 10828 677458 Snuuka 10938 435504
Karmatron 10859 678058
basepi 10637 744851 basepi 10637 744851
jibarbosa 10628 857100
Cubox 10621 826448 Cubox 10621 826448
mecevdimitar 10609 787318
michaelrpg 10509 739239 michaelrpg 10509 739239
Def9Infinity 10427 686978
OIVAS7572 10420 995586 OIVAS7572 10420 995586
jojo2357 10419 929708 wxt9861 10412 1013864
WoodMan777 10380 873720
Garruk 10365 706465 Garruk 10365 706465
dzjp 10343 732529 dzjp 10343 732529
+120
View File
@@ -0,0 +1,120 @@
#!/bin/sh
#
# Returns properties of the native system.
# best architecture as supported by the CPU
# filename of the best binary uploaded as an artifact during CI
#
# Check if all the given flags are present in the CPU flags list
check_flags() {
for flag; do
printf '%s\n' "$flags" | grep -q -w "$flag" || return 1
done
}
# Set the CPU flags list
# remove underscores and points from flags, e.g. gcc uses avx512vnni, while some cpuinfo can have avx512_vnni, some systems use sse4_1 others sse4.1
get_flags() {
flags=$(awk '/^flags[ \t]*:|^Features[ \t]*:/{gsub(/^flags[ \t]*:[ \t]*|^Features[ \t]*:[ \t]*|[_.]/, ""); line=$0} END{print line}' /proc/cpuinfo)
}
# Check for gcc march "znver1" or "znver2" https://en.wikichip.org/wiki/amd/cpuid
check_znver_1_2() {
vendor_id=$(awk '/^vendor_id/{print $3; exit}' /proc/cpuinfo)
cpu_family=$(awk '/^cpu family/{print $4; exit}' /proc/cpuinfo)
[ "$vendor_id" = "AuthenticAMD" ] && [ "$cpu_family" = "23" ] && znver_1_2=true
}
# Set the file CPU x86_64 architecture
set_arch_x86_64() {
if check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then
true_arch='x86-64-vnni256'
elif check_flags 'avx512f' 'avx512bw'; then
true_arch='x86-64-avx512'
elif [ -z "${znver_1_2+1}" ] && check_flags 'bmi2'; then
true_arch='x86-64-bmi2'
elif check_flags 'avx2'; then
true_arch='x86-64-avx2'
elif check_flags 'sse41' && check_flags 'popcnt'; then
true_arch='x86-64-sse41-popcnt'
else
true_arch='x86-64'
fi
}
# Check the system type
uname_s=$(uname -s)
uname_m=$(uname -m)
case $uname_s in
'Darwin') # Mac OSX system
case $uname_m in
'arm64')
true_arch='apple-silicon'
file_arch='x86-64-sse41-popcnt' # Supported by Rosetta 2
;;
'x86_64')
flags=$(sysctl -n machdep.cpu.features machdep.cpu.leaf7_features | tr '\n' ' ' | tr '[:upper:]' '[:lower:]' | tr -d '_.')
set_arch_x86_64
if [ "$true_arch" = 'x86-64-vnni256' ] || [ "$true_arch" = 'x86-64-avx512' ]; then
file_arch='x86-64-bmi2'
fi
;;
esac
file_os='macos'
file_ext='tar'
;;
'Linux') # Linux system
get_flags
case $uname_m in
'x86_64')
file_os='ubuntu'
check_znver_1_2
set_arch_x86_64
;;
'i686')
file_os='ubuntu'
true_arch='x86-32'
;;
'aarch64')
file_os='android'
true_arch='armv8'
if check_flags 'asimddp'; then
true_arch="$true_arch-dotprod"
fi
;;
'armv7'*)
file_os='android'
true_arch='armv7'
if check_flags 'neon'; then
true_arch="$true_arch-neon"
fi
;;
*) # Unsupported machine type, exit with error
printf 'Unsupported machine type: %s\n' "$uname_m"
exit 1
;;
esac
file_ext='tar'
;;
'CYGWIN'*|'MINGW'*|'MSYS'*) # Windows system with POSIX compatibility layer
get_flags
check_znver_1_2
set_arch_x86_64
file_os='windows'
file_ext='zip'
;;
*)
# Unknown system type, exit with error
printf 'Unsupported system type: %s\n' "$uname_s"
exit 1
;;
esac
if [ -z "$file_arch" ]; then
file_arch=$true_arch
fi
file_name="stockfish-$file_os-$file_arch.$file_ext"
printf '%s %s\n' "$true_arch" "$file_name"
+142 -70
View File
@@ -1,5 +1,5 @@
# Stockfish, a UCI chess playing engine derived from Glaurung 2.1 # Stockfish, a UCI chess playing engine derived from Glaurung 2.1
# Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) # Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
# #
# Stockfish is free software: you can redistribute it and/or modify # Stockfish is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@@ -20,9 +20,9 @@
### ========================================================================== ### ==========================================================================
### Establish the operating system name ### Establish the operating system name
KERNEL = $(shell uname -s) KERNEL := $(shell uname -s)
ifeq ($(KERNEL),Linux) ifeq ($(KERNEL),Linux)
OS = $(shell uname -o) OS := $(shell uname -o)
endif endif
### Target Windows OS ### Target Windows OS
@@ -33,7 +33,7 @@ ifeq ($(OS),Windows_NT)
else ifeq ($(COMP),mingw) else ifeq ($(COMP),mingw)
target_windows = yes target_windows = yes
ifeq ($(WINE_PATH),) ifeq ($(WINE_PATH),)
WINE_PATH = $(shell which wine) WINE_PATH := $(shell which wine)
endif endif
endif endif
@@ -49,17 +49,21 @@ PREFIX = /usr/local
BINDIR = $(PREFIX)/bin BINDIR = $(PREFIX)/bin
### Built-in benchmark for pgo-builds ### Built-in benchmark for pgo-builds
ifeq ($(SDE_PATH),)
PGOBENCH = $(WINE_PATH) ./$(EXE) bench PGOBENCH = $(WINE_PATH) ./$(EXE) bench
else
PGOBENCH = $(SDE_PATH) -- $(WINE_PATH) ./$(EXE) bench
endif
### Source and object files ### Source and object files
SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \ SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \
material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \ misc.cpp movegen.cpp movepick.cpp position.cpp cluster.cpp \
search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
nnue/evaluate_nnue.cpp nnue/features/half_ka_v2_hm.cpp nnue/nnue_misc.cpp nnue/features/half_ka_v2_hm.cpp nnue/network.cpp
HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \
nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/layers/affine_transform.h \
nnue/layers/affine_transform_sparse_input.h nnue/layers/clipped_relu.h nnue/layers/simd.h \
nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \
nnue/nnue_common.h nnue/nnue_feature_transformer.h position.h \
search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \
tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h cluster.h
OBJS = $(notdir $(SRCS:.cpp=.o)) OBJS = $(notdir $(SRCS:.cpp=.o))
@@ -96,6 +100,7 @@ VPATH = syzygy:nnue:nnue/features
# vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512 # vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512
# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture # neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture
# dotprod = yes/no --- -DUSE_NEON_DOTPROD --- Use ARM advanced SIMD Int8 dot product instructions # dotprod = yes/no --- -DUSE_NEON_DOTPROD --- Use ARM advanced SIMD Int8 dot product instructions
# mpi = yes/no --- -DUSE_MPI --- Use Message Passing Interface
# #
# Note that Makefile is space sensitive, so when adding new architectures # Note that Makefile is space sensitive, so when adding new architectures
# or modifying existing flags, you have to make sure there are no extra spaces # or modifying existing flags, you have to make sure there are no extra spaces
@@ -108,16 +113,20 @@ VPATH = syzygy:nnue:nnue/features
### 2.1. General and architecture defaults ### 2.1. General and architecture defaults
ifeq ($(ARCH),) ifeq ($(ARCH),)
ARCH = x86-64-modern ARCH = native
help_skip_sanity = yes
endif endif
ifeq ($(ARCH), native)
override ARCH := $(shell $(SHELL) ../scripts/get_native_properties.sh | cut -d " " -f 1)
endif
# explicitly check for the list of supported architectures (as listed with make help), # explicitly check for the list of supported architectures (as listed with make help),
# the user can override with `make ARCH=x86-32-vnni256 SUPPORTED_ARCH=true` # the user can override with `make ARCH=x86-32-vnni256 SUPPORTED_ARCH=true`
ifeq ($(ARCH), $(filter $(ARCH), \ ifeq ($(ARCH), $(filter $(ARCH), \
x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \ x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \
x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \
x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \ x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \
armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64)) armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64 loongarch64))
SUPPORTED_ARCH=true SUPPORTED_ARCH=true
else else
SUPPORTED_ARCH=false SUPPORTED_ARCH=false
@@ -141,10 +150,17 @@ avx512 = no
vnni256 = no vnni256 = no
vnni512 = no vnni512 = no
neon = no neon = no
mpi = no
dotprod = no dotprod = no
arm_version = 0 arm_version = 0
STRIP = strip STRIP = strip
ifneq ($(shell which clang-format-17 2> /dev/null),)
CLANG-FORMAT = clang-format-17
else
CLANG-FORMAT = clang-format
endif
### 2.2 Architecture specific ### 2.2 Architecture specific
ifeq ($(findstring x86,$(ARCH)),x86) ifeq ($(findstring x86,$(ARCH)),x86)
@@ -193,6 +209,8 @@ ifeq ($(findstring -sse41,$(ARCH)),-sse41)
endif endif
ifeq ($(findstring -modern,$(ARCH)),-modern) ifeq ($(findstring -modern,$(ARCH)),-modern)
$(warning *** ARCH=$(ARCH) is deprecated, defaulting to ARCH=x86-64-sse41-popcnt. Execute `make help` for a list of available architectures. ***)
$(shell sleep 5)
popcnt = yes popcnt = yes
sse = yes sse = yes
sse2 = yes sse2 = yes
@@ -353,6 +371,10 @@ endif
ifeq ($(ARCH),riscv64) ifeq ($(ARCH),riscv64)
arch = riscv64 arch = riscv64
endif endif
ifeq ($(ARCH),loongarch64)
arch = loongarch64
endif
endif endif
@@ -388,6 +410,8 @@ ifeq ($(COMP),gcc)
ifeq ($(ARCH),riscv64) ifeq ($(ARCH),riscv64)
CXXFLAGS += -latomic CXXFLAGS += -latomic
endif endif
else ifeq ($(ARCH),loongarch64)
CXXFLAGS += -latomic
else else
CXXFLAGS += -m$(bits) CXXFLAGS += -m$(bits)
LDFLAGS += -m$(bits) LDFLAGS += -m$(bits)
@@ -458,6 +482,8 @@ ifeq ($(COMP),clang)
ifeq ($(ARCH),riscv64) ifeq ($(ARCH),riscv64)
CXXFLAGS += -latomic CXXFLAGS += -latomic
endif endif
else ifeq ($(ARCH),loongarch64)
CXXFLAGS += -latomic
else else
CXXFLAGS += -m$(bits) CXXFLAGS += -m$(bits)
LDFLAGS += -m$(bits) LDFLAGS += -m$(bits)
@@ -497,6 +523,14 @@ ifeq ($(COMP),ndk)
STRIP=llvm-strip STRIP=llvm-strip
endif endif
endif endif
ifeq ($(arch),x86_64)
CXX=x86_64-linux-android21-clang++
ifneq ($(shell which x86_64-linux-android-strip 2>/dev/null),)
STRIP=x86_64-linux-android-strip
else
STRIP=llvm-strip
endif
endif
LDFLAGS += -static-libstdc++ -pie -lm -latomic LDFLAGS += -static-libstdc++ -pie -lm -latomic
endif endif
@@ -526,8 +560,8 @@ endif
### Sometimes gcc is really clang ### Sometimes gcc is really clang
ifeq ($(COMP),gcc) ifeq ($(COMP),gcc)
gccversion = $(shell $(CXX) --version 2>/dev/null) gccversion := $(shell $(CXX) --version 2>/dev/null)
gccisclang = $(findstring clang,$(gccversion)) gccisclang := $(findstring clang,$(gccversion))
ifneq ($(gccisclang),) ifneq ($(gccisclang),)
profile_make = clang-profile-make profile_make = clang-profile-make
profile_use = clang-profile-use profile_use = clang-profile-use
@@ -564,7 +598,7 @@ endif
### 3.3 Optimization ### 3.3 Optimization
ifeq ($(optimize),yes) ifeq ($(optimize),yes)
CXXFLAGS += -O3 CXXFLAGS += -O3 -funroll-loops
ifeq ($(comp),gcc) ifeq ($(comp),gcc)
ifeq ($(OS), Android) ifeq ($(OS), Android)
@@ -585,7 +619,7 @@ ifeq ($(optimize),yes)
endif endif
ifeq ($(comp),clang) ifeq ($(comp),clang)
clangmajorversion = $(shell $(CXX) -dumpversion 2>/dev/null | cut -f1 -d.) clangmajorversion := $(shell $(CXX) -dumpversion 2>/dev/null | cut -f1 -d.)
ifeq ($(shell expr $(clangmajorversion) \< 16),1) ifeq ($(shell expr $(clangmajorversion) \< 16),1)
CXXFLAGS += -fexperimental-new-pass-manager CXXFLAGS += -fexperimental-new-pass-manager
endif endif
@@ -672,7 +706,6 @@ ifeq ($(sse2),yes)
endif endif
ifeq ($(mmx),yes) ifeq ($(mmx),yes)
CXXFLAGS += -DUSE_MMX
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -mmmx CXXFLAGS += -mmmx
endif endif
@@ -701,19 +734,24 @@ ifeq ($(pext),yes)
endif endif
endif endif
### 3.7.1 Try to include git commit sha for versioning ### 3.8.1 Try to include git commit sha for versioning
GIT_SHA = $(shell git rev-parse HEAD 2>/dev/null | cut -c 1-8) GIT_SHA := $(shell git rev-parse HEAD 2>/dev/null | cut -c 1-8)
ifneq ($(GIT_SHA), ) ifneq ($(GIT_SHA), )
CXXFLAGS += -DGIT_SHA=$(GIT_SHA) CXXFLAGS += -DGIT_SHA=$(GIT_SHA)
endif endif
### 3.7.2 Try to include git commit date for versioning ### 3.8.2 Try to include git commit date for versioning
GIT_DATE = $(shell git show -s --date=format:'%Y%m%d' --format=%cd HEAD 2>/dev/null) GIT_DATE := $(shell git show -s --date=format:'%Y%m%d' --format=%cd HEAD 2>/dev/null)
ifneq ($(GIT_DATE), ) ifneq ($(GIT_DATE), )
CXXFLAGS += -DGIT_DATE=$(GIT_DATE) CXXFLAGS += -DGIT_DATE=$(GIT_DATE)
endif endif
### 3.8 Link Time Optimization ### 3.8.3 Try to include architecture
ifneq ($(ARCH), )
CXXFLAGS += -DARCH=$(ARCH)
endif
### 3.9 Link Time Optimization
### This is a mix of compile and link time options because the lto link phase ### This is a mix of compile and link time options because the lto link phase
### needs access to the optimization flags. ### needs access to the optimization flags.
ifeq ($(optimize),yes) ifeq ($(optimize),yes)
@@ -748,36 +786,45 @@ ifeq ($(debug), no)
endif endif
endif endif
### 3.9 Android 5 can only run position independent executables. Note that this ### 3.10 Android 5 can only run position independent executables. Note that this
### breaks Android 4.0 and earlier. ### breaks Android 4.0 and earlier.
ifeq ($(OS), Android) ifeq ($(OS), Android)
CXXFLAGS += -fPIE CXXFLAGS += -fPIE
LDFLAGS += -fPIE -pie LDFLAGS += -fPIE -pie
endif endif
### 3.10 MPI
ifneq (,$(findstring mpi, $(CXX)))
mpi = yes
endif
ifeq ($(mpi),yes)
CXXFLAGS += -DUSE_MPI -Wno-cast-qual -fexceptions
DEPENDFLAGS += -DUSE_MPI
endif
### ========================================================================== ### ==========================================================================
### Section 4. Public Targets ### Section 4. Public Targets
### ========================================================================== ### ==========================================================================
help: help:
@echo "" @echo ""
@echo "To compile stockfish, type: " @echo "To compile stockfish, type: "
@echo "" @echo ""
@echo "make target ARCH=arch [COMP=compiler] [COMPCXX=cxx]" @echo "make -j target [ARCH=arch] [COMP=compiler] [COMPCXX=cxx]"
@echo "" @echo ""
@echo "Supported targets:" @echo "Supported targets:"
@echo "" @echo ""
@echo "help > Display architecture details" @echo "help > Display architecture details"
@echo "profile-build > standard build with profile-guided optimization" @echo "profile-build > standard build with profile-guided optimization"
@echo "build > skip profile-guided optimization" @echo "build > skip profile-guided optimization"
@echo "net > Download the default nnue net" @echo "net > Download the default nnue nets"
@echo "strip > Strip executable" @echo "strip > Strip executable"
@echo "install > Install executable" @echo "install > Install executable"
@echo "clean > Clean up" @echo "clean > Clean up"
@echo "" @echo ""
@echo "Supported archs:" @echo "Supported archs:"
@echo "" @echo ""
@echo "native > select the best architecture for the host processor (default)"
@echo "x86-64-vnni512 > x86 64-bit with vnni 512bit support" @echo "x86-64-vnni512 > x86 64-bit with vnni 512bit support"
@echo "x86-64-vnni256 > x86 64-bit with vnni 512bit support, limit operands to 256bit wide" @echo "x86-64-vnni256 > x86 64-bit with vnni 512bit support, limit operands to 256bit wide"
@echo "x86-64-avx512 > x86 64-bit with avx512 support" @echo "x86-64-avx512 > x86 64-bit with avx512 support"
@@ -785,13 +832,13 @@ help:
@echo "x86-64-bmi2 > x86 64-bit with bmi2 support" @echo "x86-64-bmi2 > x86 64-bit with bmi2 support"
@echo "x86-64-avx2 > x86 64-bit with avx2 support" @echo "x86-64-avx2 > x86 64-bit with avx2 support"
@echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support" @echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support"
@echo "x86-64-modern > common modern CPU, currently x86-64-sse41-popcnt" @echo "x86-64-modern > deprecated, currently x86-64-sse41-popcnt"
@echo "x86-64-ssse3 > x86 64-bit with ssse3 support" @echo "x86-64-ssse3 > x86 64-bit with ssse3 support"
@echo "x86-64-sse3-popcnt > x86 64-bit with sse3 and popcnt support" @echo "x86-64-sse3-popcnt > x86 64-bit with sse3 compile and popcnt support"
@echo "x86-64 > x86 64-bit generic (with sse2 support)" @echo "x86-64 > x86 64-bit generic (with sse2 support)"
@echo "x86-32-sse41-popcnt > x86 32-bit with sse41 and popcnt support" @echo "x86-32-sse41-popcnt > x86 32-bit with sse41 and popcnt support"
@echo "x86-32-sse2 > x86 32-bit with sse2 support" @echo "x86-32-sse2 > x86 32-bit with sse2 support"
@echo "x86-32 > x86 32-bit generic (with mmx and sse support)" @echo "x86-32 > x86 32-bit generic (with mmx compile support)"
@echo "ppc-64 > PPC 64-bit" @echo "ppc-64 > PPC 64-bit"
@echo "ppc-32 > PPC 32-bit" @echo "ppc-32 > PPC 32-bit"
@echo "armv7 > ARMv7 32-bit" @echo "armv7 > ARMv7 32-bit"
@@ -803,11 +850,12 @@ help:
@echo "general-64 > unspecified 64-bit" @echo "general-64 > unspecified 64-bit"
@echo "general-32 > unspecified 32-bit" @echo "general-32 > unspecified 32-bit"
@echo "riscv64 > RISC-V 64-bit" @echo "riscv64 > RISC-V 64-bit"
@echo "loongarch64 > LoongArch 64-bit"
@echo "" @echo ""
@echo "Supported compilers:" @echo "Supported compilers:"
@echo "" @echo ""
@echo "gcc > Gnu compiler (default)" @echo "gcc > GNU compiler (default)"
@echo "mingw > Gnu compiler with MinGW under Windows" @echo "mingw > GNU compiler with MinGW under Windows"
@echo "clang > LLVM Clang compiler" @echo "clang > LLVM Clang compiler"
@echo "icx > Intel oneAPI DPC++/C++ Compiler" @echo "icx > Intel oneAPI DPC++/C++ Compiler"
@echo "ndk > Google NDK to cross-compile for Android" @echo "ndk > Google NDK to cross-compile for Android"
@@ -815,30 +863,30 @@ help:
@echo "Simple examples. If you don't know what to do, you likely want to run one of: " @echo "Simple examples. If you don't know what to do, you likely want to run one of: "
@echo "" @echo ""
@echo "make -j profile-build ARCH=x86-64-avx2 # typically a fast compile for common systems " @echo "make -j profile-build ARCH=x86-64-avx2 # typically a fast compile for common systems "
@echo "make -j profile-build ARCH=x86-64-modern # A more portable compile for 64-bit systems " @echo "make -j profile-build ARCH=x86-64-sse41-popcnt # A more portable compile for 64-bit systems "
@echo "make -j profile-build ARCH=x86-64 # A portable compile for 64-bit systems " @echo "make -j profile-build ARCH=x86-64 # A portable compile for 64-bit systems "
@echo "" @echo ""
@echo "Advanced examples, for experienced users: " @echo "Advanced examples, for experienced users: "
@echo "" @echo ""
@echo "make -j profile-build ARCH=x86-64-bmi2" @echo "make -j profile-build ARCH=x86-64-avxvnni"
@echo "make -j profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-9.0" @echo "make -j profile-build ARCH=x86-64-avxvnni COMP=gcc COMPCXX=g++-12.0"
@echo "make -j build ARCH=x86-64-ssse3 COMP=clang" @echo "make -j build ARCH=x86-64-ssse3 COMP=clang"
@echo "" @echo ""
@echo "-------------------------------" ifneq ($(SUPPORTED_ARCH), true)
ifeq ($(SUPPORTED_ARCH)$(help_skip_sanity), true)
@echo "The selected architecture $(ARCH) will enable the following configuration: "
@$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity
else
@echo "Specify a supported architecture with the ARCH option for more details" @echo "Specify a supported architecture with the ARCH option for more details"
@echo "" @echo ""
endif endif
.PHONY: help build profile-build strip install clean net objclean profileclean \ .PHONY: help analyze build profile-build strip install clean net \
config-sanity \ objclean profileclean config-sanity \
icx-profile-use icx-profile-make \ icx-profile-use icx-profile-make \
gcc-profile-use gcc-profile-make \ gcc-profile-use gcc-profile-make \
clang-profile-use clang-profile-make FORCE clang-profile-use clang-profile-make FORCE \
format analyze
analyze: net config-sanity objclean
$(MAKE) -k ARCH=$(ARCH) COMP=$(COMP) $(OBJS)
build: net config-sanity build: net config-sanity
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) all $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all
@@ -849,7 +897,8 @@ profile-build: net config-sanity objclean profileclean
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make) $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make)
@echo "" @echo ""
@echo "Step 2/4. Running benchmark for pgo-build ..." @echo "Step 2/4. Running benchmark for pgo-build ..."
$(PGOBENCH) 2>&1 | tail -n 4 $(PGOBENCH) > PGOBENCH.out 2>&1
tail -n 4 PGOBENCH.out
@echo "" @echo ""
@echo "Step 3/4. Building optimized executable ..." @echo "Step 3/4. Building optimized executable ..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) objclean $(MAKE) ARCH=$(ARCH) COMP=$(COMP) objclean
@@ -870,23 +919,35 @@ install:
clean: objclean profileclean clean: objclean profileclean
@rm -f .depend *~ core @rm -f .depend *~ core
# evaluation network (nnue) # clean binaries and objects
net: objclean:
$(eval nnuenet := $(shell grep EvalFileDefaultName evaluate.h | grep define | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/')) @rm -f stockfish stockfish.exe *.o ./syzygy/*.o ./nnue/*.o ./nnue/features/*.o
# clean auxiliary profiling files
profileclean:
@rm -rf profdir
@rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda *.s PGOBENCH.out
@rm -f stockfish.profdata *.profraw
@rm -f stockfish.*args*
@rm -f stockfish.*lt*
@rm -f stockfish.res
@rm -f ./-lstdc++.res
define fetch_network
@echo "Default net: $(nnuenet)" @echo "Default net: $(nnuenet)"
$(eval nnuedownloadurl1 := https://tests.stockfishchess.org/api/nn/$(nnuenet))
$(eval nnuedownloadurl2 := https://github.com/official-stockfish/networks/raw/master/$(nnuenet))
$(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi))
@if [ "x$(curl_or_wget)" = "x" ]; then \ @if [ "x$(curl_or_wget)" = "x" ]; then \
echo "Neither curl nor wget is installed. Install one of these tools unless the net has been downloaded manually"; \ echo "Neither curl nor wget is installed. Install one of these tools unless the net has been downloaded manually"; \
fi fi
$(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi))
@if [ "x$(shasum_command)" = "x" ]; then \ @if [ "x$(shasum_command)" = "x" ]; then \
echo "shasum / sha256sum not found, skipping net validation"; \ echo "shasum / sha256sum not found, skipping net validation"; \
fi elif test -f "$(nnuenet)"; then \
if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \
echo "Removing invalid network"; rm -f $(nnuenet); \
fi; \
fi;
@for nnuedownloadurl in "$(nnuedownloadurl1)" "$(nnuedownloadurl2)"; do \ @for nnuedownloadurl in "$(nnuedownloadurl1)" "$(nnuedownloadurl2)"; do \
if test -f "$(nnuenet)"; then \ if test -f "$(nnuenet)"; then \
echo "$(nnuenet) available."; \ echo "$(nnuenet) available : OK"; break; \
else \ else \
if [ "x$(curl_or_wget)" != "x" ]; then \ if [ "x$(curl_or_wget)" != "x" ]; then \
echo "Downloading $${nnuedownloadurl}"; $(curl_or_wget) $${nnuedownloadurl} > $(nnuenet);\ echo "Downloading $${nnuedownloadurl}"; $(curl_or_wget) $${nnuedownloadurl} > $(nnuenet);\
@@ -897,29 +958,39 @@ net:
if [ "x$(shasum_command)" != "x" ]; then \ if [ "x$(shasum_command)" != "x" ]; then \
if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \
echo "Removing failed download"; rm -f $(nnuenet); \ echo "Removing failed download"; rm -f $(nnuenet); \
else \
echo "Network validated"; break; \
fi; \ fi; \
fi; \ fi; \
done done
@if ! test -f "$(nnuenet)"; then \ @if ! test -f "$(nnuenet)"; then \
echo "Failed to download $(nnuenet)."; \ echo "Failed to download $(nnuenet)."; \
fi fi;
@if [ "x$(shasum_command)" != "x" ]; then \
if [ "$(nnuenet)" = "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \
echo "Network validated"; break; \
fi; \
fi;
endef
# clean binaries and objects # set up shell variables for the net stuff
objclean: define netvariables
@rm -f stockfish stockfish.exe *.o ./syzygy/*.o ./nnue/*.o ./nnue/features/*.o $(eval nnuenet := $(shell grep $(1) evaluate.h | grep define | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/'))
$(eval nnuedownloadurl1 := https://tests.stockfishchess.org/api/nn/$(nnuenet))
$(eval nnuedownloadurl2 := https://github.com/official-stockfish/networks/raw/master/$(nnuenet))
$(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi))
$(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi))
endef
# clean auxiliary profiling files # evaluation network (nnue)
profileclean: net:
@rm -rf profdir $(call netvariables, EvalFileDefaultNameBig)
@rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda *.s $(call fetch_network)
@rm -f stockfish.profdata *.profraw $(call netvariables, EvalFileDefaultNameSmall)
@rm -f stockfish.*args* $(call fetch_network)
@rm -f stockfish.*lt*
@rm -f stockfish.res
@rm -f ./-lstdc++.res
format:
$(CLANG-FORMAT) -i $(SRCS) $(HEADERS) -style=file
# default target
default: default:
help help
@@ -953,6 +1024,7 @@ config-sanity: net
@echo "vnni256: '$(vnni256)'" @echo "vnni256: '$(vnni256)'"
@echo "vnni512: '$(vnni512)'" @echo "vnni512: '$(vnni512)'"
@echo "neon: '$(neon)'" @echo "neon: '$(neon)'"
@echo "mpi: '$(mpi)'"
@echo "dotprod: '$(dotprod)'" @echo "dotprod: '$(dotprod)'"
@echo "arm_version: '$(arm_version)'" @echo "arm_version: '$(arm_version)'"
@echo "target_windows: '$(target_windows)'" @echo "target_windows: '$(target_windows)'"
@@ -969,7 +1041,7 @@ config-sanity: net
@test "$(SUPPORTED_ARCH)" = "true" @test "$(SUPPORTED_ARCH)" = "true"
@test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "e2k" || \ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "e2k" || \
test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" || test "$(arch)" = "riscv64" test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" || test "$(arch)" = "riscv64" || test "$(arch)" = "loongarch64"
@test "$(bits)" = "32" || test "$(bits)" = "64" @test "$(bits)" = "32" || test "$(bits)" = "64"
@test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
@test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no"
+29 -41
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,18 +18,17 @@
#include "benchmark.h" #include "benchmark.h"
#include <cstdlib>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <istream>
#include <vector> #include <vector>
#include "position.h" #include "position.h"
using namespace std;
namespace { namespace {
const vector<string> Defaults = { // clang-format off
const std::vector<std::string> Defaults = {
"setoption name UCI_Chess960 value false", "setoption name UCI_Chess960 value false",
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10", "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10",
@@ -92,36 +91,34 @@ const vector<string> Defaults = {
"nqbnrkrb/pppppppp/8/8/8/8/PPPPPPPP/NQBNRKRB w KQkq - 0 1", "nqbnrkrb/pppppppp/8/8/8/8/PPPPPPPP/NQBNRKRB w KQkq - 0 1",
"setoption name UCI_Chess960 value false" "setoption name UCI_Chess960 value false"
}; };
// clang-format on
} // namespace } // namespace
namespace Stockfish { namespace Stockfish {
/// setup_bench() builds a list of UCI commands to be run by bench. There // Builds a list of UCI commands to be run by bench. There
/// are five parameters: TT size in MB, number of search threads that // 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 // should be used, the limit value spent for each position, a file name
/// where to look for positions in FEN format, the type of the limit: // where to look for positions in FEN format, and the type of the limit:
/// depth, perft, nodes and movetime (in millisecs), and evaluation type // depth, perft, nodes and movetime (in milliseconds). Examples:
/// mixed (default), classical, NNUE. //
/// // bench : search default positions up to depth 13
/// bench -> search default positions up to depth 13 // bench 64 1 15 : search default positions up to depth 15 (TT = 64MB)
/// bench 64 1 15 -> search default positions up to depth 15 (TT = 64MB) // bench 64 1 100000 default nodes : search default positions for 100K nodes each
/// bench 64 4 5000 current movetime -> search current position with 4 threads for 5 sec // bench 64 4 5000 current movetime : search current position with 4 threads for 5 sec
/// bench 64 1 100000 default nodes -> search default positions for 100K nodes each // bench 16 1 5 blah perft : run a perft 5 on positions in file "blah"
/// bench 16 1 5 default perft -> run a perft 5 on default positions std::vector<std::string> setup_bench(const Position& current, std::istream& is) {
vector<string> setup_bench(const Position& current, istream& is) { std::vector<std::string> fens, list;
std::string go, token;
vector<string> fens, list;
string go, token;
// Assign default values to missing arguments // Assign default values to missing arguments
string ttSize = (is >> token) ? token : "16"; std::string ttSize = (is >> token) ? token : "16";
string threads = (is >> token) ? token : "1"; std::string threads = (is >> token) ? token : "1";
string limit = (is >> token) ? token : "13"; std::string limit = (is >> token) ? token : "13";
string fenFile = (is >> token) ? token : "default"; std::string fenFile = (is >> token) ? token : "default";
string limitType = (is >> token) ? token : "depth"; std::string limitType = (is >> token) ? token : "depth";
string evalType = (is >> token) ? token : "mixed";
go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit; go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit;
@@ -133,12 +130,12 @@ vector<string> setup_bench(const Position& current, istream& is) {
else else
{ {
string fen; std::string fen;
ifstream file(fenFile); std::ifstream file(fenFile);
if (!file.is_open()) if (!file.is_open())
{ {
cerr << "Unable to open file " << fenFile << endl; std::cerr << "Unable to open file " << fenFile << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -153,24 +150,15 @@ vector<string> setup_bench(const Position& current, istream& is) {
list.emplace_back("setoption name Hash value " + ttSize); list.emplace_back("setoption name Hash value " + ttSize);
list.emplace_back("ucinewgame"); list.emplace_back("ucinewgame");
size_t posCounter = 0; for (const std::string& fen : fens)
if (fen.find("setoption") != std::string::npos)
for (const string& fen : fens)
if (fen.find("setoption") != string::npos)
list.emplace_back(fen); list.emplace_back(fen);
else else
{ {
if (evalType == "classical" || (evalType == "mixed" && posCounter % 2 == 0))
list.emplace_back("setoption name Use NNUE value false");
else if (evalType == "NNUE" || (evalType == "mixed" && posCounter % 2 != 0))
list.emplace_back("setoption name Use NNUE value true");
list.emplace_back("position fen " + fen); list.emplace_back("position fen " + fen);
list.emplace_back(go); list.emplace_back(go);
++posCounter;
} }
list.emplace_back("setoption name Use NNUE value true");
return list; return list;
} }
+1 -1
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
-172
View File
@@ -1,172 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <vector>
#include <bitset>
#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.
// Positions with the pawn on files E to H will be mirrored before probing.
constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608
std::bitset<MAX_INDEX> KPKBitbase;
// A KPK bitbase index is an integer in [0, IndexMax] range
//
// Information is mapped in a way that minimizes the number of iterations:
//
// bit 0- 5: white king square (from SQ_A1 to SQ_H8)
// bit 6-11: black king square (from SQ_A1 to SQ_H8)
// bit 12: side to move (WHITE or BLACK)
// bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2)
unsigned index(Color stm, Square bksq, Square wksq, Square psq) {
return int(wksq) | (bksq << 6) | (stm << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15);
}
enum Result {
INVALID = 0,
UNKNOWN = 1,
DRAW = 2,
WIN = 4
};
Result& operator|=(Result& r, Result v) { return r = Result(r | v); }
struct KPKPosition {
KPKPosition() = default;
explicit KPKPosition(unsigned idx);
operator Result() const { return result; }
Result classify(const std::vector<KPKPosition>& db);
Color stm;
Square ksq[COLOR_NB], psq;
Result result;
};
} // namespace
bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) {
assert(file_of(wpsq) <= FILE_D);
return KPKBitbase[index(stm, bksq, wksq, wpsq)];
}
void Bitbases::init() {
std::vector<KPKPosition> db(MAX_INDEX);
unsigned idx, repeat = 1;
// Initialize db with known win / draw positions
for (idx = 0; idx < MAX_INDEX; ++idx)
db[idx] = KPKPosition(idx);
// Iterate through the positions until none of the unknown positions can be
// changed to either wins or draws (15 cycles needed).
while (repeat)
for (repeat = idx = 0; idx < MAX_INDEX; ++idx)
repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN);
// Fill the bitbase with the decisive results
for (idx = 0; idx < MAX_INDEX; ++idx)
if (db[idx] == WIN)
KPKBitbase.set(idx);
}
namespace {
KPKPosition::KPKPosition(unsigned idx) {
ksq[WHITE] = Square((idx >> 0) & 0x3F);
ksq[BLACK] = Square((idx >> 6) & 0x3F);
stm = Color ((idx >> 12) & 0x01);
psq = make_square(File((idx >> 13) & 0x3), Rank(RANK_7 - ((idx >> 15) & 0x7)));
// Invalid if two pieces are on the same square or if a king can be captured
if ( distance(ksq[WHITE], ksq[BLACK]) <= 1
|| ksq[WHITE] == psq
|| ksq[BLACK] == psq
|| (stm == WHITE && (pawn_attacks_bb(WHITE, psq) & ksq[BLACK])))
result = INVALID;
// Win if the pawn can be promoted without getting captured
else if ( stm == WHITE
&& rank_of(psq) == RANK_7
&& ksq[WHITE] != psq + NORTH
&& ( distance(ksq[BLACK], psq + NORTH) > 1
|| (distance(ksq[WHITE], psq + NORTH) == 1)))
result = WIN;
// Draw if it is stalemate or the black king can capture the pawn
else if ( stm == BLACK
&& ( !(attacks_bb<KING>(ksq[BLACK]) & ~(attacks_bb<KING>(ksq[WHITE]) | pawn_attacks_bb(WHITE, psq)))
|| (attacks_bb<KING>(ksq[BLACK]) & ~attacks_bb<KING>(ksq[WHITE]) & psq)))
result = DRAW;
// Position will be classified later
else
result = UNKNOWN;
}
Result KPKPosition::classify(const std::vector<KPKPosition>& db) {
// White to move: If one move leads to a position classified as WIN, the result
// of the current position is WIN. If all moves lead to positions classified
// as DRAW, the current position is classified as DRAW, otherwise the current
// position is classified as UNKNOWN.
//
// Black to move: If one move leads to a position classified as DRAW, the result
// of the current position is DRAW. If all moves lead to positions classified
// as WIN, the position is classified as WIN, otherwise the current position is
// classified as UNKNOWN.
const Result Good = (stm == WHITE ? WIN : DRAW);
const Result Bad = (stm == WHITE ? DRAW : WIN);
Result r = INVALID;
Bitboard b = attacks_bb<KING>(ksq[stm]);
while (b)
r |= stm == WHITE ? db[index(BLACK, ksq[BLACK], pop_lsb(b), psq)]
: db[index(WHITE, pop_lsb(b), ksq[WHITE], psq)];
if (stm == WHITE)
{
if (rank_of(psq) < RANK_7) // Single push
r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH)];
if ( rank_of(psq) == RANK_2 // Double push
&& psq + NORTH != ksq[WHITE]
&& psq + NORTH != ksq[BLACK])
r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH + NORTH)];
}
return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad;
}
} // namespace
} // namespace Stockfish
+17 -19
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -16,10 +16,12 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "bitboard.h"
#include <algorithm> #include <algorithm>
#include <bitset> #include <bitset>
#include <initializer_list>
#include "bitboard.h"
#include "misc.h" #include "misc.h"
namespace Stockfish { namespace Stockfish {
@@ -42,20 +44,16 @@ namespace {
void init_magics(PieceType pt, Bitboard table[], Magic magics[]); void init_magics(PieceType pt, Bitboard table[], Magic magics[]);
} // Returns the bitboard of target square for the given step
// from the given square. If the step is off the board, returns empty bitboard.
/// safe_destination() returns the bitboard of target square for the given step Bitboard safe_destination(Square s, int step) {
/// from the given square. If the step is off the board, returns empty bitboard.
inline Bitboard safe_destination(Square s, int step) {
Square to = Square(s + step); Square to = Square(s + step);
return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
} }
}
// Returns an ASCII representation of a bitboard suitable
/// Bitboards::pretty() returns an ASCII representation of a bitboard suitable // to be printed to standard output. Useful for debugging.
/// to be printed to standard output. Useful for debugging.
std::string Bitboards::pretty(Bitboard b) { std::string Bitboards::pretty(Bitboard b) {
std::string s = "+---+---+---+---+---+---+---+---+\n"; std::string s = "+---+---+---+---+---+---+---+---+\n";
@@ -73,9 +71,8 @@ std::string Bitboards::pretty(Bitboard b) {
} }
/// Bitboards::init() initializes various bitboard tables. It is called at // Initializes various bitboard tables. It is called at
/// startup and relies on global objects to be already zero-initialized. // startup and relies on global objects to be already zero-initialized.
void Bitboards::init() { void Bitboards::init() {
for (unsigned i = 0; i < (1 << 16); ++i) for (unsigned i = 0; i < (1 << 16); ++i)
@@ -108,7 +105,8 @@ void Bitboards::init() {
if (PseudoAttacks[pt][s1] & 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] =
(attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1)));
} }
BetweenBB[s1][s2] |= s2; BetweenBB[s1][s2] |= s2;
} }
@@ -134,11 +132,10 @@ namespace {
} }
// init_magics() computes all rook and bishop attacks at startup. Magic // Computes all rook and bishop attacks at startup. Magic
// bitboards are used to look up attacks of sliding pieces. As a reference see // bitboards are used to look up attacks of sliding pieces. As a reference see
// www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so // www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so
// called "fancy" approach. // called "fancy" approach.
void init_magics(PieceType pt, Bitboard table[], Magic magics[]) { void init_magics(PieceType pt, Bitboard table[], Magic magics[]) {
// Optimal PRNG seeds to pick the correct magics in the shortest time // Optimal PRNG seeds to pick the correct magics in the shortest time
@@ -169,7 +166,8 @@ namespace {
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and // Use Carry-Rippler trick to enumerate all subsets of masks[s] and
// store the corresponding sliding attack bitboard in reference[]. // store the corresponding sliding attack bitboard in reference[].
b = size = 0; b = size = 0;
do { do
{
occupancy[size] = b; occupancy[size] = b;
reference[size] = sliding_attack(pt, s, b); reference[size] = sliding_attack(pt, s, b);
+136 -215
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,19 +19,17 @@
#ifndef BITBOARD_H_INCLUDED #ifndef BITBOARD_H_INCLUDED
#define BITBOARD_H_INCLUDED #define BITBOARD_H_INCLUDED
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <string> #include <string>
#include "types.h" #include "types.h"
namespace Stockfish { namespace Stockfish {
namespace Bitbases {
void init();
bool probe(Square wksq, Square wpsq, Square bksq, Color us);
} // namespace Stockfish::Bitbases
namespace Bitboards { namespace Bitboards {
void init(); void init();
@@ -39,9 +37,6 @@ std::string pretty(Bitboard b);
} // namespace Stockfish::Bitboards } // namespace Stockfish::Bitboards
constexpr Bitboard AllSquares = ~Bitboard(0);
constexpr Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
constexpr Bitboard FileABB = 0x0101010101010101ULL; constexpr Bitboard FileABB = 0x0101010101010101ULL;
constexpr Bitboard FileBBB = FileABB << 1; constexpr Bitboard FileBBB = FileABB << 1;
constexpr Bitboard FileCBB = FileABB << 2; constexpr Bitboard FileCBB = FileABB << 2;
@@ -60,17 +55,6 @@ constexpr Bitboard Rank6BB = Rank1BB << (8 * 5);
constexpr Bitboard Rank7BB = Rank1BB << (8 * 6); constexpr Bitboard Rank7BB = Rank1BB << (8 * 6);
constexpr Bitboard Rank8BB = Rank1BB << (8 * 7); constexpr Bitboard Rank8BB = Rank1BB << (8 * 7);
constexpr Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB;
constexpr Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB;
constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB;
constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB);
constexpr Bitboard KingFlank[FILE_NB] = {
QueenSide ^ FileDBB, QueenSide, QueenSide,
CenterFiles, CenterFiles,
KingSide, KingSide, KingSide ^ FileEBB
};
extern uint8_t PopCnt16[1 << 16]; extern uint8_t PopCnt16[1 << 16];
extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
@@ -80,7 +64,7 @@ extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
/// Magic holds all magic bitboards relevant data for a single square // Magic holds all magic bitboards relevant data for a single square
struct Magic { struct Magic {
Bitboard mask; Bitboard mask;
Bitboard magic; Bitboard magic;
@@ -105,14 +89,14 @@ struct Magic {
extern Magic RookMagics[SQUARE_NB]; extern Magic RookMagics[SQUARE_NB];
extern Magic BishopMagics[SQUARE_NB]; extern Magic BishopMagics[SQUARE_NB];
inline Bitboard square_bb(Square s) { constexpr Bitboard square_bb(Square s) {
assert(is_ok(s)); assert(is_ok(s));
return (1ULL << s); return (1ULL << s);
} }
/// Overloads of bitwise operators between a Bitboard and a Square for testing // Overloads of bitwise operators between a Bitboard and a Square for testing
/// whether a given bit is set in a bitboard, and for setting and clearing bits. // whether a given bit is set in a bitboard, and for setting and clearing bits.
inline Bitboard operator&(Bitboard b, Square s) { return b & square_bb(s); } inline Bitboard operator&(Bitboard b, Square s) { return b & square_bb(s); }
inline Bitboard operator|(Bitboard b, Square s) { return b | square_bb(s); } inline Bitboard operator|(Bitboard b, Square s) { return b | square_bb(s); }
@@ -126,52 +110,40 @@ inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; }
inline Bitboard operator|(Square s1, Square s2) { return square_bb(s1) | s2; } inline Bitboard operator|(Square s1, Square s2) { return square_bb(s1) | s2; }
constexpr bool more_than_one(Bitboard b) { constexpr bool more_than_one(Bitboard b) { return b & (b - 1); }
return b & (b - 1);
}
constexpr bool opposite_colors(Square s1, Square s2) { // rank_bb() and file_bb() return a bitboard representing all the squares on
return (s1 + rank_of(s1) + s2 + rank_of(s2)) & 1; // the given file or rank.
}
constexpr Bitboard rank_bb(Rank r) { return Rank1BB << (8 * r); }
constexpr Bitboard rank_bb(Square s) { return rank_bb(rank_of(s)); }
constexpr Bitboard file_bb(File f) { return FileABB << f; }
constexpr Bitboard file_bb(Square s) { return file_bb(file_of(s)); }
/// rank_bb() and file_bb() return a bitboard representing all the squares on // Moves a bitboard one or two steps as specified by the direction D
/// the given file or rank.
constexpr Bitboard rank_bb(Rank r) {
return Rank1BB << (8 * r);
}
constexpr Bitboard rank_bb(Square s) {
return rank_bb(rank_of(s));
}
constexpr Bitboard file_bb(File f) {
return FileABB << f;
}
constexpr Bitboard file_bb(Square s) {
return file_bb(file_of(s));
}
/// shift() moves a bitboard one or two steps as specified by the direction D
template<Direction D> template<Direction D>
constexpr Bitboard shift(Bitboard b) { constexpr Bitboard shift(Bitboard b) {
return D == NORTH ? b << 8 : D == SOUTH ? b >> 8 return D == NORTH ? b << 8
: D == NORTH+NORTH? b <<16 : D == SOUTH+SOUTH? b >>16 : D == SOUTH ? b >> 8
: D == EAST ? (b & ~FileHBB) << 1 : D == WEST ? (b & ~FileABB) >> 1 : D == NORTH + NORTH ? b << 16
: D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == NORTH_WEST ? (b & ~FileABB) << 7 : D == SOUTH + SOUTH ? b >> 16
: D == SOUTH_EAST ? (b & ~FileHBB) >> 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9 : D == EAST ? (b & ~FileHBB) << 1
: D == WEST ? (b & ~FileABB) >> 1
: D == NORTH_EAST ? (b & ~FileHBB) << 9
: D == NORTH_WEST ? (b & ~FileABB) << 7
: D == SOUTH_EAST ? (b & ~FileHBB) >> 7
: D == SOUTH_WEST ? (b & ~FileABB) >> 9
: 0; : 0;
} }
/// pawn_attacks_bb() returns the squares attacked by pawns of the given color // Returns the squares attacked by pawns of the given color
/// from the squares in the given bitboard. // from the squares in the given bitboard.
template<Color C> template<Color C>
constexpr Bitboard pawn_attacks_bb(Bitboard b) { constexpr Bitboard pawn_attacks_bb(Bitboard b) {
return C == WHITE ? shift<NORTH_WEST>(b) | shift<NORTH_EAST>(b) return C == WHITE ? shift<NORTH_WEST>(b) | shift<NORTH_EAST>(b)
@@ -184,125 +156,71 @@ inline Bitboard pawn_attacks_bb(Color c, Square s) {
return PawnAttacks[c][s]; return PawnAttacks[c][s];
} }
// Returns a bitboard representing an entire line (from board edge
/// pawn_double_attacks_bb() returns the squares doubly attacked by pawns of the // to board edge) that intersects the two given squares. If the given squares
/// given color from the squares in the given bitboard. // are not on a same file/rank/diagonal, the function returns 0. For instance,
// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal.
template<Color C>
constexpr Bitboard pawn_double_attacks_bb(Bitboard b) {
return C == WHITE ? shift<NORTH_WEST>(b) & shift<NORTH_EAST>(b)
: shift<SOUTH_WEST>(b) & shift<SOUTH_EAST>(b);
}
/// adjacent_files_bb() returns a bitboard representing all the squares on the
/// adjacent files of a given square.
constexpr Bitboard adjacent_files_bb(Square s) {
return shift<EAST>(file_bb(s)) | shift<WEST>(file_bb(s));
}
/// line_bb() returns a bitboard representing an entire line (from board edge
/// to board edge) that intersects the two given squares. If the given squares
/// are not on a same file/rank/diagonal, the function returns 0. For instance,
/// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal.
inline Bitboard line_bb(Square s1, Square s2) { inline Bitboard line_bb(Square s1, Square s2) {
assert(is_ok(s1) && is_ok(s2)); assert(is_ok(s1) && is_ok(s2));
return LineBB[s1][s2]; return LineBB[s1][s2];
} }
/// between_bb(s1, s2) returns a bitboard representing the squares in the semi-open // Returns a bitboard representing the squares in the semi-open
/// segment between the squares s1 and s2 (excluding s1 but including s2). If the // 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, // 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_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 // 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 // allows to generate non-king evasion moves faster: the defending piece must either
/// interpose itself to cover the check or capture the checking piece. // interpose itself to cover the check or capture the checking piece.
inline Bitboard between_bb(Square s1, Square s2) { inline Bitboard between_bb(Square s1, Square s2) {
assert(is_ok(s1) && is_ok(s2)); assert(is_ok(s1) && is_ok(s2));
return BetweenBB[s1][s2]; return BetweenBB[s1][s2];
} }
// Returns true if the squares s1, s2 and s3 are aligned either on a
// straight or on a diagonal line.
inline bool aligned(Square s1, Square s2, Square s3) { return line_bb(s1, s2) & s3; }
/// 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) { // distance() functions return the distance between x and y, defined as the
return c == WHITE ? ~Rank1BB << 8 * relative_rank(WHITE, s) // number of steps for a king in x to reach y.
: ~Rank8BB >> 8 * relative_rank(BLACK, s);
template<typename T1 = Square>
inline int distance(Square x, Square y);
template<>
inline int distance<File>(Square x, Square y) {
return std::abs(file_of(x) - file_of(y));
} }
template<>
/// forward_file_bb() returns a bitboard representing all the squares along the inline int distance<Rank>(Square x, Square y) {
/// line in front of the given one, from the point of view of the given color. return std::abs(rank_of(x) - rank_of(y));
constexpr Bitboard forward_file_bb(Color c, Square s) {
return forward_ranks_bb(c, s) & file_bb(s);
} }
template<>
/// pawn_attack_span() returns a bitboard representing all the squares that can inline int distance<Square>(Square x, Square y) {
/// be attacked by a pawn of the given color when it moves along its file, starting return SquareDistance[x][y];
/// from the given square.
constexpr Bitboard pawn_attack_span(Color c, Square s) {
return forward_ranks_bb(c, s) & adjacent_files_bb(s);
} }
/// passed_pawn_span() returns a bitboard which can be used to test if a pawn of
/// the given color and on the given square is a passed pawn.
constexpr Bitboard passed_pawn_span(Color c, Square s) {
return pawn_attack_span(c, s) | forward_file_bb(c, s);
}
/// aligned() returns true if the squares s1, s2 and s3 are aligned either on a
/// straight or on a diagonal line.
inline bool aligned(Square s1, Square s2, Square s3) {
return line_bb(s1, s2) & s3;
}
/// distance() functions return the distance between x and y, defined as the
/// number of steps for a king in x to reach y.
template<typename T1 = Square> inline int distance(Square x, Square y);
template<> inline int distance<File>(Square x, Square y) { return std::abs(file_of(x) - file_of(y)); }
template<> inline int distance<Rank>(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); }
template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; }
inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); } inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); }
inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); }
/// attacks_bb(Square) returns the pseudo attacks of the give piece type
/// assuming an empty board.
// Returns the pseudo attacks of the given piece type
// assuming an empty board.
template<PieceType Pt> template<PieceType Pt>
inline Bitboard attacks_bb(Square s) { inline Bitboard attacks_bb(Square s) {
assert((Pt != PAWN) && (is_ok(s))); assert((Pt != PAWN) && (is_ok(s)));
return PseudoAttacks[Pt][s]; return PseudoAttacks[Pt][s];
} }
/// attacks_bb(Square, Bitboard) returns the attacks by the given piece // Returns the attacks by the given piece
/// assuming the board is occupied according to the passed Bitboard. // assuming the board is occupied according to the passed Bitboard.
/// Sliding piece attacks do not continue passed an occupied square. // Sliding piece attacks do not continue passed an occupied square.
template<PieceType Pt> template<PieceType Pt>
inline Bitboard attacks_bb(Square s, Bitboard occupied) { inline Bitboard attacks_bb(Square s, Bitboard occupied) {
@@ -310,39 +228,52 @@ inline Bitboard attacks_bb(Square s, Bitboard occupied) {
switch (Pt) switch (Pt)
{ {
case BISHOP: return BishopMagics[s].attacks[BishopMagics[s].index(occupied)]; case BISHOP :
case ROOK : return RookMagics[s].attacks[ RookMagics[s].index(occupied)]; return BishopMagics[s].attacks[BishopMagics[s].index(occupied)];
case QUEEN : return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied); case ROOK :
default : return PseudoAttacks[Pt][s]; return RookMagics[s].attacks[RookMagics[s].index(occupied)];
case QUEEN :
return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
default :
return PseudoAttacks[Pt][s];
} }
} }
// Returns the attacks by the given piece
// assuming the board is occupied according to the passed Bitboard.
// Sliding piece attacks do not continue passed an occupied square.
inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) { inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
assert((pt != PAWN) && (is_ok(s))); assert((pt != PAWN) && (is_ok(s)));
switch (pt) switch (pt)
{ {
case BISHOP: return attacks_bb<BISHOP>(s, occupied); case BISHOP :
case ROOK : return attacks_bb< ROOK>(s, occupied); return attacks_bb<BISHOP>(s, occupied);
case QUEEN : return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied); case ROOK :
default : return PseudoAttacks[pt][s]; return attacks_bb<ROOK>(s, occupied);
case QUEEN :
return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
default :
return PseudoAttacks[pt][s];
} }
} }
/// popcount() counts the number of non-zero bits in a bitboard // Counts the number of non-zero bits in a bitboard.
inline int popcount(Bitboard b) { inline int popcount(Bitboard b) {
#ifndef USE_POPCNT #ifndef USE_POPCNT
union { Bitboard bb; uint16_t u[4]; } v = { b }; union {
Bitboard bb;
uint16_t u[4];
} v = {b};
return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]]; return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]];
#elif defined(_MSC_VER) || defined(__INTEL_COMPILER) #elif defined(_MSC_VER)
return (int)_mm_popcnt_u64(b); return int(_mm_popcnt_u64(b));
#else // Assumed gcc or compatible compiler #else // Assumed gcc or compatible compiler
@@ -351,85 +282,83 @@ inline int popcount(Bitboard b) {
#endif #endif
} }
// Returns the least significant bit in a non-zero bitboard.
/// lsb() and msb() return the least/most significant bit in a non-zero bitboard
#if defined(__GNUC__) // GCC, Clang, ICC
inline Square lsb(Bitboard b) { inline Square lsb(Bitboard b) {
assert(b); assert(b);
#if defined(__GNUC__) // GCC, Clang, ICX
return Square(__builtin_ctzll(b)); return Square(__builtin_ctzll(b));
}
inline Square msb(Bitboard b) {
assert(b);
return Square(63 ^ __builtin_clzll(b));
}
#elif defined(_MSC_VER) // MSVC
#elif defined(_MSC_VER)
#ifdef _WIN64 // MSVC, WIN64 #ifdef _WIN64 // MSVC, WIN64
inline Square lsb(Bitboard b) {
assert(b);
unsigned long idx; unsigned long idx;
_BitScanForward64(&idx, b); _BitScanForward64(&idx, b);
return (Square) idx; return Square(idx);
}
inline Square msb(Bitboard b) {
assert(b);
unsigned long idx;
_BitScanReverse64(&idx, b);
return (Square) idx;
}
#else // MSVC, WIN32 #else // MSVC, WIN32
inline Square lsb(Bitboard b) {
assert(b);
unsigned long idx; unsigned long idx;
if (b & 0xffffffff) { if (b & 0xffffffff)
{
_BitScanForward(&idx, int32_t(b)); _BitScanForward(&idx, int32_t(b));
return Square(idx); return Square(idx);
} else { }
else
{
_BitScanForward(&idx, int32_t(b >> 32)); _BitScanForward(&idx, int32_t(b >> 32));
return Square(idx + 32); return Square(idx + 32);
} }
#endif
#else // Compiler is neither GCC nor MSVC compatible
#error "Compiler not supported."
#endif
} }
// Returns the most significant bit in a non-zero bitboard.
inline Square msb(Bitboard b) { inline Square msb(Bitboard b) {
assert(b); assert(b);
#if defined(__GNUC__) // GCC, Clang, ICX
return Square(63 ^ __builtin_clzll(b));
#elif defined(_MSC_VER)
#ifdef _WIN64 // MSVC, WIN64
unsigned long idx;
_BitScanReverse64(&idx, b);
return Square(idx);
#else // MSVC, WIN32
unsigned long idx; unsigned long idx;
if (b >> 32) { if (b >> 32)
{
_BitScanReverse(&idx, int32_t(b >> 32)); _BitScanReverse(&idx, int32_t(b >> 32));
return Square(idx + 32); return Square(idx + 32);
} else { }
else
{
_BitScanReverse(&idx, int32_t(b)); _BitScanReverse(&idx, int32_t(b));
return Square(idx); return Square(idx);
} }
#endif
#else // Compiler is neither GCC nor MSVC compatible
#error "Compiler not supported."
#endif
} }
#endif // Returns the bitboard of the least significant
// square of a non-zero bitboard. It is equivalent to square_bb(lsb(bb)).
#else // Compiler is neither GCC nor MSVC compatible
#error "Compiler not supported."
#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) { inline Bitboard least_significant_square_bb(Bitboard b) {
assert(b); assert(b);
return b & -b; return b & -b;
} }
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard // Finds and clears the least significant bit in a non-zero bitboard.
inline Square pop_lsb(Bitboard& b) { inline Square pop_lsb(Bitboard& b) {
assert(b); assert(b);
const Square s = lsb(b); const Square s = lsb(b);
@@ -437,14 +366,6 @@ inline Square pop_lsb(Bitboard& b) {
return s; return s;
} }
/// frontmost_sq() returns the most advanced square for the given color,
/// requires a non-zero bitboard.
inline Square frontmost_sq(Color c, Bitboard b) {
assert(b);
return c == WHITE ? msb(b) : lsb(b);
}
} // namespace Stockfish } // namespace Stockfish
#endif // #ifndef BITBOARD_H_INCLUDED #endif // #ifndef BITBOARD_H_INCLUDED
+480
View File
@@ -0,0 +1,480 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_MPI
#include <array>
#include <cstddef>
#include <cstdlib>
#include <iostream>
#include <istream>
#include <map>
#include <mpi.h>
#include <string>
#include <vector>
#include "cluster.h"
#include "thread.h"
#include "timeman.h"
#include "tt.h"
#include "search.h"
namespace Stockfish {
namespace Cluster {
// Total number of ranks and rank within the communicator
static int world_rank = MPI_PROC_NULL;
static int world_size = 0;
// Signals between ranks exchange basic info using a dedicated communicator
static MPI_Comm signalsComm = MPI_COMM_NULL;
static MPI_Request reqSignals = MPI_REQUEST_NULL;
static uint64_t signalsCallCounter = 0;
// Signals are the number of nodes searched, stop, table base hits, transposition table saves
enum Signals : int {
SIG_NODES = 0,
SIG_STOP = 1,
SIG_TB = 2,
SIG_TTS = 3,
SIG_NB = 4
};
static uint64_t signalsSend[SIG_NB] = {};
static uint64_t signalsRecv[SIG_NB] = {};
static uint64_t nodesSearchedOthers = 0;
static uint64_t tbHitsOthers = 0;
static uint64_t TTsavesOthers = 0;
static uint64_t stopSignalsPosted = 0;
// The UCI threads of each rank exchange use a dedicated communicator
static MPI_Comm InputComm = MPI_COMM_NULL;
// bestMove requires MoveInfo communicators and data types
static MPI_Comm MoveComm = MPI_COMM_NULL;
static MPI_Datatype MIDatatype = MPI_DATATYPE_NULL;
// TT entries are communicated with a dedicated communicator.
// The receive buffer is used to gather information from all ranks.
// THe TTCacheCounter tracks the number of local elements that are ready to be sent.
static MPI_Comm TTComm = MPI_COMM_NULL;
static std::array<std::vector<KeyedTTEntry>, 2> TTSendRecvBuffs;
static std::array<MPI_Request, 2> reqsTTSendRecv = {MPI_REQUEST_NULL, MPI_REQUEST_NULL};
static uint64_t sendRecvPosted = 0;
static std::atomic<uint64_t> TTCacheCounter = {};
/// Initialize MPI and associated data types. Note that the MPI library must be configured
/// to support MPI_THREAD_MULTIPLE, since multiple threads access MPI simultaneously.
void init() {
int thread_support;
MPI_Init_thread(nullptr, nullptr, MPI_THREAD_MULTIPLE, &thread_support);
if (thread_support < MPI_THREAD_MULTIPLE)
{
std::cerr << "Stockfish requires support for MPI_THREAD_MULTIPLE." << std::endl;
std::exit(EXIT_FAILURE);
}
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
const std::array<MPI_Aint, 5> MIdisps = {offsetof(MoveInfo, move), offsetof(MoveInfo, ponder),
offsetof(MoveInfo, depth), offsetof(MoveInfo, score),
offsetof(MoveInfo, rank)};
MPI_Type_create_hindexed_block(5, 1, MIdisps.data(), MPI_INT, &MIDatatype);
MPI_Type_commit(&MIDatatype);
MPI_Comm_dup(MPI_COMM_WORLD, &InputComm);
MPI_Comm_dup(MPI_COMM_WORLD, &TTComm);
MPI_Comm_dup(MPI_COMM_WORLD, &MoveComm);
MPI_Comm_dup(MPI_COMM_WORLD, &signalsComm);
}
/// Finalize MPI and free the associated data types.
void finalize() {
MPI_Type_free(&MIDatatype);
MPI_Comm_free(&InputComm);
MPI_Comm_free(&TTComm);
MPI_Comm_free(&MoveComm);
MPI_Comm_free(&signalsComm);
MPI_Finalize();
}
/// Return the total number of ranks
int size() { return world_size; }
/// Return the rank (index) of the process
int rank() { return world_rank; }
/// The receive buffer depends on the number of MPI ranks and threads, resize as needed
void ttSendRecvBuff_resize(size_t nThreads) {
for (int i : {0, 1})
{
TTSendRecvBuffs[i].resize(TTCacheSize * world_size * nThreads);
std::fill(TTSendRecvBuffs[i].begin(), TTSendRecvBuffs[i].end(), KeyedTTEntry());
}
}
/// As input is only received by the root (rank 0) of the cluster, this input must be relayed
/// to the UCI threads of all ranks, in order to setup the position, etc. We do this with a
/// dedicated getline implementation, where the root broadcasts to all other ranks the received
/// information.
bool getline(std::istream& input, std::string& str) {
int size;
std::vector<char> vec;
int state;
if (is_root())
{
state = static_cast<bool>(std::getline(input, str));
vec.assign(str.begin(), str.end());
size = vec.size();
}
// Some MPI implementations use busy-wait polling, while we need yielding as otherwise
// the UCI thread on the non-root ranks would be consuming resources.
static MPI_Request reqInput = MPI_REQUEST_NULL;
MPI_Ibcast(&size, 1, MPI_INT, 0, InputComm, &reqInput);
if (is_root())
MPI_Wait(&reqInput, MPI_STATUS_IGNORE);
else
{
while (true)
{
int flag;
MPI_Test(&reqInput, &flag, MPI_STATUS_IGNORE);
if (flag)
break;
else
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
// Broadcast received string
if (!is_root())
vec.resize(size);
MPI_Bcast(vec.data(), size, MPI_CHAR, 0, InputComm);
if (!is_root())
str.assign(vec.begin(), vec.end());
MPI_Bcast(&state, 1, MPI_INT, 0, InputComm);
return state;
}
/// Sending part of the signal communication loop
namespace {
void signals_send(const ThreadPool& threads) {
signalsSend[SIG_NODES] = threads.nodes_searched();
signalsSend[SIG_TB] = threads.tb_hits();
signalsSend[SIG_TTS] = threads.TT_saves();
signalsSend[SIG_STOP] = threads.stop;
MPI_Iallreduce(signalsSend, signalsRecv, SIG_NB, MPI_UINT64_T, MPI_SUM, signalsComm,
&reqSignals);
++signalsCallCounter;
}
/// Processing part of the signal communication loop.
/// For some counters (e.g. nodes) we only keep their sum on the other nodes
/// allowing to add local counters at any time for more fine grained process,
/// which is useful to indicate progress during early iterations, and to have
/// node counts that exactly match the non-MPI code in the single rank case.
/// This call also propagates the stop signal between ranks.
void signals_process(ThreadPool& threads) {
nodesSearchedOthers = signalsRecv[SIG_NODES] - signalsSend[SIG_NODES];
tbHitsOthers = signalsRecv[SIG_TB] - signalsSend[SIG_TB];
TTsavesOthers = signalsRecv[SIG_TTS] - signalsSend[SIG_TTS];
stopSignalsPosted = signalsRecv[SIG_STOP];
if (signalsRecv[SIG_STOP] > 0)
threads.stop = true;
}
void sendrecv_post() {
++sendRecvPosted;
MPI_Irecv(TTSendRecvBuffs[sendRecvPosted % 2].data(),
TTSendRecvBuffs[sendRecvPosted % 2].size() * sizeof(KeyedTTEntry), MPI_BYTE,
(rank() + size() - 1) % size(), 42, TTComm, &reqsTTSendRecv[0]);
MPI_Isend(TTSendRecvBuffs[(sendRecvPosted + 1) % 2].data(),
TTSendRecvBuffs[(sendRecvPosted + 1) % 2].size() * sizeof(KeyedTTEntry), MPI_BYTE,
(rank() + 1) % size(), 42, TTComm, &reqsTTSendRecv[1]);
}
}
/// During search, most message passing is asynchronous, but at the end of
/// search it makes sense to bring them to a common, finalized state.
void signals_sync(ThreadPool& threads) {
while (stopSignalsPosted < uint64_t(size()))
signals_poll(threads);
// Finalize outstanding messages of the signal loops.
// We might have issued one call less than needed on some ranks.
uint64_t globalCounter;
MPI_Allreduce(&signalsCallCounter, &globalCounter, 1, MPI_UINT64_T, MPI_MAX, MoveComm);
if (signalsCallCounter < globalCounter)
{
MPI_Wait(&reqSignals, MPI_STATUS_IGNORE);
signals_send(threads);
}
assert(signalsCallCounter == globalCounter);
MPI_Wait(&reqSignals, MPI_STATUS_IGNORE);
signals_process(threads);
// Finalize outstanding messages in the sendRecv loop
MPI_Allreduce(&sendRecvPosted, &globalCounter, 1, MPI_UINT64_T, MPI_MAX, MoveComm);
while (sendRecvPosted < globalCounter)
{
MPI_Waitall(reqsTTSendRecv.size(), reqsTTSendRecv.data(), MPI_STATUSES_IGNORE);
sendrecv_post();
}
assert(sendRecvPosted == globalCounter);
MPI_Waitall(reqsTTSendRecv.size(), reqsTTSendRecv.data(), MPI_STATUSES_IGNORE);
}
/// Initialize signal counters to zero.
void signals_init() {
stopSignalsPosted = tbHitsOthers = TTsavesOthers = nodesSearchedOthers = 0;
signalsSend[SIG_NODES] = signalsRecv[SIG_NODES] = 0;
signalsSend[SIG_TB] = signalsRecv[SIG_TB] = 0;
signalsSend[SIG_TTS] = signalsRecv[SIG_TTS] = 0;
signalsSend[SIG_STOP] = signalsRecv[SIG_STOP] = 0;
}
/// Poll the signal loop, and start next round as needed.
void signals_poll(ThreadPool& threads) {
int flag;
MPI_Test(&reqSignals, &flag, MPI_STATUS_IGNORE);
if (flag)
{
signals_process(threads);
signals_send(threads);
}
}
/// Provide basic info related the cluster performance, in particular, the number of signals send,
/// signals per sounds (sps), the number of gathers, the number of positions gathered (per node and per second, gpps)
/// The number of TT saves and TT saves per second. If gpps equals approximately TTSavesps the gather loop has enough bandwidth.
void cluster_info(const ThreadPool& threads, Depth depth, TimePoint elapsed) {
// TimePoint elapsed = Time.elapsed() + 1;
uint64_t TTSaves = TT_saves(threads);
sync_cout << "info depth " << depth << " cluster "
<< " signals " << signalsCallCounter << " sps " << signalsCallCounter * 1000 / elapsed
<< " sendRecvs " << sendRecvPosted << " srpps "
<< TTSendRecvBuffs[0].size() * sendRecvPosted * 1000 / elapsed << " TTSaves "
<< TTSaves << " TTSavesps " << TTSaves * 1000 / elapsed << sync_endl;
}
/// When a TT entry is saved, additional steps are taken if the entry is of sufficient depth.
/// If sufficient entries has been collected, a communication is initiated.
/// If a communication has been completed, the received results are saved to the TT.
void save(TranspositionTable& TT,
ThreadPool& threads,
Search::Worker* thread,
TTEntry* tte,
Key k,
Value v,
bool PvHit,
Bound b,
Depth d,
Move m,
Value ev,
uint8_t generation8) {
// Standard save to the TT
tte->save(k, v, PvHit, b, d, m, ev, generation8);
// If the entry is of sufficient depth to be worth communicating, take action.
if (d > 3)
{
// count the TTsaves to information: this should be relatively similar
// to the number of entries we can send/recv.
thread->TTsaves.fetch_add(1, std::memory_order_relaxed);
// Add to thread's send buffer, the locking here avoids races when the master thread
// prepares the send buffer.
{
std::lock_guard<std::mutex> lk(thread->ttCache.mutex);
thread->ttCache.buffer.replace(KeyedTTEntry(k, *tte));
++TTCacheCounter;
}
size_t recvBuffPerRankSize = threads.size() * TTCacheSize;
// Communicate on main search thread, as soon the threads combined have collected
// sufficient data to fill the send buffers.
if (thread == threads.main_thread()->worker.get() && TTCacheCounter > recvBuffPerRankSize)
{
// Test communication status
int flag;
MPI_Testall(reqsTTSendRecv.size(), reqsTTSendRecv.data(), &flag, MPI_STATUSES_IGNORE);
// Current communication is complete
if (flag)
{
// Save all received entries to TT, and store our TTCaches, ready for the next round of communication
for (size_t irank = 0; irank < size_t(size()); ++irank)
{
if (irank
== size_t(
rank())) // this is our part, fill the part of the buffer for sending
{
// Copy from the thread caches to the right spot in the buffer
size_t i = irank * recvBuffPerRankSize;
for (auto&& th : threads)
{
std::lock_guard<std::mutex> lk(th->worker->ttCache.mutex);
for (auto&& e : th->worker->ttCache.buffer)
TTSendRecvBuffs[sendRecvPosted % 2][i++] = e;
// Reset thread's send buffer
th->worker->ttCache.buffer = {};
}
TTCacheCounter = 0;
}
else // process data received from the corresponding rank.
for (size_t i = irank * recvBuffPerRankSize;
i < (irank + 1) * recvBuffPerRankSize; ++i)
{
auto&& e = TTSendRecvBuffs[sendRecvPosted % 2][i];
bool found;
TTEntry* replace_tte;
replace_tte = TT.probe(e.first, found);
replace_tte->save(e.first, e.second.value(), e.second.is_pv(),
e.second.bound(), e.second.depth(), e.second.move(),
e.second.eval(), TT.generation());
}
}
// Start next communication
sendrecv_post();
// Force check of time on the next occasion, the above actions might have taken some time.
thread->main_manager()->callsCnt = 0;
}
}
}
}
/// Picks the bestMove across ranks, and send the associated info and PV to the root of the cluster.
/// Note that this bestMove and PV must be output by the root, the guarantee proper ordering of output.
/// TODO update to the scheme in master.. can this use aggregation of votes?
void pick_moves(MoveInfo& mi, std::string& PVLine) {
MoveInfo* pMoveInfo = NULL;
if (is_root())
{
pMoveInfo = (MoveInfo*) malloc(sizeof(MoveInfo) * size());
}
MPI_Gather(&mi, 1, MIDatatype, pMoveInfo, 1, MIDatatype, 0, MoveComm);
if (is_root())
{
std::map<int, int> votes;
int minScore = pMoveInfo[0].score;
for (int i = 0; i < size(); ++i)
{
minScore = std::min(minScore, pMoveInfo[i].score);
votes[pMoveInfo[i].move] = 0;
}
for (int i = 0; i < size(); ++i)
{
votes[pMoveInfo[i].move] += pMoveInfo[i].score - minScore + pMoveInfo[i].depth;
}
int bestVote = votes[pMoveInfo[0].move];
for (int i = 0; i < size(); ++i)
{
if (votes[pMoveInfo[i].move] > bestVote)
{
bestVote = votes[pMoveInfo[i].move];
mi = pMoveInfo[i];
}
}
free(pMoveInfo);
}
// Send around the final result
MPI_Bcast(&mi, 1, MIDatatype, 0, MoveComm);
// Send PV line to root as needed
if (mi.rank != 0 && mi.rank == rank())
{
int size;
std::vector<char> vec;
vec.assign(PVLine.begin(), PVLine.end());
size = vec.size();
MPI_Send(&size, 1, MPI_INT, 0, 42, MoveComm);
MPI_Send(vec.data(), size, MPI_CHAR, 0, 42, MoveComm);
}
if (mi.rank != 0 && is_root())
{
int size;
std::vector<char> vec;
MPI_Recv(&size, 1, MPI_INT, mi.rank, 42, MoveComm, MPI_STATUS_IGNORE);
vec.resize(size);
MPI_Recv(vec.data(), size, MPI_CHAR, mi.rank, 42, MoveComm, MPI_STATUS_IGNORE);
PVLine.assign(vec.begin(), vec.end());
}
}
/// Return nodes searched (lazily updated cluster wide in the signal loop)
uint64_t nodes_searched(const ThreadPool& threads) {
return nodesSearchedOthers + threads.nodes_searched();
}
/// Return table base hits (lazily updated cluster wide in the signal loop)
uint64_t tb_hits(const ThreadPool& threads) { return tbHitsOthers + threads.tb_hits(); }
/// Return the number of saves to the TT buffers, (lazily updated cluster wide in the signal loop)
uint64_t TT_saves(const ThreadPool& threads) { return TTsavesOthers + threads.TT_saves(); }
}
}
#else
#include "cluster.h"
#include "thread.h"
namespace Stockfish {
namespace Cluster {
uint64_t nodes_searched(const ThreadPool& threads) { return threads.nodes_searched(); }
uint64_t tb_hits(const ThreadPool& threads) { return threads.tb_hits(); }
uint64_t TT_saves(const ThreadPool& threads) { return threads.TT_saves(); }
}
}
#endif // USE_MPI
+157
View File
@@ -0,0 +1,157 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CLUSTER_H_INCLUDED
#define CLUSTER_H_INCLUDED
#include <algorithm>
#include <array>
#include <istream>
#include <string>
#include "tt.h"
namespace Stockfish {
class Thread;
class ThreadPool;
namespace Search {
class Worker;
}
/// The Cluster namespace contains functionality required to run on distributed
/// memory architectures using MPI as the message passing interface. On a high level,
/// a 'lazy SMP'-like scheme is implemented where TT saves of sufficient depth are
/// collected on each rank and distributed to, and used by, all other ranks,
/// which search essentially independently. The root (MPI rank 0) of the cluster
/// is responsible for all I/O and time management, communicating this info to
/// the other ranks as needed. UCI options such as Threads and Hash specify these
/// quantities per MPI rank. It is recommended to have one rank (MPI process) per node.
/// For the non-MPI case, wrappers that will be compiler-optimized away are provided.
namespace Cluster {
/// Basic info to find the cluster-wide bestMove
struct MoveInfo {
int move;
int ponder;
int depth;
int score;
int rank;
};
#ifdef USE_MPI
// store the TTEntry with its full key, so it can be saved on the receiver side
using KeyedTTEntry = std::pair<Key, TTEntry>;
constexpr std::size_t TTCacheSize = 16;
// Threads locally cache their high-depth TT entries till a batch can be send by MPI
template<std::size_t N>
class TTCache: public std::array<KeyedTTEntry, N> {
struct Compare {
inline bool operator()(const KeyedTTEntry& lhs, const KeyedTTEntry& rhs) {
return lhs.second.depth() > rhs.second.depth();
}
};
Compare compare;
public:
// Keep a heap of entries replacing low depth with high depth entries
bool replace(const KeyedTTEntry& value) {
if (compare(value, this->front()))
{
std::pop_heap(this->begin(), this->end(), compare);
this->back() = value;
std::push_heap(this->begin(), this->end(), compare);
return true;
}
return false;
}
};
void init();
void finalize();
bool getline(std::istream& input, std::string& str);
int size();
int rank();
inline bool is_root() { return rank() == 0; }
void save(TranspositionTable&,
ThreadPool&,
Search::Worker* thread,
TTEntry* tte,
Key k,
Value v,
bool PvHit,
Bound b,
Depth d,
Move m,
Value ev,
uint8_t generation8);
void pick_moves(MoveInfo& mi, std::string& PVLine);
void ttSendRecvBuff_resize(size_t nThreads);
uint64_t nodes_searched(const ThreadPool&);
uint64_t tb_hits(const ThreadPool&);
uint64_t TT_saves(const ThreadPool&);
void cluster_info(const ThreadPool&, Depth depth, TimePoint elapsed);
void signals_init();
void signals_poll(ThreadPool& threads);
void signals_sync(ThreadPool& threads);
#else
inline void init() {}
inline void finalize() {}
inline bool getline(std::istream& input, std::string& str) {
return static_cast<bool>(std::getline(input, str));
}
constexpr int size() { return 1; }
constexpr int rank() { return 0; }
constexpr bool is_root() { return true; }
inline void save(TranspositionTable&,
ThreadPool&,
Search::Worker*,
TTEntry* tte,
Key k,
Value v,
bool PvHit,
Bound b,
Depth d,
Move m,
Value ev,
uint8_t generation8) {
tte->save(k, v, PvHit, b, d, m, ev, generation8);
}
inline void pick_moves(MoveInfo&, std::string&) {}
inline void ttSendRecvBuff_resize(size_t) {}
uint64_t nodes_searched(const ThreadPool&);
uint64_t tb_hits(const ThreadPool&);
uint64_t TT_saves(const ThreadPool&);
inline void cluster_info(const ThreadPool&, Depth, TimePoint) {}
inline void signals_init() {}
inline void signals_poll(ThreadPool& threads) {}
inline void signals_sync(ThreadPool& threads) {}
#endif /* USE_MPI */
}
}
#endif // #ifndef CLUSTER_H_INCLUDED
-747
View File
@@ -1,747 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include "bitboard.h"
#include "endgame.h"
#include "movegen.h"
namespace Stockfish {
namespace {
// Used to drive the king towards the edge of the board
// in KX vs K and KQ vs KR endgames.
// Values range from 27 (center squares) to 90 (in the corners)
inline int push_to_edge(Square s) {
int rd = edge_distance(rank_of(s)), fd = edge_distance(file_of(s));
return 90 - (7 * fd * fd / 2 + 7 * rd * rd / 2);
}
// Used to drive the king towards A1H8 corners in KBN vs K endgames.
// Values range from 0 on A8H1 diagonal to 7 in A1H8 corners
inline int push_to_corner(Square s) {
return abs(7 - rank_of(s) - file_of(s));
}
// Drive a piece close to or away from another piece
inline int push_close(Square s1, Square s2) { return 140 - 20 * distance(s1, s2); }
inline int push_away(Square s1, Square s2) { return 120 - push_close(s1, s2); }
#ifndef NDEBUG
bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) {
return pos.non_pawn_material(c) == npm && pos.count<PAWN>(c) == pawnsCnt;
}
#endif
// Map the square as if strongSide is white and strongSide's only pawn
// is on the left half of the board.
Square normalize(const Position& pos, Color strongSide, Square sq) {
assert(pos.count<PAWN>(strongSide) == 1);
if (file_of(pos.square<PAWN>(strongSide)) >= FILE_E)
sq = flip_file(sq);
return strongSide == WHITE ? sq : flip_rank(sq);
}
} // namespace
namespace Endgames {
std::pair<Map<Value>, Map<ScaleFactor>> maps;
void init() {
add<KPK>("KPK");
add<KNNK>("KNNK");
add<KBNK>("KBNK");
add<KRKP>("KRKP");
add<KRKB>("KRKB");
add<KRKN>("KRKN");
add<KQKP>("KQKP");
add<KQKR>("KQKR");
add<KNNKP>("KNNKP");
add<KRPKR>("KRPKR");
add<KRPKB>("KRPKB");
add<KBPKB>("KBPKB");
add<KBPKN>("KBPKN");
add<KBPPKB>("KBPPKB");
add<KRPPKRP>("KRPPKRP");
}
}
/// Mate with KX vs K. This function is used to evaluate positions with
/// king and plenty of material vs a lone king. It simply gives the
/// attacking side a bonus for driving the defending king towards the edge
/// of the board, and for keeping the distance between the two kings small.
template<>
Value Endgame<KXK>::operator()(const Position& pos) const {
assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
assert(!pos.checkers()); // Eval is never called when in check
// Stalemate detection with lone king
if (pos.side_to_move() == weakSide && !MoveList<LEGAL>(pos).size())
return VALUE_DRAW;
Square strongKing = pos.square<KING>(strongSide);
Square weakKing = pos.square<KING>(weakSide);
Value result = pos.non_pawn_material(strongSide)
+ pos.count<PAWN>(strongSide) * PawnValueEg
+ push_to_edge(weakKing)
+ push_close(strongKing, weakKing);
if ( pos.count<QUEEN>(strongSide)
|| pos.count<ROOK>(strongSide)
||(pos.count<BISHOP>(strongSide) && pos.count<KNIGHT>(strongSide))
|| ( (pos.pieces(strongSide, BISHOP) & ~DarkSquares)
&& (pos.pieces(strongSide, BISHOP) & DarkSquares)))
result = std::min(result + VALUE_KNOWN_WIN, VALUE_TB_WIN_IN_MAX_PLY - 1);
return strongSide == pos.side_to_move() ? result : -result;
}
/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the
/// defending king towards a corner square that our bishop attacks.
template<>
Value Endgame<KBNK>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, KnightValueMg + BishopValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
Square strongKing = pos.square<KING>(strongSide);
Square strongBishop = pos.square<BISHOP>(strongSide);
Square weakKing = pos.square<KING>(weakSide);
// If our bishop does not attack A1/H8, we flip the enemy king square
// to drive to opposite corners (A8/H1).
Value result = (VALUE_KNOWN_WIN + 3520)
+ push_close(strongKing, weakKing)
+ 420 * push_to_corner(opposite_colors(strongBishop, SQ_A1) ? flip_file(weakKing) : weakKing);
assert(abs(result) < VALUE_TB_WIN_IN_MAX_PLY);
return strongSide == pos.side_to_move() ? result : -result;
}
/// KP vs K. This endgame is evaluated with the help of a bitbase
template<>
Value Endgame<KPK>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, VALUE_ZERO, 1));
assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
// Assume strongSide is white and the pawn is on files A-D
Square strongKing = normalize(pos, strongSide, pos.square<KING>(strongSide));
Square strongPawn = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Square weakKing = normalize(pos, strongSide, pos.square<KING>(weakSide));
Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
if (!Bitbases::probe(strongKing, strongPawn, weakKing, us))
return VALUE_DRAW;
Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(strongPawn));
return strongSide == pos.side_to_move() ? result : -result;
}
/// KR vs KP. This is a somewhat tricky endgame to evaluate precisely without
/// a bitbase. The function below returns drawish scores when the pawn is
/// far advanced with support of the king, while the attacking king is far
/// away.
template<>
Value Endgame<KRKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
Square strongKing = pos.square<KING>(strongSide);
Square weakKing = pos.square<KING>(weakSide);
Square strongRook = pos.square<ROOK>(strongSide);
Square weakPawn = pos.square<PAWN>(weakSide);
Square queeningSquare = make_square(file_of(weakPawn), relative_rank(weakSide, RANK_8));
Value result;
// If the stronger side's king is in front of the pawn, it's a win
if (forward_file_bb(strongSide, strongKing) & weakPawn)
result = RookValueEg - distance(strongKing, weakPawn);
// If the weaker side's king is too far from the pawn and the rook,
// it's a win.
else if ( distance(weakKing, weakPawn) >= 3 + (pos.side_to_move() == weakSide)
&& distance(weakKing, strongRook) >= 3)
result = RookValueEg - distance(strongKing, weakPawn);
// If the pawn is far advanced and supported by the defending king,
// the position is drawish
else if ( relative_rank(strongSide, weakKing) <= RANK_3
&& distance(weakKing, weakPawn) == 1
&& relative_rank(strongSide, strongKing) >= RANK_4
&& distance(strongKing, weakPawn) > 2 + (pos.side_to_move() == strongSide))
result = Value(80) - 8 * distance(strongKing, weakPawn);
else
result = Value(200) - 8 * ( distance(strongKing, weakPawn + pawn_push(weakSide))
- distance(weakKing, weakPawn + pawn_push(weakSide))
- distance(weakPawn, queeningSquare));
return strongSide == pos.side_to_move() ? result : -result;
}
/// KR vs KB. This is very simple, and always returns drawish scores. The
/// score is slightly bigger when the defending king is close to the edge.
template<>
Value Endgame<KRKB>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 0));
assert(verify_material(pos, weakSide, BishopValueMg, 0));
Value result = Value(push_to_edge(pos.square<KING>(weakSide)));
return strongSide == pos.side_to_move() ? result : -result;
}
/// KR vs KN. The attacking side has slightly better winning chances than
/// in KR vs KB, particularly if the king and the knight are far apart.
template<>
Value Endgame<KRKN>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 0));
assert(verify_material(pos, weakSide, KnightValueMg, 0));
Square weakKing = pos.square<KING>(weakSide);
Square weakKnight = pos.square<KNIGHT>(weakSide);
Value result = Value(push_to_edge(weakKing) + push_away(weakKing, weakKnight));
return strongSide == pos.side_to_move() ? result : -result;
}
/// KQ vs KP. In general, this is a win for the stronger side, but there are a
/// few important exceptions. A pawn on 7th rank and on the A,C,F or H files
/// with a king positioned next to it can be a draw, so in that case, we only
/// use the distance between the kings.
template<>
Value Endgame<KQKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, QueenValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
Square strongKing = pos.square<KING>(strongSide);
Square weakKing = pos.square<KING>(weakSide);
Square weakPawn = pos.square<PAWN>(weakSide);
Value result = Value(push_close(strongKing, weakKing));
if ( relative_rank(weakSide, weakPawn) != RANK_7
|| distance(weakKing, weakPawn) != 1
|| ((FileBBB | FileDBB | FileEBB | FileGBB) & weakPawn))
result += QueenValueEg - PawnValueEg;
return strongSide == pos.side_to_move() ? result : -result;
}
/// KQ vs KR. This is almost identical to KX vs K: we give the attacking
/// king a bonus for having the kings close together, and for forcing the
/// defending king towards the edge. If we also take care to avoid null move for
/// the defending side in the search, this is usually sufficient to win KQ vs KR.
template<>
Value Endgame<KQKR>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, QueenValueMg, 0));
assert(verify_material(pos, weakSide, RookValueMg, 0));
Square strongKing = pos.square<KING>(strongSide);
Square weakKing = pos.square<KING>(weakSide);
Value result = QueenValueEg
- RookValueEg
+ push_to_edge(weakKing)
+ push_close(strongKing, weakKing);
return strongSide == pos.side_to_move() ? result : -result;
}
/// KNN vs KP. Very drawish, but there are some mate opportunities if we can
/// press the weakSide King to a corner before the pawn advances too much.
template<>
Value Endgame<KNNKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
Square weakKing = pos.square<KING>(weakSide);
Square weakPawn = pos.square<PAWN>(weakSide);
Value result = PawnValueEg
+ 2 * push_to_edge(weakKing)
- 10 * relative_rank(weakSide, weakPawn);
return strongSide == pos.side_to_move() ? result : -result;
}
/// Some cases of trivial draws
template<> Value Endgame<KNNK>::operator()(const Position&) const { return VALUE_DRAW; }
/// KB and one or more pawns vs K. It checks for draws with rook pawns and
/// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW
/// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
/// will be used.
template<>
ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongSide) == BishopValueMg);
assert(pos.count<PAWN>(strongSide) >= 1);
// No assertions about the material of weakSide, because we want draws to
// be detected even when the weaker side has some pawns.
Bitboard strongPawns = pos.pieces(strongSide, PAWN);
Bitboard allPawns = pos.pieces(PAWN);
Square strongBishop = pos.square<BISHOP>(strongSide);
Square weakKing = pos.square<KING>(weakSide);
Square strongKing = pos.square<KING>(strongSide);
// All strongSide pawns are on a single rook file?
if (!(strongPawns & ~FileABB) || !(strongPawns & ~FileHBB))
{
Square queeningSquare = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8));
if ( opposite_colors(queeningSquare, strongBishop)
&& distance(queeningSquare, weakKing) <= 1)
return SCALE_FACTOR_DRAW;
}
// If all the pawns are on the same B or G file, then it's potentially a draw
if ((!(allPawns & ~FileBBB) || !(allPawns & ~FileGBB))
&& pos.non_pawn_material(weakSide) == 0
&& pos.count<PAWN>(weakSide) >= 1)
{
// Get the least advanced weakSide pawn
Square weakPawn = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN));
// There's potential for a draw if our pawn is blocked on the 7th rank,
// the bishop cannot attack it or they only have one pawn left.
if ( relative_rank(strongSide, weakPawn) == RANK_7
&& (strongPawns & (weakPawn + pawn_push(weakSide)))
&& (opposite_colors(strongBishop, weakPawn) || !more_than_one(strongPawns)))
{
int strongKingDist = distance(weakPawn, strongKing);
int weakKingDist = distance(weakPawn, weakKing);
// It's a draw if the weak king is on its back two ranks, within 2
// squares of the blocking pawn and the strong king is not
// closer. (I think this rule only fails in practically
// unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w
// and positions where qsearch will immediately correct the
// problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w).
if ( relative_rank(strongSide, weakKing) >= RANK_7
&& weakKingDist <= 2
&& weakKingDist <= strongKingDist)
return SCALE_FACTOR_DRAW;
}
}
return SCALE_FACTOR_NONE;
}
/// KQ vs KR and one or more pawns. It tests for fortress draws with a rook on
/// the third rank defended by a pawn.
template<>
ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, QueenValueMg, 0));
assert(pos.count<ROOK>(weakSide) == 1);
assert(pos.count<PAWN>(weakSide) >= 1);
Square strongKing = pos.square<KING>(strongSide);
Square weakKing = pos.square<KING>(weakSide);
Square weakRook = pos.square<ROOK>(weakSide);
if ( relative_rank(weakSide, weakKing) <= RANK_2
&& relative_rank(weakSide, strongKing) >= RANK_4
&& relative_rank(weakSide, weakRook) == RANK_3
&& ( pos.pieces(weakSide, PAWN)
& attacks_bb<KING>(weakKing)
& pawn_attacks_bb(strongSide, weakRook)))
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
}
/// KRP vs KR. This function knows a handful of the most important classes of
/// drawn positions, but is far from perfect. It would probably be a good idea
/// to add more knowledge in the future.
///
/// It would also be nice to rewrite the actual code for this function,
/// which is mostly copied from Glaurung 1.x, and isn't very pretty.
template<>
ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 1));
assert(verify_material(pos, weakSide, RookValueMg, 0));
// Assume strongSide is white and the pawn is on files A-D
Square strongKing = normalize(pos, strongSide, pos.square<KING>(strongSide));
Square strongRook = normalize(pos, strongSide, pos.square<ROOK>(strongSide));
Square strongPawn = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Square weakKing = normalize(pos, strongSide, pos.square<KING>(weakSide));
Square weakRook = normalize(pos, strongSide, pos.square<ROOK>(weakSide));
File pawnFile = file_of(strongPawn);
Rank pawnRank = rank_of(strongPawn);
Square queeningSquare = make_square(pawnFile, RANK_8);
int tempo = (pos.side_to_move() == strongSide);
// If the pawn is not too far advanced and the defending king defends the
// queening square, use the third-rank defence.
if ( pawnRank <= RANK_5
&& distance(weakKing, queeningSquare) <= 1
&& strongKing <= SQ_H5
&& (rank_of(weakRook) == RANK_6 || (pawnRank <= RANK_3 && rank_of(strongRook) != RANK_6)))
return SCALE_FACTOR_DRAW;
// The defending side saves a draw by checking from behind in case the pawn
// has advanced to the 6th rank with the king behind.
if ( pawnRank == RANK_6
&& distance(weakKing, queeningSquare) <= 1
&& rank_of(strongKing) + tempo <= RANK_6
&& (rank_of(weakRook) == RANK_1 || (!tempo && distance<File>(weakRook, strongPawn) >= 3)))
return SCALE_FACTOR_DRAW;
if ( pawnRank >= RANK_6
&& weakKing == queeningSquare
&& rank_of(weakRook) == RANK_1
&& (!tempo || distance(strongKing, strongPawn) >= 2))
return SCALE_FACTOR_DRAW;
// White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7
// and the black rook is behind the pawn.
if ( strongPawn == SQ_A7
&& strongRook == SQ_A8
&& (weakKing == SQ_H7 || weakKing == SQ_G7)
&& file_of(weakRook) == FILE_A
&& (rank_of(weakRook) <= RANK_3 || file_of(strongKing) >= FILE_D || rank_of(strongKing) <= RANK_5))
return SCALE_FACTOR_DRAW;
// If the defending king blocks the pawn and the attacking king is too far
// away, it's a draw.
if ( pawnRank <= RANK_5
&& weakKing == strongPawn + NORTH
&& distance(strongKing, strongPawn) - tempo >= 2
&& distance(strongKing, weakRook) - tempo >= 2)
return SCALE_FACTOR_DRAW;
// Pawn on the 7th rank supported by the rook from behind usually wins if the
// attacking king is closer to the queening square than the defending king,
// and the defending king cannot gain tempi by threatening the attacking rook.
if ( pawnRank == RANK_7
&& pawnFile != FILE_A
&& file_of(strongRook) == pawnFile
&& strongRook != queeningSquare
&& (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo)
&& (distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo))
return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(strongKing, queeningSquare));
// Similar to the above, but with the pawn further back
if ( pawnFile != FILE_A
&& file_of(strongRook) == pawnFile
&& strongRook < strongPawn
&& (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo)
&& (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn + NORTH) - 2 + tempo)
&& ( distance(weakKing, strongRook) + tempo >= 3
|| ( distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo
&& (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn) + tempo))))
return ScaleFactor( SCALE_FACTOR_MAX
- 8 * distance(strongPawn, queeningSquare)
- 2 * distance(strongKing, queeningSquare));
// If the pawn is not far advanced and the defending king is somewhere in
// the pawn's path, it's probably a draw.
if (pawnRank <= RANK_4 && weakKing > strongPawn)
{
if (file_of(weakKing) == file_of(strongPawn))
return ScaleFactor(10);
if ( distance<File>(weakKing, strongPawn) == 1
&& distance(strongKing, weakKing) > 2)
return ScaleFactor(24 - 2 * distance(strongKing, weakKing));
}
return SCALE_FACTOR_NONE;
}
template<>
ScaleFactor Endgame<KRPKB>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 1));
assert(verify_material(pos, weakSide, BishopValueMg, 0));
// Test for a rook pawn
if (pos.pieces(PAWN) & (FileABB | FileHBB))
{
Square weakKing = pos.square<KING>(weakSide);
Square weakBishop = pos.square<BISHOP>(weakSide);
Square strongKing = pos.square<KING>(strongSide);
Square strongPawn = pos.square<PAWN>(strongSide);
Rank pawnRank = relative_rank(strongSide, strongPawn);
Direction push = pawn_push(strongSide);
// If the pawn is on the 5th rank and the pawn (currently) is on
// the same color square as the bishop then there is a chance of
// a fortress. Depending on the king position give a moderate
// reduction or a stronger one if the defending king is near the
// corner but not trapped there.
if (pawnRank == RANK_5 && !opposite_colors(weakBishop, strongPawn))
{
int d = distance(strongPawn + 3 * push, weakKing);
if (d <= 2 && !(d == 0 && weakKing == strongKing + 2 * push))
return ScaleFactor(24);
else
return ScaleFactor(48);
}
// When the pawn has moved to the 6th rank we can be fairly sure
// it's drawn if the bishop attacks the square in front of the
// pawn from a reasonable distance and the defending king is near
// the corner
if ( pawnRank == RANK_6
&& distance(strongPawn + 2 * push, weakKing) <= 1
&& (attacks_bb<BISHOP>(weakBishop) & (strongPawn + push))
&& distance<File>(weakBishop, strongPawn) >= 2)
return ScaleFactor(8);
}
return SCALE_FACTOR_NONE;
}
/// KRPP vs KRP. There is just a single rule: if the stronger side has no passed
/// pawns and the defending king is actively placed, the position is drawish.
template<>
ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, RookValueMg, 2));
assert(verify_material(pos, weakSide, RookValueMg, 1));
Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN));
Square strongPawn2 = msb(pos.pieces(strongSide, PAWN));
Square weakKing = pos.square<KING>(weakSide);
// Does the stronger side have a passed pawn?
if (pos.pawn_passed(strongSide, strongPawn1) || pos.pawn_passed(strongSide, strongPawn2))
return SCALE_FACTOR_NONE;
Rank pawnRank = std::max(relative_rank(strongSide, strongPawn1), relative_rank(strongSide, strongPawn2));
if ( distance<File>(weakKing, strongPawn1) <= 1
&& distance<File>(weakKing, strongPawn2) <= 1
&& relative_rank(strongSide, weakKing) > pawnRank)
{
assert(pawnRank > RANK_1 && pawnRank < RANK_7);
return ScaleFactor(7 * pawnRank);
}
return SCALE_FACTOR_NONE;
}
/// K and two or more pawns vs K. There is just a single rule here: if all pawns
/// are on the same rook file and are blocked by the defending king, it's a draw.
template<>
ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongSide) == VALUE_ZERO);
assert(pos.count<PAWN>(strongSide) >= 2);
assert(verify_material(pos, weakSide, VALUE_ZERO, 0));
Square weakKing = pos.square<KING>(weakSide);
Bitboard strongPawns = pos.pieces(strongSide, PAWN);
// If all pawns are ahead of the king on a single rook file, it's a draw.
if ( !(strongPawns & ~(FileABB | FileHBB))
&& !(strongPawns & ~passed_pawn_span(weakSide, weakKing)))
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
}
/// KBP vs KB. There are two rules: if the defending king is somewhere along the
/// path of the pawn, and the square of the king is not of the same color as the
/// stronger side's bishop, it's a draw. If the two bishops have opposite color,
/// it's almost always a draw.
template<>
ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, BishopValueMg, 1));
assert(verify_material(pos, weakSide, BishopValueMg, 0));
Square strongPawn = pos.square<PAWN>(strongSide);
Square strongBishop = pos.square<BISHOP>(strongSide);
Square weakBishop = pos.square<BISHOP>(weakSide);
Square weakKing = pos.square<KING>(weakSide);
// Case 1: Defending king blocks the pawn, and cannot be driven away
if ( (forward_file_bb(strongSide, strongPawn) & weakKing)
&& ( opposite_colors(weakKing, strongBishop)
|| relative_rank(strongSide, weakKing) <= RANK_6))
return SCALE_FACTOR_DRAW;
// Case 2: Opposite colored bishops
if (opposite_colors(strongBishop, weakBishop))
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
}
/// KBPP vs KB. It detects a few basic draws with opposite-colored bishops
template<>
ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, BishopValueMg, 2));
assert(verify_material(pos, weakSide, BishopValueMg, 0));
Square strongBishop = pos.square<BISHOP>(strongSide);
Square weakBishop = pos.square<BISHOP>(weakSide);
if (!opposite_colors(strongBishop, weakBishop))
return SCALE_FACTOR_NONE;
Square weakKing = pos.square<KING>(weakSide);
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))
{
blockSq1 = strongPawn1 + pawn_push(strongSide);
blockSq2 = make_square(file_of(strongPawn2), rank_of(strongPawn1));
}
else
{
blockSq1 = strongPawn2 + pawn_push(strongSide);
blockSq2 = make_square(file_of(strongPawn1), rank_of(strongPawn2));
}
switch (distance<File>(strongPawn1, strongPawn2))
{
case 0:
// Both pawns are on the same file. It's an easy draw if the defender firmly
// controls some square in the frontmost pawn's path.
if ( file_of(weakKing) == file_of(blockSq1)
&& relative_rank(strongSide, weakKing) >= relative_rank(strongSide, blockSq1)
&& opposite_colors(weakKing, strongBishop))
return SCALE_FACTOR_DRAW;
else
return SCALE_FACTOR_NONE;
case 1:
// Pawns on adjacent files. It's a draw if the defender firmly controls the
// square in front of the frontmost pawn's path, and the square diagonally
// behind this square on the file of the other pawn.
if ( weakKing == blockSq1
&& opposite_colors(weakKing, strongBishop)
&& ( weakBishop == blockSq2
|| (attacks_bb<BISHOP>(blockSq2, pos.pieces()) & pos.pieces(weakSide, BISHOP))
|| distance<Rank>(strongPawn1, strongPawn2) >= 2))
return SCALE_FACTOR_DRAW;
else if ( weakKing == blockSq2
&& opposite_colors(weakKing, strongBishop)
&& ( weakBishop == blockSq1
|| (attacks_bb<BISHOP>(blockSq1, pos.pieces()) & pos.pieces(weakSide, BISHOP))))
return SCALE_FACTOR_DRAW;
else
return SCALE_FACTOR_NONE;
default:
// The pawns are not on the same file or adjacent files. No scaling.
return SCALE_FACTOR_NONE;
}
}
/// KBP vs KN. There is a single rule: if the defending king is somewhere along
/// the path of the pawn, and the square of the king is not of the same color as
/// the stronger side's bishop, it's a draw.
template<>
ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, BishopValueMg, 1));
assert(verify_material(pos, weakSide, KnightValueMg, 0));
Square strongPawn = pos.square<PAWN>(strongSide);
Square strongBishop = pos.square<BISHOP>(strongSide);
Square weakKing = pos.square<KING>(weakSide);
if ( file_of(weakKing) == file_of(strongPawn)
&& relative_rank(strongSide, strongPawn) < relative_rank(strongSide, weakKing)
&& ( opposite_colors(weakKing, strongBishop)
|| relative_rank(strongSide, weakKing) <= RANK_6))
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
}
/// KP vs KP. This is done by removing the weakest side's pawn and probing the
/// KP vs K bitbase: if the weakest side has a draw without the pawn, it probably
/// has at least a draw with the pawn as well. The exception is when the stronger
/// side's pawn is far advanced and not on a rook file; in this case it is often
/// possible to win (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1).
template<>
ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, VALUE_ZERO, 1));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
// Assume strongSide is white and the pawn is on files A-D
Square strongKing = normalize(pos, strongSide, pos.square<KING>(strongSide));
Square weakKing = normalize(pos, strongSide, pos.square<KING>(weakSide));
Square strongPawn = normalize(pos, strongSide, pos.square<PAWN>(strongSide));
Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
// If the pawn has advanced to the fifth rank or further, and is not a
// rook pawn, it's too dangerous to assume that it's at least a draw.
if (rank_of(strongPawn) >= RANK_5 && file_of(strongPawn) != FILE_A)
return SCALE_FACTOR_NONE;
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
// 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
-126
View File
@@ -1,126 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ENDGAME_H_INCLUDED
#define ENDGAME_H_INCLUDED
#include <memory>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include "position.h"
#include "types.h"
namespace Stockfish {
/// EndgameCode lists all supported endgame functions by corresponding codes
enum EndgameCode {
EVALUATION_FUNCTIONS,
KNNK, // KNN vs K
KNNKP, // KNN vs KP
KXK, // Generic "mate lone king" eval
KBNK, // KBN vs K
KPK, // KP vs K
KRKP, // KR vs KP
KRKB, // KR vs KB
KRKN, // KR vs KN
KQKP, // KQ vs KP
KQKR, // KQ vs KR
SCALING_FUNCTIONS,
KBPsK, // KB and pawns vs K
KQKRPs, // KQ vs KR and pawns
KRPKR, // KRP vs KR
KRPKB, // KRP vs KB
KRPPKRP, // KRPP vs KRP
KPsK, // K and pawns vs K
KBPKB, // KBP vs KB
KBPPKB, // KBPP vs KB
KBPKN, // KBP vs KN
KPKP // KP vs KP
};
/// Endgame functions can be of two types depending on whether they return a
/// Value or a ScaleFactor.
template<EndgameCode E> using
eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type;
/// Base and derived functors for endgame evaluation and scaling functions
template<typename T>
struct EndgameBase {
explicit EndgameBase(Color c) : strongSide(c), weakSide(~c) {}
virtual ~EndgameBase() = default;
virtual T operator()(const Position&) const = 0;
const Color strongSide, weakSide;
};
template<EndgameCode E, typename T = eg_type<E>>
struct Endgame : public EndgameBase<T> {
explicit Endgame(Color c) : EndgameBase<T>(c) {}
T operator()(const Position&) const override;
};
/// The Endgames namespace handles the pointers to endgame evaluation and scaling
/// base objects in two std::map. We use polymorphism to invoke the actual
/// endgame function by calling its virtual operator().
namespace Endgames {
template<typename T> using Ptr = std::unique_ptr<EndgameBase<T>>;
template<typename T> using Map = std::unordered_map<Key, Ptr<T>>;
extern std::pair<Map<Value>, Map<ScaleFactor>> maps;
void init();
template<typename T>
Map<T>& map() {
return std::get<std::is_same<T, ScaleFactor>::value>(maps);
}
template<EndgameCode E, typename T = eg_type<E>>
void add(const std::string& code) {
StateInfo st;
map<T>()[Position().set(code, WHITE, &st).material_key()] = Ptr<T>(new Endgame<E>(WHITE));
map<T>()[Position().set(code, BLACK, &st).material_key()] = Ptr<T>(new Endgame<E>(BLACK));
}
template<typename T>
const EndgameBase<T>* probe(Key key) {
auto it = map<T>().find(key);
return it != map<T>().end() ? it->second.get() : nullptr;
}
}
} // namespace Stockfish
#endif // #ifndef ENDGAME_H_INCLUDED
+57 -1096
View File
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,7 +20,6 @@
#define EVALUATE_H_INCLUDED #define EVALUATE_H_INCLUDED
#include <string> #include <string>
#include <optional>
#include "types.h" #include "types.h"
@@ -30,23 +29,24 @@ class Position;
namespace Eval { namespace Eval {
std::string trace(Position& pos); constexpr inline int SmallNetThreshold = 1165, PsqtOnlyThreshold = 2500;
Value evaluate(const Position& pos);
extern bool useNNUE;
extern std::string currentEvalFileName;
// The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // 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 // 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. // name of the macro or the location where this macro is defined, as it is used
#define EvalFileDefaultName "nn-5af11540bbfe.nnue" // in the Makefile/Fishtest.
#define EvalFileDefaultNameBig "nn-ae6a388e4a1a.nnue"
#define EvalFileDefaultNameSmall "nn-baff1ede1f90.nnue"
namespace NNUE { namespace NNUE {
struct Networks;
}
void init(); std::string trace(Position& pos, const Eval::NNUE::Networks& networks);
void verify();
int simple_eval(const Position& pos, Color c);
Value evaluate(const NNUE::Networks& networks, const Position& pos, int optimism);
} // namespace NNUE
} // namespace Eval } // namespace Eval
+5 -5
View File
@@ -3,8 +3,8 @@
* @author Dale Weiler * @author Dale Weiler
* @brief Utility for including binary files * @brief Utility for including binary files
* *
* Facilities for including binary files into the current translation unit and * Facilities for including binary files into the current translation unit
* making use from them externally in other translation units. * and making use of them externally in other translation units.
*/ */
#ifndef INCBIN_HDR #ifndef INCBIN_HDR
#define INCBIN_HDR #define INCBIN_HDR
@@ -139,7 +139,7 @@
#endif #endif
#if defined(__APPLE__) #if defined(__APPLE__)
/* The directives are different for Apple branded compilers */ /* The directives are different for Apple-branded compilers */
# define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n" # define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n"
# define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n" # define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
# define INCBIN_INT ".long " # define INCBIN_INT ".long "
@@ -261,8 +261,8 @@
INCBIN_STRINGIZE( \ INCBIN_STRINGIZE( \
INCBIN_STYLE_IDENT(TYPE)) \ INCBIN_STYLE_IDENT(TYPE)) \
/* Generate the global labels by indirectly invoking the macro with our style /* Generate the global labels by indirectly invoking the macro
* type and concatenating the name against them. */ * with our style type and concatenate the name against them. */
#define INCBIN_GLOBAL_LABELS(NAME, TYPE) \ #define INCBIN_GLOBAL_LABELS(NAME, TYPE) \
INCBIN_INVOKE( \ INCBIN_INVOKE( \
INCBIN_GLOBAL, \ INCBIN_GLOBAL, \
+13 -18
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,35 +19,30 @@
#include <iostream> #include <iostream>
#include "bitboard.h" #include "bitboard.h"
#include "endgame.h" #include "misc.h"
#include "position.h" #include "position.h"
#include "psqt.h" #include "tune.h"
#include "search.h" #include "types.h"
#include "syzygy/tbprobe.h"
#include "thread.h"
#include "tt.h"
#include "uci.h" #include "uci.h"
using namespace Stockfish; using namespace Stockfish;
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
Cluster::init();
if (Cluster::is_root())
std::cout << engine_info() << std::endl; std::cout << engine_info() << std::endl;
CommandLine::init(argc, argv);
UCI::init(Options);
Tune::init();
PSQT::init();
Bitboards::init(); Bitboards::init();
Position::init(); Position::init();
Bitbases::init();
Endgames::init();
Threads.set(size_t(Options["Threads"]));
Search::clear(); // After threads are up
Eval::NNUE::init();
UCI::loop(argc, argv); UCI uci(argc, argv);
Tune::init(uci.options);
uci.loop();
Cluster::finalize();
Threads.set(0);
return 0; return 0;
} }
-229
View File
@@ -1,229 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <cstring> // For std::memset
#include "material.h"
#include "thread.h"
using namespace std;
namespace Stockfish {
namespace {
#define S(mg, eg) make_score(mg, eg)
// Polynomial material imbalance parameters
// 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
};
// One Score parameter for each pair (our piece, their piece)
constexpr Score QuadraticTheirs[][PIECE_TYPE_NB] = {
// THEIR PIECE
// bishop pair pawn knight bishop rook queen
{ }, // Bishop pair
{S( 33, 30) }, // Pawn
{S( 46, 18), S(106, 84) }, // Knight OUR PIECE
{S( 75, 35), S( 59, 44), S( 60, 15) }, // Bishop
{S( 26, 35), S( 6, 22), S( 38, 39), S(-12, -2) }, // Rook
{S( 97, 93), S(100, 163), S(-58, -91), S(112, 192), S(276, 225) } // Queen
};
#undef S
// Endgame evaluation and scaling functions are accessed directly and not through
// the function maps because they correspond to more than one material hash key.
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
Endgame<KBPsK> ScaleKBPsK[] = { Endgame<KBPsK>(WHITE), Endgame<KBPsK>(BLACK) };
Endgame<KQKRPs> ScaleKQKRPs[] = { Endgame<KQKRPs>(WHITE), Endgame<KQKRPs>(BLACK) };
Endgame<KPsK> ScaleKPsK[] = { Endgame<KPsK>(WHITE), Endgame<KPsK>(BLACK) };
Endgame<KPKP> ScaleKPKP[] = { Endgame<KPKP>(WHITE), Endgame<KPKP>(BLACK) };
// Helper used to detect a given material distribution
bool is_KXK(const Position& pos, Color us) {
return !more_than_one(pos.pieces(~us))
&& pos.non_pawn_material(us) >= RookValueMg;
}
bool is_KBPsK(const Position& pos, Color us) {
return pos.non_pawn_material(us) == BishopValueMg
&& pos.count<PAWN>(us) >= 1;
}
bool is_KQKRPs(const Position& pos, Color us) {
return !pos.count<PAWN>(us)
&& pos.non_pawn_material(us) == QueenValueMg
&& pos.count<ROOK>(~us) == 1
&& pos.count<PAWN>(~us) >= 1;
}
/// imbalance() calculates the imbalance by comparing the piece count of each
/// piece type for both colors.
template<Color Us>
Score imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
constexpr Color Them = ~Us;
Score bonus = SCORE_ZERO;
// Second-degree polynomial material imbalance, by Tord Romstad
for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
{
if (!pieceCount[Us][pt1])
continue;
int v = QuadraticOurs[pt1][pt1] * pieceCount[Us][pt1];
for (int pt2 = NO_PIECE_TYPE; pt2 < pt1; ++pt2)
v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2];
bonus += pieceCount[Us][pt1] * v;
}
return bonus;
}
} // namespace
namespace Material {
/// Material::probe() looks up the current position's material configuration in
/// the material hash table. It returns a pointer to the Entry if the position
/// is found. Otherwise a new Entry is computed and stored there, so we don't
/// have to recompute all when the same material configuration occurs again.
Entry* probe(const Position& pos) {
Key key = pos.material_key();
Entry* e = pos.this_thread()->materialTable[key];
if (e->key == key)
return e;
std::memset(e, 0, sizeof(Entry));
e->key = key;
e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL;
Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK);
Value npm = std::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
// Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME]
e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
// Let's look if we have a specialized evaluation function for this particular
// material configuration. Firstly we look for a fixed configuration one, then
// for a generic one if the previous search failed.
if ((e->evaluationFunction = Endgames::probe<Value>(key)) != nullptr)
return e;
for (Color c : { WHITE, BLACK })
if (is_KXK(pos, c))
{
e->evaluationFunction = &EvaluateKXK[c];
return e;
}
// OK, we didn't find any special evaluation function for the current material
// configuration. Is there a suitable specialized scaling function?
const auto* sf = Endgames::probe<ScaleFactor>(key);
if (sf)
{
e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned
return e;
}
// We didn't find any specialized scaling function, so fall back on generic
// ones that refer to more than one material distribution. Note that in this
// case we don't return after setting the function.
for (Color c : { WHITE, BLACK })
{
if (is_KBPsK(pos, c))
e->scalingFunction[c] = &ScaleKBPsK[c];
else if (is_KQKRPs(pos, c))
e->scalingFunction[c] = &ScaleKQKRPs[c];
}
if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board
{
if (!pos.count<PAWN>(BLACK))
{
assert(pos.count<PAWN>(WHITE) >= 2);
e->scalingFunction[WHITE] = &ScaleKPsK[WHITE];
}
else if (!pos.count<PAWN>(WHITE))
{
assert(pos.count<PAWN>(BLACK) >= 2);
e->scalingFunction[BLACK] = &ScaleKPsK[BLACK];
}
else if (pos.count<PAWN>(WHITE) == 1 && pos.count<PAWN>(BLACK) == 1)
{
// This is a special case because we set scaling functions
// for both colors instead of only one.
e->scalingFunction[WHITE] = &ScaleKPKP[WHITE];
e->scalingFunction[BLACK] = &ScaleKPKP[BLACK];
}
}
// Zero or just one pawn makes it difficult to win, even with a small material
// advantage. This catches some trivial draws like KK, KBK and KNK and gives a
// drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN).
if (!pos.count<PAWN>(WHITE) && npm_w - npm_b <= BishopValueMg)
e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW :
npm_b <= BishopValueMg ? 4 : 14);
if (!pos.count<PAWN>(BLACK) && npm_b - npm_w <= BishopValueMg)
e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW :
npm_w <= BishopValueMg ? 4 : 14);
// Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
// for the bishop pair "extended piece", which allows us to be more flexible
// in defining bishop pair bonuses.
const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = {
{ pos.count<BISHOP>(WHITE) > 1, pos.count<PAWN>(WHITE), pos.count<KNIGHT>(WHITE),
pos.count<BISHOP>(WHITE) , pos.count<ROOK>(WHITE), pos.count<QUEEN >(WHITE) },
{ pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK),
pos.count<BISHOP>(BLACK) , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } };
e->score = (imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16;
return e;
}
} // namespace Material
} // namespace Stockfish
-71
View File
@@ -1,71 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MATERIAL_H_INCLUDED
#define MATERIAL_H_INCLUDED
#include "endgame.h"
#include "misc.h"
#include "position.h"
#include "types.h"
namespace Stockfish::Material {
/// Material::Entry contains various information about a material configuration.
/// It contains a material imbalance evaluation, a function pointer to a special
/// endgame evaluation function (which in most cases is nullptr, meaning that the
/// standard evaluation function will be used), and scale factors.
///
/// The scale factors are used to scale the evaluation score up or down. For
/// instance, in KRB vs KR endgames, the score is scaled down by a factor of 4,
/// which will result in scores of absolute value less than one pawn.
struct Entry {
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); }
// scale_factor() takes a position and a color as input and returns a scale factor
// for the given color. We have to provide the position in addition to the color
// because the scale factor may also be a function which should be applied to
// the position. For instance, in KBP vs K endgames, the scaling function looks
// for rook pawns and wrong-colored bishops.
ScaleFactor scale_factor(const Position& pos, Color c) const {
ScaleFactor sf = scalingFunction[c] ? (*scalingFunction[c])(pos)
: SCALE_FACTOR_NONE;
return sf != SCALE_FACTOR_NONE ? sf : ScaleFactor(factor[c]);
}
Key key;
const EndgameBase<Value>* evaluationFunction;
const EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each
// side (e.g. KPKP, KBPsK)
Score score;
int16_t gamePhase;
uint8_t factor[COLOR_NB];
};
using Table = HashTable<Entry, 8192>;
Entry* probe(const Position& pos);
} // namespace Stockfish::Material
#endif // #ifndef MATERIAL_H_INCLUDED
+144 -164
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -16,6 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "misc.h"
#ifdef _WIN32 #ifdef _WIN32
#if _WIN32_WINNT < 0x0601 #if _WIN32_WINNT < 0x0601
#undef _WIN32_WINNT #undef _WIN32_WINNT
@@ -33,7 +35,8 @@
// first to define the corresponding function pointers. // first to define the corresponding function pointers.
extern "C" { extern "C" {
using fun1_t = bool (*)(LOGICAL_PROCESSOR_RELATIONSHIP, using fun1_t = bool (*)(LOGICAL_PROCESSOR_RELATIONSHIP,
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD); PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX,
PDWORD);
using fun2_t = bool (*)(USHORT, PGROUP_AFFINITY); using fun2_t = bool (*)(USHORT, PGROUP_AFFINITY);
using fun3_t = bool (*)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); using fun3_t = bool (*)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
using fun4_t = bool (*)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT); using fun4_t = bool (*)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT);
@@ -44,53 +47,54 @@ using fun8_t = bool(*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES
} }
#endif #endif
#include <atomic>
#include <cmath> #include <cmath>
#include <cstdlib> #include <cstdlib>
#include <fstream> #include <fstream>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <mutex>
#include <sstream> #include <sstream>
#include <string_view> #include <string_view>
#include <vector>
#include "types.h"
#if defined(__linux__) && !defined(__ANDROID__) #if defined(__linux__) && !defined(__ANDROID__)
#include <stdlib.h>
#include <sys/mman.h> #include <sys/mman.h>
#endif #endif
#if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) || defined(__e2k__) #if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) \
|| (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) \
|| defined(__e2k__)
#define POSIXALIGNEDALLOC #define POSIXALIGNEDALLOC
#include <stdlib.h> #include <stdlib.h>
#endif #endif
#include "misc.h"
#include "thread.h"
using namespace std;
namespace Stockfish { namespace Stockfish {
namespace { namespace {
/// Version number or dev. // Version number or dev.
constexpr string_view version = "16"; constexpr std::string_view version = "dev";
/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and // Our fancy logging facility. The trick here is to replace cin.rdbuf() and
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We // cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
/// can toggle the logging of std::cout and std:cin at runtime whilst preserving // can toggle the logging of std::cout and std:cin at runtime whilst preserving
/// usual I/O functionality, all without changing a single line of code! // usual I/O functionality, all without changing a single line of code!
/// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81 // Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81
struct Tie: public streambuf { // MSVC requires split streambuf for cin and cout struct Tie: public std::streambuf { // MSVC requires split streambuf for cin and cout
Tie(streambuf* b, streambuf* l) : buf(b), logBuf(l) {} Tie(std::streambuf* b, std::streambuf* l) :
buf(b),
logBuf(l) {}
int sync() override { return logBuf->pubsync(), buf->pubsync(); } int sync() override { return logBuf->pubsync(), buf->pubsync(); }
int overflow(int c) override { return log(buf->sputc((char)c), "<< "); } int overflow(int c) override { return log(buf->sputc(char(c)), "<< "); }
int underflow() override { return buf->sgetc(); } int underflow() override { return buf->sgetc(); }
int uflow() override { return log(buf->sbumpc(), ">> "); } int uflow() override { return log(buf->sbumpc(), ">> "); }
streambuf *buf, *logBuf; std::streambuf *buf, *logBuf;
int log(int c, const char* prefix) { int log(int c, const char* prefix) {
@@ -99,16 +103,18 @@ struct Tie: public streambuf { // MSVC requires split streambuf for cin and cout
if (last == '\n') if (last == '\n')
logBuf->sputn(prefix, 3); logBuf->sputn(prefix, 3);
return last = logBuf->sputc((char)c); return last = logBuf->sputc(char(c));
} }
}; };
class Logger { class Logger {
Logger() : in(cin.rdbuf(), file.rdbuf()), out(cout.rdbuf(), file.rdbuf()) {} Logger() :
in(std::cin.rdbuf(), file.rdbuf()),
out(std::cout.rdbuf(), file.rdbuf()) {}
~Logger() { start(""); } ~Logger() { start(""); }
ofstream file; std::ofstream file;
Tie in, out; Tie in, out;
public: public:
@@ -118,23 +124,23 @@ public:
if (l.file.is_open()) if (l.file.is_open())
{ {
cout.rdbuf(l.out.buf); std::cout.rdbuf(l.out.buf);
cin.rdbuf(l.in.buf); std::cin.rdbuf(l.in.buf);
l.file.close(); l.file.close();
} }
if (!fname.empty()) if (!fname.empty())
{ {
l.file.open(fname, ifstream::out); l.file.open(fname, std::ifstream::out);
if (!l.file.is_open()) if (!l.file.is_open())
{ {
cerr << "Unable to open debug log file " << fname << endl; std::cerr << "Unable to open debug log file " << fname << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
cin.rdbuf(&l.in); std::cin.rdbuf(&l.in);
cout.rdbuf(&l.out); std::cout.rdbuf(&l.out);
} }
} }
}; };
@@ -142,19 +148,18 @@ public:
} // namespace } // namespace
/// engine_info() returns the full name of the current Stockfish version. // Returns the full name of the current Stockfish version.
/// For local dev compiles we try to append the commit sha and commit date // For local dev compiles we try to append the commit sha and commit date
/// from git if that fails only the local compilation date is set and "nogit" is specified: // from git if that fails only the local compilation date is set and "nogit" is specified:
/// Stockfish dev-YYYYMMDD-SHA // Stockfish dev-YYYYMMDD-SHA
/// or // or
/// Stockfish dev-YYYYMMDD-nogit // Stockfish dev-YYYYMMDD-nogit
/// //
/// For releases (non dev builds) we only include the version number: // For releases (non-dev builds) we only include the version number:
/// Stockfish version // Stockfish version
std::string engine_info(bool to_uci) {
string engine_info(bool to_uci) { std::stringstream ss;
stringstream ss; ss << "Stockfish " << version << std::setfill('0');
ss << "Stockfish " << version << setfill('0');
if constexpr (version == "dev") if constexpr (version == "dev")
{ {
@@ -162,12 +167,13 @@ string engine_info(bool to_uci) {
#ifdef GIT_DATE #ifdef GIT_DATE
ss << stringify(GIT_DATE); ss << stringify(GIT_DATE);
#else #else
constexpr string_view months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); constexpr std::string_view months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
string month, day, year; std::string month, day, year;
stringstream date(__DATE__); // From compiler, format is "Sep 21 2008" std::stringstream date(__DATE__); // From compiler, format is "Sep 21 2008"
date >> month >> day >> year; date >> month >> day >> year;
ss << year << setw(2) << setfill('0') << (1 + months.find(month) / 4) << setw(2) << setfill('0') << day; ss << year << std::setw(2) << std::setfill('0') << (1 + months.find(month) / 4)
<< std::setw(2) << std::setfill('0') << day;
#endif #endif
ss << "-"; ss << "-";
@@ -179,37 +185,35 @@ string engine_info(bool to_uci) {
#endif #endif
} }
ss << (to_uci ? "\nid author ": " by ") ss << (to_uci ? "\nid author " : " by ") << "the Stockfish developers (see AUTHORS file)";
<< "the Stockfish developers (see AUTHORS file)";
return ss.str(); return ss.str();
} }
/// compiler_info() returns a string trying to describe the compiler we use // Returns a string trying to describe the compiler we use
std::string compiler_info() { std::string compiler_info() {
#define make_version_string(major, minor, patch) stringify(major) "." stringify(minor) "." stringify(patch) #define make_version_string(major, minor, patch) \
stringify(major) "." stringify(minor) "." stringify(patch)
/// Predefined macros hell: // Predefined macros hell:
/// //
/// __GNUC__ Compiler is gcc, Clang or Intel on Linux // __GNUC__ Compiler is GCC, Clang or ICX
/// __INTEL_COMPILER Compiler is Intel // __clang__ Compiler is Clang or ICX
/// _MSC_VER Compiler is MSVC or Intel on Windows // __INTEL_LLVM_COMPILER Compiler is ICX
/// _WIN32 Building on Windows (any) // _MSC_VER Compiler is MSVC
/// _WIN64 Building on Windows 64 bit // _WIN32 Building on Windows (any)
// _WIN64 Building on Windows 64 bit
std::string compiler = "\nCompiled by "; std::string compiler = "\nCompiled by : ";
#ifdef __clang__ #if defined(__INTEL_LLVM_COMPILER)
compiler += "ICX ";
compiler += stringify(__INTEL_LLVM_COMPILER);
#elif defined(__clang__)
compiler += "clang++ "; compiler += "clang++ ";
compiler += make_version_string(__clang_major__, __clang_minor__, __clang_patchlevel__); compiler += make_version_string(__clang_major__, __clang_minor__, __clang_patchlevel__);
#elif __INTEL_COMPILER
compiler += "Intel compiler ";
compiler += "(version ";
compiler += stringify(__INTEL_COMPILER) " update " stringify(__INTEL_COMPILER_UPDATE);
compiler += ")";
#elif _MSC_VER #elif _MSC_VER
compiler += "MSVC "; compiler += "MSVC ";
compiler += "(version "; compiler += "(version ";
@@ -217,16 +221,14 @@ std::string compiler_info() {
compiler += ")"; compiler += ")";
#elif defined(__e2k__) && defined(__LCC__) #elif defined(__e2k__) && defined(__LCC__)
#define dot_ver2(n) \ #define dot_ver2(n) \
compiler += (char)'.'; \ compiler += char('.'); \
compiler += (char)('0' + (n) / 10); \ compiler += char('0' + (n) / 10); \
compiler += (char)('0' + (n) % 10); compiler += char('0' + (n) % 10);
compiler += "MCST LCC "; compiler += "MCST LCC ";
compiler += "(version "; compiler += "(version ";
compiler += std::to_string(__LCC__ / 100); compiler += std::to_string(__LCC__ / 100);
dot_ver2(__LCC__ % 100) dot_ver2(__LCC__ % 100) dot_ver2(__LCC_MINOR__) compiler += ")";
dot_ver2(__LCC_MINOR__)
compiler += ")";
#elif __GNUC__ #elif __GNUC__
compiler += "g++ (GNUC) "; compiler += "g++ (GNUC) ";
compiler += make_version_string(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); compiler += make_version_string(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
@@ -255,7 +257,14 @@ std::string compiler_info() {
compiler += " on unknown system"; compiler += " on unknown system";
#endif #endif
compiler += "\nCompilation settings include: "; compiler += "\nCompilation architecture : ";
#if defined(ARCH)
compiler += stringify(ARCH);
#else
compiler += "(undefined architecture)";
#endif
compiler += "\nCompilation settings : ";
compiler += (Is64Bit ? "64bit" : "32bit"); compiler += (Is64Bit ? "64bit" : "32bit");
#if defined(USE_VNNI) #if defined(USE_VNNI)
compiler += " VNNI"; compiler += " VNNI";
@@ -277,10 +286,9 @@ std::string compiler_info() {
compiler += " SSE2"; compiler += " SSE2";
#endif #endif
compiler += (HasPopCnt ? " POPCNT" : ""); compiler += (HasPopCnt ? " POPCNT" : "");
#if defined(USE_MMX) #if defined(USE_NEON_DOTPROD)
compiler += " MMX"; compiler += " NEON_DOTPROD";
#endif #elif defined(USE_NEON)
#if defined(USE_NEON)
compiler += " NEON"; compiler += " NEON";
#endif #endif
@@ -288,19 +296,20 @@ std::string compiler_info() {
compiler += " DEBUG"; compiler += " DEBUG";
#endif #endif
compiler += "\n__VERSION__ macro expands to: "; compiler += "\nCompiler __VERSION__ macro : ";
#ifdef __VERSION__ #ifdef __VERSION__
compiler += __VERSION__; compiler += __VERSION__;
#else #else
compiler += "(undefined macro)"; compiler += "(undefined macro)";
#endif #endif
compiler += "\n"; compiler += "\n";
return compiler; return compiler;
} }
/// Debug functions used mainly to collect run-time statistics // Debug functions used mainly to collect run-time statistics
constexpr int MaxDebugSlots = 32; constexpr int MaxDebugSlots = 32;
namespace { namespace {
@@ -357,44 +366,35 @@ void dbg_print() {
for (int i = 0; i < MaxDebugSlots; ++i) for (int i = 0; i < MaxDebugSlots; ++i)
if ((n = hit[i][0])) if ((n = hit[i][0]))
std::cerr << "Hit #" << i std::cerr << "Hit #" << i << ": Total " << n << " Hits " << hit[i][1]
<< ": Total " << n << " Hits " << hit[i][1] << " Hit Rate (%) " << 100.0 * E(hit[i][1]) << std::endl;
<< " Hit Rate (%) " << 100.0 * E(hit[i][1])
<< std::endl;
for (int i = 0; i < MaxDebugSlots; ++i) for (int i = 0; i < MaxDebugSlots; ++i)
if ((n = mean[i][0])) if ((n = mean[i][0]))
{ {
std::cerr << "Mean #" << i std::cerr << "Mean #" << i << ": Total " << n << " Mean " << E(mean[i][1]) << std::endl;
<< ": Total " << n << " Mean " << E(mean[i][1])
<< std::endl;
} }
for (int i = 0; i < MaxDebugSlots; ++i) for (int i = 0; i < MaxDebugSlots; ++i)
if ((n = stdev[i][0])) if ((n = stdev[i][0]))
{ {
double r = sqrtl(E(stdev[i][2]) - sqr(E(stdev[i][1]))); double r = sqrt(E(stdev[i][2]) - sqr(E(stdev[i][1])));
std::cerr << "Stdev #" << i std::cerr << "Stdev #" << i << ": Total " << n << " Stdev " << r << std::endl;
<< ": Total " << n << " Stdev " << r
<< std::endl;
} }
for (int i = 0; i < MaxDebugSlots; ++i) for (int i = 0; i < MaxDebugSlots; ++i)
if ((n = correl[i][0])) if ((n = correl[i][0]))
{ {
double r = (E(correl[i][5]) - E(correl[i][1]) * E(correl[i][3])) double r = (E(correl[i][5]) - E(correl[i][1]) * E(correl[i][3]))
/ ( sqrtl(E(correl[i][2]) - sqr(E(correl[i][1]))) / (sqrt(E(correl[i][2]) - sqr(E(correl[i][1])))
* sqrtl(E(correl[i][4]) - sqr(E(correl[i][3])))); * sqrt(E(correl[i][4]) - sqr(E(correl[i][3]))));
std::cerr << "Correl. #" << i std::cerr << "Correl. #" << i << ": Total " << n << " Coefficient " << r << std::endl;
<< ": Total " << n << " Coefficient " << r
<< std::endl;
} }
} }
/// Used to serialize access to std::cout to avoid multiple threads writing at // Used to serialize access to std::cout
/// the same time. // to avoid multiple threads writing at the same time.
std::ostream& operator<<(std::ostream& os, SyncCout sc) { std::ostream& operator<<(std::ostream& os, SyncCout sc) {
static std::mutex m; static std::mutex m;
@@ -409,13 +409,10 @@ std::ostream& operator<<(std::ostream& os, SyncCout sc) {
} }
/// Trampoline helper to avoid moving Logger to misc.h // Trampoline helper to avoid moving Logger to misc.h
void start_logger(const std::string& fname) { Logger::start(fname); } void start_logger(const std::string& fname) { Logger::start(fname); }
/// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking
/// function that doesn't stall the CPU waiting for data to be loaded from memory,
/// which can be quite slow.
#ifdef NO_PREFETCH #ifdef NO_PREFETCH
void prefetch(void*) {} void prefetch(void*) {}
@@ -424,13 +421,7 @@ void prefetch(void*) {}
void prefetch(void* addr) { void prefetch(void* addr) {
# if defined(__INTEL_COMPILER) #if defined(_MSC_VER)
// This hack prevents prefetches from being optimized away by
// Intel compiler. Both MSVC and gcc seem not be affected by this.
__asm__ ("");
# endif
# if defined(__INTEL_COMPILER) || defined(_MSC_VER)
_mm_prefetch((char*) addr, _MM_HINT_T0); _mm_prefetch((char*) addr, _MM_HINT_T0);
#else #else
__builtin_prefetch(addr); __builtin_prefetch(addr);
@@ -440,10 +431,9 @@ void prefetch(void* addr) {
#endif #endif
/// std_aligned_alloc() is our wrapper for systems where the c++17 implementation // Wrapper for systems where the c++17 implementation
/// does not guarantee the availability of aligned_alloc(). Memory allocated with // does not guarantee the availability of aligned_alloc(). Memory allocated with
/// std_aligned_alloc() must be freed with std_aligned_free(). // std_aligned_alloc() must be freed with std_aligned_free().
void* std_aligned_alloc(size_t alignment, size_t size) { void* std_aligned_alloc(size_t alignment, size_t size) {
#if defined(POSIXALIGNEDALLOC) #if defined(POSIXALIGNEDALLOC)
@@ -471,7 +461,7 @@ void std_aligned_free(void* ptr) {
#endif #endif
} }
/// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages. // aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages.
#if defined(_WIN32) #if defined(_WIN32)
@@ -496,13 +486,13 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize
if (!hAdvapi32) if (!hAdvapi32)
hAdvapi32 = LoadLibrary(TEXT("advapi32.dll")); hAdvapi32 = LoadLibrary(TEXT("advapi32.dll"));
auto fun6 = (fun6_t)(void(*)())GetProcAddress(hAdvapi32, "OpenProcessToken"); auto fun6 = fun6_t((void (*)()) GetProcAddress(hAdvapi32, "OpenProcessToken"));
if (!fun6) if (!fun6)
return nullptr; return nullptr;
auto fun7 = (fun7_t)(void(*)())GetProcAddress(hAdvapi32, "LookupPrivilegeValueA"); auto fun7 = fun7_t((void (*)()) GetProcAddress(hAdvapi32, "LookupPrivilegeValueA"));
if (!fun7) if (!fun7)
return nullptr; return nullptr;
auto fun8 = (fun8_t)(void(*)())GetProcAddress(hAdvapi32, "AdjustTokenPrivileges"); auto fun8 = fun8_t((void (*)()) GetProcAddress(hAdvapi32, "AdjustTokenPrivileges"));
if (!fun8) if (!fun8)
return nullptr; return nullptr;
@@ -525,13 +515,13 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize
// Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds, // Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds,
// we still need to query GetLastError() to ensure that the privileges were actually obtained. // we still need to query GetLastError() to ensure that the privileges were actually obtained.
if (fun8( // AdjustTokenPrivileges() if (fun8( // AdjustTokenPrivileges()
hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) && hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen)
GetLastError() == ERROR_SUCCESS) && GetLastError() == ERROR_SUCCESS)
{ {
// Round up size to full pages and allocate // Round up size to full pages and allocate
allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1); allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1);
mem = VirtualAlloc( mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES,
nullptr, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); PAGE_READWRITE);
// Privilege no longer needed, restore previous state // Privilege no longer needed, restore previous state
fun8( // AdjustTokenPrivileges () fun8( // AdjustTokenPrivileges ()
@@ -551,7 +541,7 @@ void* aligned_large_pages_alloc(size_t allocSize) {
// Try to allocate large pages // Try to allocate large pages
void* mem = aligned_large_pages_alloc_windows(allocSize); void* mem = aligned_large_pages_alloc_windows(allocSize);
// Fall back to regular, page aligned, allocation if necessary // Fall back to regular, page-aligned, allocation if necessary
if (!mem) if (!mem)
mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
@@ -568,7 +558,7 @@ void* aligned_large_pages_alloc(size_t allocSize) {
constexpr size_t alignment = 4096; // assumed small page size constexpr size_t alignment = 4096; // assumed small page size
#endif #endif
// round up to multiples of alignment // Round up to multiples of alignment
size_t size = ((allocSize + alignment - 1) / alignment) * alignment; size_t size = ((allocSize + alignment - 1) / alignment) * alignment;
void* mem = std_aligned_alloc(alignment, size); void* mem = std_aligned_alloc(alignment, size);
#if defined(MADV_HUGEPAGE) #if defined(MADV_HUGEPAGE)
@@ -580,7 +570,7 @@ void* aligned_large_pages_alloc(size_t allocSize) {
#endif #endif
/// aligned_large_pages_free() will free the previously allocated ttmem // aligned_large_pages_free() will free the previously allocated ttmem
#if defined(_WIN32) #if defined(_WIN32)
@@ -589,8 +579,7 @@ void aligned_large_pages_free(void* mem) {
if (mem && !VirtualFree(mem, 0, MEM_RELEASE)) if (mem && !VirtualFree(mem, 0, MEM_RELEASE))
{ {
DWORD err = GetLastError(); DWORD err = GetLastError();
std::cerr << "Failed to free large page memory. Error code: 0x" std::cerr << "Failed to free large page memory. Error code: 0x" << std::hex << err
<< std::hex << err
<< std::dec << std::endl; << std::dec << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -598,9 +587,7 @@ void aligned_large_pages_free(void* mem) {
#else #else
void aligned_large_pages_free(void *mem) { void aligned_large_pages_free(void* mem) { std_aligned_free(mem); }
std_aligned_free(mem);
}
#endif #endif
@@ -609,15 +596,15 @@ namespace WinProcGroup {
#ifndef _WIN32 #ifndef _WIN32
void bindThisThread(size_t) {} void bind_this_thread(size_t) {}
#else #else
/// best_node() retrieves logical processor information using Windows specific namespace {
/// API and returns the best node id for the thread with index idx. Original // Retrieves logical processor information using Windows-specific
/// code from Texel by Peter Österlund. // API and returns the best node id for the thread with index idx. Original
// code from Texel by Peter Österlund.
static int best_node(size_t idx) { int best_node(size_t idx) {
int threads = 0; int threads = 0;
int nodes = 0; int nodes = 0;
@@ -667,15 +654,14 @@ static int best_node(size_t idx) {
std::vector<int> groups; std::vector<int> groups;
// Run as many threads as possible on the same node until core limit is // Run as many threads as possible on the same node until the core limit is
// reached, then move on filling the next node. // reached, then move on to filling the next node.
for (int n = 0; n < nodes; n++) for (int n = 0; n < nodes; n++)
for (int i = 0; i < cores / nodes; i++) for (int i = 0; i < cores / nodes; i++)
groups.push_back(n); groups.push_back(n);
// In case a core has more than one logical processor (we assume 2) and we // In case a core has more than one logical processor (we assume 2) and we
// have still threads to allocate, then spread them evenly across available // still have threads to allocate, spread them evenly across available nodes.
// nodes.
for (int t = 0; t < threads - cores; t++) for (int t = 0; t < threads - cores; t++)
groups.push_back(t % nodes); groups.push_back(t % nodes);
@@ -683,11 +669,11 @@ static int best_node(size_t idx) {
// then return -1 and let the OS to decide what to do. // then return -1 and let the OS to decide what to do.
return idx < groups.size() ? groups[idx] : -1; return idx < groups.size() ? groups[idx] : -1;
} }
}
/// bindThisThread() set the group affinity of the current thread // Sets the group affinity of the current thread
void bind_this_thread(size_t idx) {
void bindThisThread(size_t idx) {
// Use only local variables to be thread-safe // Use only local variables to be thread-safe
int node = best_node(idx); int node = best_node(idx);
@@ -697,10 +683,10 @@ void bindThisThread(size_t idx) {
// Early exit if the needed API are not available at runtime // Early exit if the needed API are not available at runtime
HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll"));
auto fun2 = (fun2_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx"); auto fun2 = fun2_t((void (*)()) GetProcAddress(k32, "GetNumaNodeProcessorMaskEx"));
auto fun3 = (fun3_t)(void(*)())GetProcAddress(k32, "SetThreadGroupAffinity"); auto fun3 = fun3_t((void (*)()) GetProcAddress(k32, "SetThreadGroupAffinity"));
auto fun4 = (fun4_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMask2"); auto fun4 = fun4_t((void (*)()) GetProcAddress(k32, "GetNumaNodeProcessorMask2"));
auto fun5 = (fun5_t)(void(*)())GetProcAddress(k32, "GetMaximumProcessorGroupCount"); auto fun5 = fun5_t((void (*)()) GetProcAddress(k32, "GetMaximumProcessorGroupCount"));
if (!fun2 || !fun3) if (!fun2 || !fun3)
return; return;
@@ -719,7 +705,8 @@ void bindThisThread(size_t idx) {
elements = fun5(); // GetMaximumProcessorGroupCount elements = fun5(); // GetMaximumProcessorGroupCount
GROUP_AFFINITY* affinity = (GROUP_AFFINITY*) malloc(elements * sizeof(GROUP_AFFINITY)); GROUP_AFFINITY* affinity = (GROUP_AFFINITY*) malloc(elements * sizeof(GROUP_AFFINITY));
if (fun4(node, affinity, elements, &returnedElements)) // GetNumaNodeProcessorMask2 if (fun4(node, affinity, elements, &returnedElements)) // GetNumaNodeProcessorMask2
fun3(GetCurrentThread(), &affinity[idx % returnedElements], nullptr); // SetThreadGroupAffinity fun3(GetCurrentThread(), &affinity[idx % returnedElements],
nullptr); // SetThreadGroupAffinity
free(affinity); free(affinity);
} }
} }
@@ -736,23 +723,19 @@ void bindThisThread(size_t idx) {
#define GETCWD getcwd #define GETCWD getcwd
#endif #endif
namespace CommandLine { CommandLine::CommandLine(int _argc, char** _argv) :
argc(_argc),
argv(_argv) {
std::string pathSeparator;
string argv0; // path+name of the executable binary, as given by argv[0] // Extract the path+name of the executable binary
string binaryDirectory; // path of the executable directory std::string argv0 = argv[0];
string workingDirectory; // path of the working directory
void init([[maybe_unused]] int argc, char* argv[]) {
string pathSeparator;
// extract the path+name of the executable binary
argv0 = argv[0];
#ifdef _WIN32 #ifdef _WIN32
pathSeparator = "\\"; pathSeparator = "\\";
#ifdef _MSC_VER #ifdef _MSC_VER
// Under windows argv[0] may not have the extension. Also _get_pgmptr() had // Under windows argv[0] may not have the extension. Also _get_pgmptr() had
// issues in some windows 10 versions, so check returned values carefully. // issues in some Windows 10 versions, so check returned values carefully.
char* pgmptr = nullptr; char* pgmptr = nullptr;
if (!_get_pgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr) if (!_get_pgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr)
argv0 = pgmptr; argv0 = pgmptr;
@@ -761,14 +744,14 @@ void init([[maybe_unused]] int argc, char* argv[]) {
pathSeparator = "/"; pathSeparator = "/";
#endif #endif
// extract the working directory // Extract the working directory
workingDirectory = ""; workingDirectory = "";
char buff[40000]; char buff[40000];
char* cwd = GETCWD(buff, 40000); char* cwd = GETCWD(buff, 40000);
if (cwd) if (cwd)
workingDirectory = cwd; workingDirectory = cwd;
// extract the binary directory path from argv0 // Extract the binary directory path from argv0
binaryDirectory = argv0; binaryDirectory = argv0;
size_t pos = binaryDirectory.find_last_of("\\/"); size_t pos = binaryDirectory.find_last_of("\\/");
if (pos == std::string::npos) if (pos == std::string::npos)
@@ -776,12 +759,9 @@ void init([[maybe_unused]] int argc, char* argv[]) {
else else
binaryDirectory.resize(pos + 1); binaryDirectory.resize(pos + 1);
// pattern replacement: "./" at the start of path is replaced by the working directory // Pattern replacement: "./" at the start of path is replaced by the working directory
if (binaryDirectory.find("." + pathSeparator) == 0) if (binaryDirectory.find("." + pathSeparator) == 0)
binaryDirectory.replace(0, 1, workingDirectory); binaryDirectory.replace(0, 1, workingDirectory);
} }
} // namespace CommandLine
} // namespace Stockfish } // namespace Stockfish
+115 -58
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,14 +19,15 @@
#ifndef MISC_H_INCLUDED #ifndef MISC_H_INCLUDED
#define MISC_H_INCLUDED #define MISC_H_INCLUDED
#include <algorithm>
#include <cassert> #include <cassert>
#include <chrono> #include <chrono>
#include <ostream> #include <cstddef>
#include <cstdint>
#include <iosfwd>
#include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include <cstdint>
#include "types.h"
#define stringify2(x) #x #define stringify2(x) #x
#define stringify(x) stringify2(x) #define stringify(x) stringify2(x)
@@ -35,12 +36,43 @@ namespace Stockfish {
std::string engine_info(bool to_uci = false); std::string engine_info(bool to_uci = false);
std::string compiler_info(); std::string compiler_info();
// Preloads the given address in L1/L2 cache. This is a non-blocking
// function that doesn't stall the CPU waiting for data to be loaded from memory,
// which can be quite slow.
void prefetch(void* addr); void prefetch(void* addr);
void start_logger(const std::string& fname); void start_logger(const std::string& fname);
void* std_aligned_alloc(size_t alignment, size_t size); void* std_aligned_alloc(size_t alignment, size_t size);
void std_aligned_free(void* ptr); void std_aligned_free(void* ptr);
void* aligned_large_pages_alloc(size_t size); // memory aligned by page size, min alignment: 4096 bytes // memory aligned by page size, min alignment: 4096 bytes
void aligned_large_pages_free(void* mem); // nop if mem == nullptr void* aligned_large_pages_alloc(size_t size);
// nop if mem == nullptr
void aligned_large_pages_free(void* mem);
// Deleter for automating release of memory area
template<typename T>
struct AlignedDeleter {
void operator()(T* ptr) const {
ptr->~T();
std_aligned_free(ptr);
}
};
template<typename T>
struct LargePageDeleter {
void operator()(T* ptr) const {
ptr->~T();
aligned_large_pages_free(ptr);
}
};
template<typename T>
using AlignedPtr = std::unique_ptr<T, AlignedDeleter<T>>;
template<typename T>
using LargePagePtr = std::unique_ptr<T, LargePageDeleter<T>>;
void dbg_hit_on(bool cond, int slot = 0); void dbg_hit_on(bool cond, int slot = 0);
void dbg_mean_of(int64_t value, int slot = 0); void dbg_mean_of(int64_t value, int slot = 0);
@@ -51,41 +83,40 @@ void dbg_print();
using TimePoint = std::chrono::milliseconds::rep; // A value in milliseconds using TimePoint = std::chrono::milliseconds::rep; // A value in milliseconds
static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits"); static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits");
inline TimePoint now() { inline TimePoint now() {
return std::chrono::duration_cast<std::chrono::milliseconds> return std::chrono::duration_cast<std::chrono::milliseconds>(
(std::chrono::steady_clock::now().time_since_epoch()).count(); std::chrono::steady_clock::now().time_since_epoch())
.count();
} }
template<class Entry, int Size>
struct HashTable {
Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; }
private: enum SyncCout {
std::vector<Entry> table = std::vector<Entry>(Size); // Allocate on the heap IO_LOCK,
IO_UNLOCK
}; };
enum SyncCout { IO_LOCK, IO_UNLOCK };
std::ostream& operator<<(std::ostream&, SyncCout); std::ostream& operator<<(std::ostream&, SyncCout);
#define sync_cout std::cout << IO_LOCK #define sync_cout std::cout << IO_LOCK
#define sync_endl std::endl << IO_UNLOCK #define sync_endl std::endl << IO_UNLOCK
// align_ptr_up() : get the first aligned element of an array. // Get the first aligned element of an array.
// ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes, // ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes,
// where N is the number of elements in the array. // where N is the number of elements in the array.
template<uintptr_t Alignment, typename T> template<uintptr_t Alignment, typename T>
T* align_ptr_up(T* ptr) T* align_ptr_up(T* ptr) {
{
static_assert(alignof(T) < Alignment); static_assert(alignof(T) < Alignment);
const uintptr_t ptrint = reinterpret_cast<uintptr_t>(reinterpret_cast<char*>(ptr)); const uintptr_t ptrint = reinterpret_cast<uintptr_t>(reinterpret_cast<char*>(ptr));
return reinterpret_cast<T*>(reinterpret_cast<char*>((ptrint + (Alignment - 1)) / Alignment * Alignment)); return reinterpret_cast<T*>(
reinterpret_cast<char*>((ptrint + (Alignment - 1)) / Alignment * Alignment));
} }
// IsLittleEndian : true if and only if the binary is compiled on a little endian machine // True if and only if the binary is compiled on a little-endian machine
static inline const union { uint32_t i; char c[4]; } Le = { 0x01020304 }; static inline const union {
uint32_t i;
char c[4];
} Le = {0x01020304};
static inline const bool IsLittleEndian = (Le.c[0] == 4); static inline const bool IsLittleEndian = (Le.c[0] == 4);
@@ -97,6 +128,7 @@ public:
void push_back(const T& value) { values_[size_++] = value; } void push_back(const T& value) { values_[size_++] = value; }
const T* begin() const { return values_; } const T* begin() const { return values_; }
const T* end() const { return values_ + size_; } const T* end() const { return values_ + size_; }
const T& operator[](int index) const { return values_[index]; }
private: private:
T values_[MaxSize]; T values_[MaxSize];
@@ -104,20 +136,20 @@ private:
}; };
/// xorshift64star Pseudo-Random Number Generator // xorshift64star Pseudo-Random Number Generator
/// This class is based on original code written and dedicated // This class is based on original code written and dedicated
/// to the public domain by Sebastiano Vigna (2014). // to the public domain by Sebastiano Vigna (2014).
/// It has the following characteristics: // It has the following characteristics:
/// //
/// - Outputs 64-bit numbers // - Outputs 64-bit numbers
/// - Passes Dieharder and SmallCrush test batteries // - Passes Dieharder and SmallCrush test batteries
/// - Does not require warm-up, no zeroland to escape // - Does not require warm-up, no zeroland to escape
/// - Internal state is a single 64-bit integer // - Internal state is a single 64-bit integer
/// - Period is 2^64 - 1 // - Period is 2^64 - 1
/// - Speed: 1.60 ns/call (Core i7 @3.40GHz) // - Speed: 1.60 ns/call (Core i7 @3.40GHz)
/// //
/// For further analysis see // For further analysis see
/// <http://vigna.di.unimi.it/ftp/papers/xorshift.pdf> // <http://vigna.di.unimi.it/ftp/papers/xorshift.pdf>
class PRNG { class PRNG {
@@ -130,45 +162,70 @@ class PRNG {
} }
public: public:
PRNG(uint64_t seed) : s(seed) { assert(seed); } PRNG(uint64_t seed) :
s(seed) {
assert(seed);
}
template<typename T> T rand() { return T(rand64()); } template<typename T>
T rand() {
return T(rand64());
}
/// Special generator used to fast init magic numbers. // Special generator used to fast init magic numbers.
/// Output values only have 1/8th of their bits set on average. // Output values only have 1/8th of their bits set on average.
template<typename T> T sparse_rand() template<typename T>
{ return T(rand64() & rand64() & rand64()); } T sparse_rand() {
return T(rand64() & rand64() & rand64());
}
}; };
inline uint64_t mul_hi64(uint64_t a, uint64_t b) { inline uint64_t mul_hi64(uint64_t a, uint64_t b) {
#if defined(__GNUC__) && defined(IS_64BIT) #if defined(__GNUC__) && defined(IS_64BIT)
__extension__ using uint128 = unsigned __int128; __extension__ using uint128 = unsigned __int128;
return ((uint128)a * (uint128)b) >> 64; return (uint128(a) * uint128(b)) >> 64;
#else #else
uint64_t aL = (uint32_t)a, aH = a >> 32; uint64_t aL = uint32_t(a), aH = a >> 32;
uint64_t bL = (uint32_t)b, bH = b >> 32; uint64_t bL = uint32_t(b), bH = b >> 32;
uint64_t c1 = (aL * bL) >> 32; uint64_t c1 = (aL * bL) >> 32;
uint64_t c2 = aH * bL + c1; uint64_t c2 = aH * bL + c1;
uint64_t c3 = aL * bH + (uint32_t)c2; uint64_t c3 = aL * bH + uint32_t(c2);
return aH * bH + (c2 >> 32) + (c3 >> 32); return aH * bH + (c2 >> 32) + (c3 >> 32);
#endif #endif
} }
/// Under Windows it is not possible for a process to run on more than one // Under Windows it is not possible for a process to run on more than one
/// logical processor group. This usually means to be limited to use max 64 // logical processor group. This usually means being limited to using max 64
/// cores. To overcome this, some special platform specific API should be // cores. To overcome this, some special platform-specific API should be
/// called to set group affinity for each thread. Original code from Texel by // called to set group affinity for each thread. Original code from Texel by
/// Peter Österlund. // Peter Österlund.
namespace WinProcGroup { namespace WinProcGroup {
void bindThisThread(size_t idx); void bind_this_thread(size_t idx);
} }
namespace CommandLine {
void init(int argc, char* argv[]);
extern std::string binaryDirectory; // path of the executable directory struct CommandLine {
extern std::string workingDirectory; // path of the working directory public:
CommandLine(int, char**);
int argc;
char** argv;
std::string binaryDirectory; // path of the executable directory
std::string workingDirectory; // path of the working directory
};
namespace Utility {
template<typename T, typename Predicate>
void move_to_front(std::vector<T>& vec, Predicate pred) {
auto it = std::find_if(vec.begin(), vec.end(), pred);
if (it != vec.end())
{
std::rotate(vec.begin(), it, it + 1);
}
}
} }
} // namespace Stockfish } // namespace Stockfish
+37 -41
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -16,9 +16,12 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cassert>
#include "movegen.h" #include "movegen.h"
#include <cassert>
#include <initializer_list>
#include "bitboard.h"
#include "position.h" #include "position.h"
namespace Stockfish { namespace Stockfish {
@@ -28,22 +31,16 @@ namespace {
template<GenType Type, Direction D, bool Enemy> template<GenType Type, Direction D, bool Enemy>
ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) { ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) {
if constexpr (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) constexpr bool all = Type == EVASIONS || Type == NON_EVASIONS;
{
*moveList++ = make<PROMOTION>(to - D, to, QUEEN);
if constexpr (Enemy && Type == CAPTURES)
{
*moveList++ = make<PROMOTION>(to - D, to, ROOK);
*moveList++ = make<PROMOTION>(to - D, to, BISHOP);
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
}
}
if constexpr ((Type == QUIETS && !Enemy) || Type == EVASIONS || Type == NON_EVASIONS) if constexpr (Type == CAPTURES || all)
*moveList++ = Move::make<PROMOTION>(to - D, to, QUEEN);
if constexpr ((Type == CAPTURES && Enemy) || (Type == QUIETS && !Enemy) || all)
{ {
*moveList++ = make<PROMOTION>(to - D, to, ROOK); *moveList++ = Move::make<PROMOTION>(to - D, to, ROOK);
*moveList++ = make<PROMOTION>(to - D, to, BISHOP); *moveList++ = Move::make<PROMOTION>(to - D, to, BISHOP);
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT); *moveList++ = Move::make<PROMOTION>(to - D, to, KNIGHT);
} }
return moveList; return moveList;
@@ -61,8 +58,7 @@ namespace {
constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
const Bitboard emptySquares = ~pos.pieces(); const Bitboard emptySquares = ~pos.pieces();
const Bitboard enemies = Type == EVASIONS ? pos.checkers() const Bitboard enemies = Type == EVASIONS ? pos.checkers() : pos.pieces(Them);
: pos.pieces(Them);
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
@@ -93,13 +89,13 @@ namespace {
while (b1) while (b1)
{ {
Square to = pop_lsb(b1); Square to = pop_lsb(b1);
*moveList++ = make_move(to - Up, to); *moveList++ = Move(to - Up, to);
} }
while (b2) while (b2)
{ {
Square to = pop_lsb(b2); Square to = pop_lsb(b2);
*moveList++ = make_move(to - Up - Up, to); *moveList++ = Move(to - Up - Up, to);
} }
} }
@@ -132,13 +128,13 @@ namespace {
while (b1) while (b1)
{ {
Square to = pop_lsb(b1); Square to = pop_lsb(b1);
*moveList++ = make_move(to - UpRight, to); *moveList++ = Move(to - UpRight, to);
} }
while (b2) while (b2)
{ {
Square to = pop_lsb(b2); Square to = pop_lsb(b2);
*moveList++ = make_move(to - UpLeft, to); *moveList++ = Move(to - UpLeft, to);
} }
if (pos.ep_square() != SQ_NONE) if (pos.ep_square() != SQ_NONE)
@@ -154,7 +150,7 @@ namespace {
assert(b1); assert(b1);
while (b1) while (b1)
*moveList++ = make<EN_PASSANT>(pop_lsb(b1), pos.ep_square()); *moveList++ = Move::make<EN_PASSANT>(pop_lsb(b1), pos.ep_square());
} }
} }
@@ -179,7 +175,7 @@ namespace {
b &= pos.check_squares(Pt); b &= pos.check_squares(Pt);
while (b) while (b)
*moveList++ = make_move(from, pop_lsb(b)); *moveList++ = Move(from, pop_lsb(b));
} }
return moveList; return moveList;
@@ -217,12 +213,12 @@ namespace {
b &= ~attacks_bb<QUEEN>(pos.square<KING>(~Us)); b &= ~attacks_bb<QUEEN>(pos.square<KING>(~Us));
while (b) while (b)
*moveList++ = make_move(ksq, pop_lsb(b)); *moveList++ = Move(ksq, pop_lsb(b));
if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING)) if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING))
for (CastlingRights cr : {Us & KING_SIDE, Us & QUEEN_SIDE}) for (CastlingRights cr : {Us & KING_SIDE, Us & QUEEN_SIDE})
if (!pos.castling_impeded(cr) && pos.can_castle(cr)) if (!pos.castling_impeded(cr) && pos.can_castle(cr))
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(cr)); *moveList++ = Move::make<CASTLING>(ksq, pos.castling_rook_square(cr));
} }
return moveList; return moveList;
@@ -231,19 +227,19 @@ namespace {
} // namespace } // namespace
/// <CAPTURES> Generates all pseudo-legal captures plus queen promotions // <CAPTURES> Generates all pseudo-legal captures plus queen promotions
/// <QUIETS> Generates all pseudo-legal non-captures and underpromotions // <QUIETS> Generates all pseudo-legal non-captures and underpromotions
/// <EVASIONS> Generates all pseudo-legal check evasions when the side to move is in check // <EVASIONS> Generates all pseudo-legal check evasions
/// <QUIET_CHECKS> Generates all pseudo-legal non-captures giving check, except castling and promotions // <NON_EVASIONS> Generates all pseudo-legal captures and non-captures
/// <NON_EVASIONS> Generates all pseudo-legal captures and non-captures // <QUIET_CHECKS> Generates all pseudo-legal non-captures giving check,
/// // except castling and promotions
/// Returns a pointer to the end of the move list. //
// Returns a pointer to the end of the move list.
template<GenType Type> template<GenType Type>
ExtMove* generate(const Position& pos, ExtMove* moveList) { ExtMove* generate(const Position& pos, ExtMove* moveList) {
static_assert(Type != LEGAL, "Unsupported type in generate()"); static_assert(Type != LEGAL, "Unsupported type in generate()");
assert((Type == EVASIONS) == (bool)pos.checkers()); assert((Type == EVASIONS) == bool(pos.checkers()));
Color us = pos.side_to_move(); Color us = pos.side_to_move();
@@ -259,7 +255,7 @@ template ExtMove* generate<QUIET_CHECKS>(const Position&, ExtMove*);
template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*); template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
/// generate<LEGAL> generates all the legal moves in the given position // generate<LEGAL> generates all the legal moves in the given position
template<> template<>
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) { ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
@@ -269,12 +265,12 @@ ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
Square ksq = pos.square<KING>(us); Square ksq = pos.square<KING>(us);
ExtMove* cur = moveList; ExtMove* cur = moveList;
moveList = pos.checkers() ? generate<EVASIONS >(pos, moveList) moveList =
: generate<NON_EVASIONS>(pos, moveList); pos.checkers() ? generate<EVASIONS>(pos, moveList) : generate<NON_EVASIONS>(pos, moveList);
while (cur != moveList) while (cur != moveList)
if ( ((pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT) if (((pinned & cur->from_sq()) || cur->from_sq() == ksq || cur->type_of() == EN_PASSANT)
&& !pos.legal(*cur)) && !pos.legal(*cur))
*cur = (--moveList)->move; *cur = *(--moveList);
else else
++cur; ++cur;
+12 -15
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,7 +19,8 @@
#ifndef MOVEGEN_H_INCLUDED #ifndef MOVEGEN_H_INCLUDED
#define MOVEGEN_H_INCLUDED #define MOVEGEN_H_INCLUDED
#include <algorithm> #include <algorithm> // IWYU pragma: keep
#include <cstddef>
#include "types.h" #include "types.h"
@@ -36,37 +37,33 @@ enum GenType {
LEGAL LEGAL
}; };
struct ExtMove { struct ExtMove: public Move {
Move move;
int value; int value;
operator Move() const { return move; } void operator=(Move m) { data = m.raw(); }
void operator=(Move m) { move = m; }
// Inhibit unwanted implicit conversions to Move // Inhibit unwanted implicit conversions to Move
// with an ambiguity that yields to a compile error. // with an ambiguity that yields to a compile error.
operator float() const = delete; operator float() const = delete;
}; };
inline bool operator<(const ExtMove& f, const ExtMove& s) { inline bool operator<(const ExtMove& f, const ExtMove& s) { return f.value < s.value; }
return f.value < s.value;
}
template<GenType> template<GenType>
ExtMove* generate(const Position& pos, ExtMove* moveList); ExtMove* generate(const Position& pos, ExtMove* moveList);
/// The MoveList struct is a simple wrapper around generate(). It sometimes comes // The MoveList struct wraps the generate() function and returns a convenient
/// in handy to use this class instead of the low level generate() function. // list of moves. Using MoveList is sometimes preferable to directly calling
// the lower level generate() function.
template<GenType T> template<GenType T>
struct MoveList { struct MoveList {
explicit MoveList(const Position& pos) : last(generate<T>(pos, moveList)) {} explicit MoveList(const Position& pos) :
last(generate<T>(pos, moveList)) {}
const ExtMove* begin() const { return moveList; } const ExtMove* begin() const { return moveList; }
const ExtMove* end() const { return last; } const ExtMove* end() const { return last; }
size_t size() const { return last - moveList; } size_t size() const { return last - moveList; }
bool contains(Move move) const { bool contains(Move move) const { return std::find(begin(), end(), move) != end(); }
return std::find(begin(), end(), move) != end();
}
private: private:
ExtMove moveList[MAX_MOVES], *last; ExtMove moveList[MAX_MOVES], *last;
+175 -84
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -16,23 +16,50 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "movepick.h"
#include <algorithm>
#include <cassert> #include <cassert>
#include <iterator>
#include <utility>
#include "bitboard.h" #include "bitboard.h"
#include "movepick.h" #include "position.h"
namespace Stockfish { namespace Stockfish {
namespace { namespace {
enum Stages { enum Stages {
MAIN_TT, CAPTURE_INIT, GOOD_CAPTURE, REFUTATION, QUIET_INIT, QUIET, BAD_CAPTURE, // generate main search moves
EVASION_TT, EVASION_INIT, EVASION, MAIN_TT,
PROBCUT_TT, PROBCUT_INIT, PROBCUT, CAPTURE_INIT,
QSEARCH_TT, QCAPTURE_INIT, QCAPTURE, QCHECK_INIT, QCHECK GOOD_CAPTURE,
REFUTATION,
QUIET_INIT,
GOOD_QUIET,
BAD_CAPTURE,
BAD_QUIET,
// generate evasion moves
EVASION_TT,
EVASION_INIT,
EVASION,
// generate probcut moves
PROBCUT_TT,
PROBCUT_INIT,
PROBCUT,
// generate qsearch moves
QSEARCH_TT,
QCAPTURE_INIT,
QCAPTURE,
QCHECK_INIT,
QCHECK
}; };
// partial_insertion_sort() sorts moves in descending order up to and including // Sort moves in descending order up to and including
// a given limit. The order of moves smaller than the limit is left unspecified. // a given limit. The order of moves smaller than the limit is left unspecified.
void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) { void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) {
@@ -50,68 +77,85 @@ namespace {
} // namespace } // namespace
/// Constructors of the MovePicker class. As arguments we pass information // Constructors of the MovePicker class. As arguments, we pass information
/// to help it to return the (presumably) good moves first, to decide which // to help it return the (presumably) good moves first, to decide which
/// moves to return (in the quiescence search, for instance, we only want to // moves to return (in the quiescence search, for instance, we only want to
/// search captures, promotions, and some checks) and how important good move // search captures, promotions, and some checks) and how important a good
/// ordering is at the current node. // move ordering is at the current node.
/// MovePicker constructor for the main search // MovePicker constructor for the main search
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, MovePicker::MovePicker(const Position& p,
Move ttm,
Depth d,
const ButterflyHistory* mh,
const CapturePieceToHistory* cph, const CapturePieceToHistory* cph,
const PieceToHistory** ch, const PieceToHistory** ch,
const PawnHistory* ph,
Move cm, Move cm,
const Move* killers) const Move* killers) :
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), pos(p),
ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) mainHistory(mh),
{ captureHistory(cph),
continuationHistory(ch),
pawnHistory(ph),
ttMove(ttm),
refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}},
depth(d) {
assert(d > 0); assert(d > 0);
stage = (pos.checkers() ? EVASION_TT : MAIN_TT) + stage = (pos.checkers() ? EVASION_TT : MAIN_TT) + !(ttm && pos.pseudo_legal(ttm));
!(ttm && pos.pseudo_legal(ttm));
} }
/// MovePicker constructor for quiescence search // Constructor for quiescence search
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, MovePicker::MovePicker(const Position& p,
Move ttm,
Depth d,
const ButterflyHistory* mh,
const CapturePieceToHistory* cph, const CapturePieceToHistory* cph,
const PieceToHistory** ch, const PieceToHistory** ch,
Square rs) const PawnHistory* ph) :
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d) pos(p),
{ mainHistory(mh),
captureHistory(cph),
continuationHistory(ch),
pawnHistory(ph),
ttMove(ttm),
depth(d) {
assert(d <= 0); assert(d <= 0);
stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) + stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) + !(ttm && pos.pseudo_legal(ttm));
!( ttm
&& pos.pseudo_legal(ttm));
} }
/// MovePicker constructor for ProbCut: we generate captures with SEE greater // Constructor for ProbCut: we generate captures with SEE greater
/// than or equal to the given threshold. // than or equal to the given threshold.
MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) MovePicker::MovePicker(const Position& p, Move ttm, int th, const CapturePieceToHistory* cph) :
: pos(p), captureHistory(cph), ttMove(ttm), threshold(th) pos(p),
{ captureHistory(cph),
ttMove(ttm),
threshold(th) {
assert(!pos.checkers()); assert(!pos.checkers());
stage = PROBCUT_TT + !(ttm && pos.capture_stage(ttm) stage = PROBCUT_TT
&& pos.pseudo_legal(ttm) + !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm) && pos.see_ge(ttm, threshold));
&& pos.see_ge(ttm, threshold));
} }
/// MovePicker::score() assigns a numerical value to each move in a list, used // Assigns a numerical value to each move in a list, used
/// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring // for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring
/// captures with a good history. Quiets moves are ordered using the history tables. // captures with a good history. Quiets moves are ordered using the history tables.
template<GenType Type> template<GenType Type>
void MovePicker::score() { void MovePicker::score() {
static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
[[maybe_unused]] Bitboard threatenedByPawn, threatenedByMinor, threatenedByRook, threatenedPieces; [[maybe_unused]] Bitboard threatenedByPawn, threatenedByMinor, threatenedByRook,
threatenedPieces;
if constexpr (Type == QUIETS) if constexpr (Type == QUIETS)
{ {
Color us = pos.side_to_move(); Color us = pos.side_to_move();
threatenedByPawn = pos.attacks_by<PAWN>(~us); threatenedByPawn = pos.attacks_by<PAWN>(~us);
threatenedByMinor = pos.attacks_by<KNIGHT>(~us) | pos.attacks_by<BISHOP>(~us) | threatenedByPawn; threatenedByMinor =
pos.attacks_by<KNIGHT>(~us) | pos.attacks_by<BISHOP>(~us) | threatenedByPawn;
threatenedByRook = pos.attacks_by<ROOK>(~us) | threatenedByMinor; threatenedByRook = pos.attacks_by<ROOK>(~us) | threatenedByMinor;
// Pieces threatened by pieces of lesser material value // Pieces threatened by pieces of lesser material value
@@ -122,36 +166,60 @@ void MovePicker::score() {
for (auto& m : *this) for (auto& m : *this)
if constexpr (Type == CAPTURES) if constexpr (Type == CAPTURES)
m.value = (7 * int(PieceValue[MG][pos.piece_on(to_sq(m))]) m.value =
+ (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]) / 16; 7 * int(PieceValue[pos.piece_on(m.to_sq())])
+ (*captureHistory)[pos.moved_piece(m)][m.to_sq()][type_of(pos.piece_on(m.to_sq()))];
else if constexpr (Type == QUIETS) else if constexpr (Type == QUIETS)
m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)] {
+ 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] Piece pc = pos.moved_piece(m);
+ (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)] PieceType pt = type_of(pc);
+ (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)] Square from = m.from_sq();
+ (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)] Square to = m.to_sq();
+ (threatenedPieces & from_sq(m) ?
(type_of(pos.moved_piece(m)) == QUEEN && !(to_sq(m) & threatenedByRook) ? 50000 // histories
: type_of(pos.moved_piece(m)) == ROOK && !(to_sq(m) & threatenedByMinor) ? 25000 m.value = 2 * (*mainHistory)[pos.side_to_move()][m.from_to()];
: !(to_sq(m) & threatenedByPawn) ? 15000 m.value += 2 * (*pawnHistory)[pawn_structure_index(pos)][pc][to];
m.value += 2 * (*continuationHistory[0])[pc][to];
m.value += (*continuationHistory[1])[pc][to];
m.value += (*continuationHistory[2])[pc][to] / 4;
m.value += (*continuationHistory[3])[pc][to];
m.value += (*continuationHistory[5])[pc][to];
// bonus for checks
m.value += bool(pos.check_squares(pt) & to) * 16384;
// bonus for escaping from capture
m.value += threatenedPieces & from ? (pt == QUEEN && !(to & threatenedByRook) ? 51000
: pt == ROOK && !(to & threatenedByMinor) ? 24950
: !(to & threatenedByPawn) ? 14450
: 0) : 0)
: 0;
// malus for putting piece en prise
m.value -= !(threatenedPieces & from)
? (pt == QUEEN ? bool(to & threatenedByRook) * 48150
+ bool(to & threatenedByMinor) * 10650
: pt == ROOK ? bool(to & threatenedByMinor) * 24500
: pt != PAWN ? bool(to & threatenedByPawn) * 14950
: 0) : 0)
+ bool(pos.check_squares(type_of(pos.moved_piece(m))) & to_sq(m)) * 16384; : 0;
}
else // Type == EVASIONS else // Type == EVASIONS
{ {
if (pos.capture_stage(m)) if (pos.capture_stage(m))
m.value = PieceValue[MG][pos.piece_on(to_sq(m))] m.value =
- Value(type_of(pos.moved_piece(m))) PieceValue[pos.piece_on(m.to_sq())] - type_of(pos.moved_piece(m)) + (1 << 28);
+ (1 << 28);
else else
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] m.value = (*mainHistory)[pos.side_to_move()][m.from_to()]
+ (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]; + (*continuationHistory[0])[pos.moved_piece(m)][m.to_sq()]
+ (*pawnHistory)[pawn_structure_index(pos)][pos.moved_piece(m)][m.to_sq()];
} }
} }
/// MovePicker::select() returns the next move satisfying a predicate function. // Returns the next move satisfying a predicate function.
/// It never returns the TT move. // It never returns the TT move.
template<MovePicker::PickType T, typename Pred> template<MovePicker::PickType T, typename Pred>
Move MovePicker::select(Pred filter) { Move MovePicker::select(Pred filter) {
@@ -165,16 +233,19 @@ Move MovePicker::select(Pred filter) {
cur++; cur++;
} }
return MOVE_NONE; return Move::none();
} }
/// MovePicker::next_move() is the most important method of the MovePicker class. It // 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. // moves left, picking the move with the highest score from a list of generated moves.
Move MovePicker::next_move(bool skipQuiets) { Move MovePicker::next_move(bool skipQuiets) {
auto quiet_threshold = [](Depth d) { return -3550 * d; };
top: top:
switch (stage) { switch (stage)
{
case MAIN_TT : case MAIN_TT :
case EVASION_TT : case EVASION_TT :
@@ -196,9 +267,10 @@ top:
case GOOD_CAPTURE : case GOOD_CAPTURE :
if (select<Next>([&]() { if (select<Next>([&]() {
return pos.see_ge(*cur, Value(-cur->value)) ?
// Move losing capture to endBadCaptures to be tried later // Move losing capture to endBadCaptures to be tried later
true : (*endBadCaptures++ = *cur, false); })) return pos.see_ge(*cur, -cur->value / 18) ? true
: (*endBadCaptures++ = *cur, false);
}))
return *(cur - 1); return *(cur - 1);
// Prepare the pointers to loop over the refutations array // Prepare the pointers to loop over the refutations array
@@ -206,17 +278,16 @@ top:
endMoves = std::end(refutations); endMoves = std::end(refutations);
// If the countermove is the same as a killer, skip it // If the countermove is the same as a killer, skip it
if ( refutations[0].move == refutations[2].move if (refutations[0] == refutations[2] || refutations[1] == refutations[2])
|| refutations[1].move == refutations[2].move)
--endMoves; --endMoves;
++stage; ++stage;
[[fallthrough]]; [[fallthrough]];
case REFUTATION : case REFUTATION :
if (select<Next>([&](){ return *cur != MOVE_NONE if (select<Next>([&]() {
&& !pos.capture_stage(*cur) return *cur != Move::none() && !pos.capture_stage(*cur) && pos.pseudo_legal(*cur);
&& pos.pseudo_legal(*cur); })) }))
return *(cur - 1); return *(cur - 1);
++stage; ++stage;
[[fallthrough]]; [[fallthrough]];
@@ -225,22 +296,27 @@ top:
if (!skipQuiets) if (!skipQuiets)
{ {
cur = endBadCaptures; cur = endBadCaptures;
endMoves = generate<QUIETS>(pos, cur); endMoves = beginBadQuiets = endBadQuiets = generate<QUIETS>(pos, cur);
score<QUIETS>(); score<QUIETS>();
partial_insertion_sort(cur, endMoves, -3000 * depth); partial_insertion_sort(cur, endMoves, quiet_threshold(depth));
} }
++stage; ++stage;
[[fallthrough]]; [[fallthrough]];
case QUIET: case GOOD_QUIET :
if ( !skipQuiets if (!skipQuiets && select<Next>([&]() {
&& select<Next>([&](){return *cur != refutations[0].move return *cur != refutations[0] && *cur != refutations[1] && *cur != refutations[2];
&& *cur != refutations[1].move }))
&& *cur != refutations[2].move;})) {
if ((cur - 1)->value > -8000 || (cur - 1)->value <= quiet_threshold(depth))
return *(cur - 1); return *(cur - 1);
// Remaining quiets are bad
beginBadQuiets = cur - 1;
}
// Prepare the pointers to loop over the bad captures // Prepare the pointers to loop over the bad captures
cur = moves; cur = moves;
endMoves = endBadCaptures; endMoves = endBadCaptures;
@@ -249,7 +325,23 @@ top:
[[fallthrough]]; [[fallthrough]];
case BAD_CAPTURE : case BAD_CAPTURE :
return select<Next>([](){ return true; }); if (select<Next>([]() { return true; }))
return *(cur - 1);
// Prepare the pointers to loop over the bad quiets
cur = beginBadQuiets;
endMoves = endBadQuiets;
++stage;
[[fallthrough]];
case BAD_QUIET :
if (!skipQuiets)
return select<Next>([&]() {
return *cur != refutations[0] && *cur != refutations[1] && *cur != refutations[2];
});
return Move::none();
case EVASION_INIT : case EVASION_INIT :
cur = moves; cur = moves;
@@ -266,13 +358,12 @@ top:
return select<Next>([&]() { return pos.see_ge(*cur, threshold); }); return select<Next>([&]() { return pos.see_ge(*cur, threshold); });
case QCAPTURE : case QCAPTURE :
if (select<Next>([&](){ return depth > DEPTH_QS_RECAPTURES if (select<Next>([]() { return true; }))
|| to_sq(*cur) == recaptureSquare; }))
return *(cur - 1); return *(cur - 1);
// If we did not find any move and we do not try checks, we have finished // If we did not find any move and we do not try checks, we have finished
if (depth != DEPTH_QS_CHECKS) if (depth != DEPTH_QS_CHECKS)
return MOVE_NONE; return Move::none();
++stage; ++stage;
[[fallthrough]]; [[fallthrough]];
@@ -289,7 +380,7 @@ top:
} }
assert(false); assert(false);
return MOVE_NONE; // Silence warning return Move::none(); // Silence warning
} }
} // namespace Stockfish } // namespace Stockfish
+97 -50
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,9 +19,14 @@
#ifndef MOVEPICK_H_INCLUDED #ifndef MOVEPICK_H_INCLUDED
#define MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED
#include <algorithm>
#include <array> #include <array>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <limits> #include <limits>
#include <type_traits> #include <type_traits> // IWYU pragma: keep
#include "movegen.h" #include "movegen.h"
#include "position.h" #include "position.h"
@@ -29,10 +34,30 @@
namespace Stockfish { namespace Stockfish {
/// StatsEntry stores the stat table value. It is usually a number but could constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2
/// be a move or even a nested history. We use a class instead of naked value constexpr int CORRECTION_HISTORY_SIZE = 16384; // has to be a power of 2
/// to directly call history update operator<<() on the entry so to use stats constexpr int CORRECTION_HISTORY_LIMIT = 1024;
/// tables at caller sites as simple multi-dim arrays.
static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0,
"PAWN_HISTORY_SIZE has to be a power of 2");
static_assert((CORRECTION_HISTORY_SIZE & (CORRECTION_HISTORY_SIZE - 1)) == 0,
"CORRECTION_HISTORY_SIZE has to be a power of 2");
enum PawnHistoryType {
Normal,
Correction
};
template<PawnHistoryType T = Normal>
inline int pawn_structure_index(const Position& pos) {
return pos.pawn_key() & ((T == Normal ? PAWN_HISTORY_SIZE : CORRECTION_HISTORY_SIZE) - 1);
}
// 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 a naked value
// to directly call history update operator<<() on the entry so to use stats
// tables at caller sites as simple multi-dim arrays.
template<typename T, int D> template<typename T, int D>
class StatsEntry { class StatsEntry {
@@ -45,29 +70,29 @@ public:
operator const T&() const { return entry; } operator const T&() const { return entry; }
void operator<<(int bonus) { void operator<<(int bonus) {
assert(abs(bonus) <= D); // Ensure range is [-D, D]
static_assert(D <= std::numeric_limits<T>::max(), "D overflows T"); static_assert(D <= std::numeric_limits<T>::max(), "D overflows T");
entry += bonus - entry * abs(bonus) / D; // Make sure that bonus is in range [-D, D]
int clampedBonus = std::clamp(bonus, -D, D);
entry += clampedBonus - entry * std::abs(clampedBonus) / D;
assert(abs(entry) <= D); assert(std::abs(entry) <= D);
} }
}; };
/// Stats is a generic N-dimensional array used to store various statistics. // Stats is a generic N-dimensional array used to store various statistics.
/// The first template parameter T is the base type of the array, the second // The first template parameter T is the base type of the array, and the second
/// template parameter D limits the range of updates in [-D, D] when we update // template parameter D limits the range of updates in [-D, D] when we update
/// values with the << operator, while the last parameters (Size and Sizes) // values with the << operator, while the last parameters (Size and Sizes)
/// encode the dimensions of the array. // encode the dimensions of the array.
template<typename T, int D, int Size, int... Sizes> template<typename T, int D, int Size, int... Sizes>
struct Stats : public std::array<Stats<T, D, Sizes...>, Size> struct Stats: public std::array<Stats<T, D, Sizes...>, Size> {
{
using stats = Stats<T, D, Size, Sizes...>; using stats = Stats<T, D, Size, Sizes...>;
void fill(const T& v) { void fill(const T& v) {
// For standard-layout 'this' points to first struct member // For standard-layout 'this' points to the first struct member
assert(std::is_standard_layout<stats>::value); assert(std::is_standard_layout_v<stats>);
using entry = StatsEntry<T, D>; using entry = StatsEntry<T, D>;
entry* p = reinterpret_cast<entry*>(this); entry* p = reinterpret_cast<entry*>(this);
@@ -78,62 +103,84 @@ struct Stats : public std::array<Stats<T, D, Sizes...>, Size>
template<typename T, int D, int Size> template<typename T, int D, int Size>
struct Stats<T, D, Size>: public std::array<StatsEntry<T, D>, Size> {}; struct Stats<T, D, Size>: public std::array<StatsEntry<T, D>, Size> {};
/// In stats table, D=0 means that the template parameter is not used // In stats table, D=0 means that the template parameter is not used
enum StatsParams { NOT_USED = 0 }; enum StatsParams {
enum StatsType { NoCaptures, Captures }; NOT_USED = 0
};
enum StatsType {
NoCaptures,
Captures
};
/// ButterflyHistory records how often quiet moves have been successful or // ButterflyHistory records how often quiet moves have been successful or unsuccessful
/// unsuccessful during the current search, and is used for reduction and move // during the current search, and is used for reduction and move ordering decisions.
/// ordering decisions. It uses 2 tables (one for each color) indexed by // It uses 2 tables (one for each color) indexed by the move's from and to squares,
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards // see www.chessprogramming.org/Butterfly_Boards (~11 elo)
/// (~11 elo)
using ButterflyHistory = Stats<int16_t, 7183, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)>; using ButterflyHistory = Stats<int16_t, 7183, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)>;
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous // CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
/// move, see www.chessprogramming.org/Countermove_Heuristic // move, see www.chessprogramming.org/Countermove_Heuristic
using CounterMoveHistory = Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB>; using CounterMoveHistory = Stats<Move, NOT_USED, PIECE_NB, SQUARE_NB>;
/// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] // CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
using CapturePieceToHistory = Stats<int16_t, 10692, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB>; using CapturePieceToHistory = Stats<int16_t, 10692, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB>;
/// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] // PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to]
using PieceToHistory = Stats<int16_t, 29952, PIECE_NB, SQUARE_NB>; using PieceToHistory = Stats<int16_t, 29952, PIECE_NB, SQUARE_NB>;
/// ContinuationHistory is the combined history of a given pair of moves, usually // ContinuationHistory is the combined history of a given pair of moves, usually
/// the current one given a previous one. The nested history table is based on // the current one given a previous one. The nested history table is based on
/// PieceToHistory instead of ButterflyBoards. // PieceToHistory instead of ButterflyBoards.
/// (~63 elo) // (~63 elo)
using ContinuationHistory = Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB>; using ContinuationHistory = Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB>;
// PawnHistory is addressed by the pawn structure and a move's [piece][to]
using PawnHistory = Stats<int16_t, 8192, PAWN_HISTORY_SIZE, PIECE_NB, SQUARE_NB>;
/// MovePicker class is used to pick one pseudo-legal move at a time from the // CorrectionHistory is addressed by color and pawn structure
/// current position. The most important method is next_move(), which returns a using CorrectionHistory =
/// new pseudo-legal move each time it is called, until there are no moves left, Stats<int16_t, CORRECTION_HISTORY_LIMIT, COLOR_NB, CORRECTION_HISTORY_SIZE>;
/// 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 // MovePicker class is used to pick one pseudo-legal move at a time from the
/// likely to get a cut-off first. // 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.
class MovePicker { class MovePicker {
enum PickType { Next, Best }; enum PickType {
Next,
Best
};
public: public:
MovePicker(const MovePicker&) = delete; MovePicker(const MovePicker&) = delete;
MovePicker& operator=(const MovePicker&) = delete; MovePicker& operator=(const MovePicker&) = delete;
MovePicker(const Position&, Move, Depth, const ButterflyHistory*, MovePicker(const Position&,
Move,
Depth,
const ButterflyHistory*,
const CapturePieceToHistory*, const CapturePieceToHistory*,
const PieceToHistory**, const PieceToHistory**,
const PawnHistory*,
Move, Move,
const Move*); const Move*);
MovePicker(const Position&, Move, Depth, const ButterflyHistory*, MovePicker(const Position&,
Move,
Depth,
const ButterflyHistory*,
const CapturePieceToHistory*, const CapturePieceToHistory*,
const PieceToHistory**, const PieceToHistory**,
Square); const PawnHistory*);
MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); MovePicker(const Position&, Move, int, const CapturePieceToHistory*);
Move next_move(bool skipQuiets = false); Move next_move(bool skipQuiets = false);
private: private:
template<PickType T, typename Pred> Move select(Pred); template<PickType T, typename Pred>
template<GenType> void score(); Move select(Pred);
template<GenType>
void score();
ExtMove* begin() { return cur; } ExtMove* begin() { return cur; }
ExtMove* end() { return endMoves; } ExtMove* end() { return endMoves; }
@@ -141,11 +188,11 @@ private:
const ButterflyHistory* mainHistory; const ButterflyHistory* mainHistory;
const CapturePieceToHistory* captureHistory; const CapturePieceToHistory* captureHistory;
const PieceToHistory** continuationHistory; const PieceToHistory** continuationHistory;
const PawnHistory* pawnHistory;
Move ttMove; Move ttMove;
ExtMove refutations[3], *cur, *endMoves, *endBadCaptures; ExtMove refutations[3], *cur, *endMoves, *endBadCaptures, *beginBadQuiets, *endBadQuiets;
int stage; int stage;
Square recaptureSquare; int threshold;
Value threshold;
Depth depth; Depth depth;
ExtMove moves[MAX_MOVES]; ExtMove moves[MAX_MOVES];
}; };
-405
View File
@@ -1,405 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Code for calculating NNUE evaluation function
#include <fstream>
#include <iomanip>
#include <iostream>
#include <set>
#include <sstream>
#include <string_view>
#include "../evaluate.h"
#include "../position.h"
#include "../uci.h"
#include "../types.h"
#include "evaluate_nnue.h"
namespace Stockfish::Eval::NNUE {
// Input feature converter
LargePagePtr<FeatureTransformer> featureTransformer;
// Evaluation function
AlignedPtr<Network> network[LayerStacks];
// Evaluation function file name
std::string fileName;
std::string netDescription;
namespace Detail {
// Initialize the evaluation function parameters
template <typename T>
void initialize(AlignedPtr<T>& pointer) {
pointer.reset(reinterpret_cast<T*>(std_aligned_alloc(alignof(T), sizeof(T))));
std::memset(pointer.get(), 0, sizeof(T));
}
template <typename T>
void initialize(LargePagePtr<T>& pointer) {
static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T");
pointer.reset(reinterpret_cast<T*>(aligned_large_pages_alloc(sizeof(T))));
std::memset(pointer.get(), 0, sizeof(T));
}
// Read evaluation function parameters
template <typename T>
bool read_parameters(std::istream& stream, T& reference) {
std::uint32_t header;
header = read_little_endian<std::uint32_t>(stream);
if (!stream || header != T::get_hash_value()) return false;
return reference.read_parameters(stream);
}
// Write evaluation function parameters
template <typename T>
bool write_parameters(std::ostream& stream, const T& reference) {
write_little_endian<std::uint32_t>(stream, T::get_hash_value());
return reference.write_parameters(stream);
}
} // namespace Detail
// Initialize the evaluation function parameters
static void initialize() {
Detail::initialize(featureTransformer);
for (std::size_t i = 0; i < LayerStacks; ++i)
Detail::initialize(network[i]);
}
// Read network header
static bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* desc)
{
std::uint32_t version, size;
version = read_little_endian<std::uint32_t>(stream);
*hashValue = read_little_endian<std::uint32_t>(stream);
size = read_little_endian<std::uint32_t>(stream);
if (!stream || version != Version) return false;
desc->resize(size);
stream.read(&(*desc)[0], size);
return !stream.fail();
}
// Write network header
static bool write_header(std::ostream& stream, std::uint32_t hashValue, const std::string& desc)
{
write_little_endian<std::uint32_t>(stream, Version);
write_little_endian<std::uint32_t>(stream, hashValue);
write_little_endian<std::uint32_t>(stream, (std::uint32_t)desc.size());
stream.write(&desc[0], desc.size());
return !stream.fail();
}
// Read network parameters
static bool read_parameters(std::istream& stream) {
std::uint32_t hashValue;
if (!read_header(stream, &hashValue, &netDescription)) return false;
if (hashValue != HashValue) return false;
if (!Detail::read_parameters(stream, *featureTransformer)) return false;
for (std::size_t i = 0; i < LayerStacks; ++i)
if (!Detail::read_parameters(stream, *(network[i]))) return false;
return stream && stream.peek() == std::ios::traits_type::eof();
}
// Write network parameters
static bool write_parameters(std::ostream& stream) {
if (!write_header(stream, HashValue, netDescription)) return false;
if (!Detail::write_parameters(stream, *featureTransformer)) return false;
for (std::size_t i = 0; i < LayerStacks; ++i)
if (!Detail::write_parameters(stream, *(network[i]))) return false;
return (bool)stream;
}
void hint_common_parent_position(const Position& pos) {
if (Eval::useNNUE)
featureTransformer->hint_common_access(pos);
}
// Evaluation function. Perform differential calculation.
Value evaluate(const Position& pos, bool adjusted, int* complexity) {
// We manually align the arrays on the stack because with gcc < 9.3
// overaligning stack variables with alignas() doesn't work correctly.
constexpr uint64_t alignment = CacheLineSize;
constexpr int delta = 24;
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
TransformedFeatureType transformedFeaturesUnaligned[
FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)];
auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
#else
alignas(alignment)
TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize];
#endif
ASSERT_ALIGNED(transformedFeatures, alignment);
const int bucket = (pos.count<ALL_PIECES>() - 1) / 4;
const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket);
const auto positional = network[bucket]->propagate(transformedFeatures);
if (complexity)
*complexity = abs(psqt - positional) / OutputScale;
// Give more value to positional evaluation when adjusted flag is set
if (adjusted)
return static_cast<Value>(((1024 - delta) * psqt + (1024 + delta) * positional) / (1024 * OutputScale));
else
return static_cast<Value>((psqt + positional) / OutputScale);
}
struct NnueEvalTrace {
static_assert(LayerStacks == PSQTBuckets);
Value psqt[LayerStacks];
Value positional[LayerStacks];
std::size_t correctBucket;
};
static NnueEvalTrace trace_evaluate(const Position& pos) {
// We manually align the arrays on the stack because with gcc < 9.3
// overaligning stack variables with alignas() doesn't work correctly.
constexpr uint64_t alignment = CacheLineSize;
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
TransformedFeatureType transformedFeaturesUnaligned[
FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)];
auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
#else
alignas(alignment)
TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize];
#endif
ASSERT_ALIGNED(transformedFeatures, alignment);
NnueEvalTrace t{};
t.correctBucket = (pos.count<ALL_PIECES>() - 1) / 4;
for (IndexType bucket = 0; bucket < LayerStacks; ++bucket) {
const auto materialist = featureTransformer->transform(pos, transformedFeatures, bucket);
const auto positional = network[bucket]->propagate(transformedFeatures);
t.psqt[bucket] = static_cast<Value>( materialist / OutputScale );
t.positional[bucket] = static_cast<Value>( positional / OutputScale );
}
return t;
}
constexpr std::string_view PieceToChar(" PNBRQK pnbrqk");
// format_cp_compact() converts a Value into (centi)pawns and writes it in a buffer.
// The buffer must have capacity for at least 5 chars.
static void format_cp_compact(Value v, char* buffer) {
buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' ');
int cp = std::abs(100 * v / UCI::NormalizeToPawnValue);
if (cp >= 10000)
{
buffer[1] = '0' + cp / 10000; cp %= 10000;
buffer[2] = '0' + cp / 1000; cp %= 1000;
buffer[3] = '0' + cp / 100;
buffer[4] = ' ';
}
else if (cp >= 1000)
{
buffer[1] = '0' + cp / 1000; cp %= 1000;
buffer[2] = '0' + cp / 100; cp %= 100;
buffer[3] = '.';
buffer[4] = '0' + cp / 10;
}
else
{
buffer[1] = '0' + cp / 100; cp %= 100;
buffer[2] = '.';
buffer[3] = '0' + cp / 10; cp %= 10;
buffer[4] = '0' + cp / 1;
}
}
// format_cp_aligned_dot() converts a Value into (centi)pawns, always keeping two decimals.
static void format_cp_aligned_dot(Value v, std::stringstream &stream) {
const double cp = 1.0 * std::abs(int(v)) / UCI::NormalizeToPawnValue;
stream << (v < 0 ? '-' : v > 0 ? '+' : ' ')
<< std::setiosflags(std::ios::fixed)
<< std::setw(6)
<< std::setprecision(2)
<< cp;
}
// trace() returns a string with the value of each piece on a board,
// and a table for (PSQT, Layers) values bucket by bucket.
std::string trace(Position& pos) {
std::stringstream ss;
char board[3*8+1][8*8+2];
std::memset(board, ' ', sizeof(board));
for (int row = 0; row < 3*8+1; ++row)
board[row][8*8+1] = '\0';
// A lambda to output one box of the board
auto writeSquare = [&board](File file, Rank rank, Piece pc, Value value) {
const int x = ((int)file) * 8;
const int y = (7 - (int)rank) * 3;
for (int i = 1; i < 8; ++i)
board[y][x+i] = board[y+3][x+i] = '-';
for (int i = 1; i < 3; ++i)
board[y+i][x] = board[y+i][x+8] = '|';
board[y][x] = board[y][x+8] = board[y+3][x+8] = board[y+3][x] = '+';
if (pc != NO_PIECE)
board[y+1][x+4] = PieceToChar[pc];
if (value != VALUE_NONE)
format_cp_compact(value, &board[y+2][x+2]);
};
// We estimate the value of each piece by doing a differential evaluation from
// the current base eval, simulating the removal of the piece from its square.
Value base = evaluate(pos);
base = pos.side_to_move() == WHITE ? base : -base;
for (File f = FILE_A; f <= FILE_H; ++f)
for (Rank r = RANK_1; r <= RANK_8; ++r)
{
Square sq = make_square(f, r);
Piece pc = pos.piece_on(sq);
Value v = VALUE_NONE;
if (pc != NO_PIECE && type_of(pc) != KING)
{
auto st = pos.state();
pos.remove_piece(sq);
st->accumulator.computed[WHITE] = false;
st->accumulator.computed[BLACK] = false;
Value eval = evaluate(pos);
eval = pos.side_to_move() == WHITE ? eval : -eval;
v = base - eval;
pos.put_piece(pc, sq);
st->accumulator.computed[WHITE] = false;
st->accumulator.computed[BLACK] = false;
}
writeSquare(f, r, pc, v);
}
ss << " NNUE derived piece values:\n";
for (int row = 0; row < 3*8+1; ++row)
ss << board[row] << '\n';
ss << '\n';
auto t = trace_evaluate(pos);
ss << " NNUE network contributions "
<< (pos.side_to_move() == WHITE ? "(White to move)" : "(Black to move)") << std::endl
<< "+------------+------------+------------+------------+\n"
<< "| Bucket | Material | Positional | Total |\n"
<< "| | (PSQT) | (Layers) | |\n"
<< "+------------+------------+------------+------------+\n";
for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket)
{
ss << "| " << bucket << " ";
ss << " | "; format_cp_aligned_dot(t.psqt[bucket], ss); ss << " "
<< " | "; format_cp_aligned_dot(t.positional[bucket], ss); ss << " "
<< " | "; format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss); ss << " "
<< " |";
if (bucket == t.correctBucket)
ss << " <-- this bucket is used";
ss << '\n';
}
ss << "+------------+------------+------------+------------+\n";
return ss.str();
}
// Load eval, from a file stream or a memory stream
bool load_eval(std::string name, std::istream& stream) {
initialize();
fileName = name;
return read_parameters(stream);
}
// Save eval, to a file stream or a memory stream
bool save_eval(std::ostream& stream) {
if (fileName.empty())
return false;
return write_parameters(stream);
}
/// Save eval, to a file given by its name
bool save_eval(const std::optional<std::string>& filename) {
std::string actualFilename;
std::string msg;
if (filename.has_value())
actualFilename = filename.value();
else
{
if (currentEvalFileName != EvalFileDefaultName)
{
msg = "Failed to export a net. A non-embedded net can only be saved if the filename is specified";
sync_cout << msg << sync_endl;
return false;
}
actualFilename = EvalFileDefaultName;
}
std::ofstream stream(actualFilename, std::ios_base::binary);
bool saved = save_eval(stream);
msg = saved ? "Network saved successfully to " + actualFilename
: "Failed to export a net";
sync_cout << msg << sync_endl;
return saved;
}
} // namespace Stockfish::Eval::NNUE
-68
View File
@@ -1,68 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// header used in NNUE evaluation function
#ifndef NNUE_EVALUATE_NNUE_H_INCLUDED
#define NNUE_EVALUATE_NNUE_H_INCLUDED
#include "nnue_feature_transformer.h"
#include <memory>
namespace Stockfish::Eval::NNUE {
// Hash value of evaluation function structure
constexpr std::uint32_t HashValue =
FeatureTransformer::get_hash_value() ^ Network::get_hash_value();
// Deleter for automating release of memory area
template <typename T>
struct AlignedDeleter {
void operator()(T* ptr) const {
ptr->~T();
std_aligned_free(ptr);
}
};
template <typename T>
struct LargePageDeleter {
void operator()(T* ptr) const {
ptr->~T();
aligned_large_pages_free(ptr);
}
};
template <typename T>
using AlignedPtr = std::unique_ptr<T, AlignedDeleter<T>>;
template <typename T>
using LargePagePtr = std::unique_ptr<T, LargePageDeleter<T>>;
std::string trace(Position& pos);
Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr);
void hint_common_parent_position(const Position& pos);
bool load_eval(std::string name, std::istream& stream);
bool save_eval(std::ostream& stream);
bool save_eval(const std::optional<std::string>& filename);
} // namespace Stockfish::Eval::NNUE
#endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED
+22 -20
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,22 +20,23 @@
#include "half_ka_v2_hm.h" #include "half_ka_v2_hm.h"
#include "../../bitboard.h"
#include "../../position.h" #include "../../position.h"
#include "../../types.h"
#include "../nnue_common.h"
namespace Stockfish::Eval::NNUE::Features { namespace Stockfish::Eval::NNUE::Features {
// Index of a feature for a given king position and another piece on some square // Index of a feature for a given king position and another piece on some square
template<Color Perspective> template<Color Perspective>
inline IndexType HalfKAv2_hm::make_index(Square s, Piece pc, Square ksq) { inline IndexType HalfKAv2_hm::make_index(Square s, Piece pc, Square ksq) {
return IndexType((int(s) ^ OrientTBL[Perspective][ksq]) + PieceSquareIndex[Perspective][pc] + KingBuckets[Perspective][ksq]); return IndexType((int(s) ^ OrientTBL[Perspective][ksq]) + PieceSquareIndex[Perspective][pc]
+ KingBuckets[Perspective][ksq]);
} }
// Get a list of indices for active features // Get a list of indices for active features
template<Color Perspective> template<Color Perspective>
void HalfKAv2_hm::append_active_indices( void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active) {
const Position& pos,
IndexList& active
) {
Square ksq = pos.square<KING>(Perspective); Square ksq = pos.square<KING>(Perspective);
Bitboard bb = pos.pieces(); Bitboard bb = pos.pieces();
while (bb) while (bb)
@@ -49,15 +50,14 @@ namespace Stockfish::Eval::NNUE::Features {
template void HalfKAv2_hm::append_active_indices<WHITE>(const Position& pos, IndexList& active); template void HalfKAv2_hm::append_active_indices<WHITE>(const Position& pos, IndexList& active);
template void HalfKAv2_hm::append_active_indices<BLACK>(const Position& pos, IndexList& active); template void HalfKAv2_hm::append_active_indices<BLACK>(const Position& pos, IndexList& active);
// append_changed_indices() : get a list of indices for recently changed features // Get a list of indices for recently changed features
template<Color Perspective> template<Color Perspective>
void HalfKAv2_hm::append_changed_indices( void HalfKAv2_hm::append_changed_indices(Square ksq,
Square ksq,
const DirtyPiece& dp, const DirtyPiece& dp,
IndexList& removed, IndexList& removed,
IndexList& added IndexList& added) {
) { for (int i = 0; i < dp.dirty_num; ++i)
for (int i = 0; i < dp.dirty_num; ++i) { {
if (dp.from[i] != SQ_NONE) if (dp.from[i] != SQ_NONE)
removed.push_back(make_index<Perspective>(dp.from[i], dp.piece[i], ksq)); removed.push_back(make_index<Perspective>(dp.from[i], dp.piece[i], ksq));
if (dp.to[i] != SQ_NONE) if (dp.to[i] != SQ_NONE)
@@ -66,16 +66,18 @@ namespace Stockfish::Eval::NNUE::Features {
} }
// Explicit template instantiations // Explicit template instantiations
template void HalfKAv2_hm::append_changed_indices<WHITE>(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added); template void HalfKAv2_hm::append_changed_indices<WHITE>(Square ksq,
template void HalfKAv2_hm::append_changed_indices<BLACK>(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added); const DirtyPiece& dp,
IndexList& removed,
IndexList& added);
template void HalfKAv2_hm::append_changed_indices<BLACK>(Square ksq,
const DirtyPiece& dp,
IndexList& removed,
IndexList& added);
int HalfKAv2_hm::update_cost(const StateInfo* st) { int HalfKAv2_hm::update_cost(const StateInfo* st) { return st->dirtyPiece.dirty_num; }
return st->dirtyPiece.dirty_num;
}
int HalfKAv2_hm::refresh_cost(const Position& pos) { int HalfKAv2_hm::refresh_cost(const Position& pos) { return pos.count<ALL_PIECES>(); }
return pos.count<ALL_PIECES>();
}
bool HalfKAv2_hm::requires_refresh(const StateInfo* st, Color perspective) { bool HalfKAv2_hm::requires_refresh(const StateInfo* st, Color perspective) {
return st->dirtyPiece.piece[0] == make_piece(perspective, KING); return st->dirtyPiece.piece[0] == make_piece(perspective, KING);
+20 -22
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,22 +21,24 @@
#ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED #ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED
#define NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED #define NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED
#include "../nnue_common.h" #include <cstdint>
#include "../../evaluate.h"
#include "../../misc.h" #include "../../misc.h"
#include "../../types.h"
#include "../nnue_common.h"
namespace Stockfish { namespace Stockfish {
struct StateInfo; struct StateInfo;
class Position;
} }
namespace Stockfish::Eval::NNUE::Features { namespace Stockfish::Eval::NNUE::Features {
// Feature HalfKAv2_hm: Combination of the position of own king // Feature HalfKAv2_hm: Combination of the position of own king and the
// and the position of pieces. Position mirrored such that king always on e..h files. // position of pieces. Position mirrored such that king is always on e..h files.
class HalfKAv2_hm { class HalfKAv2_hm {
// unique number for each piece type on each square // Unique number for each piece type on each square
enum { enum {
PS_NONE = 0, PS_NONE = 0,
PS_W_PAWN = 0, PS_W_PAWN = 0,
@@ -54,13 +56,12 @@ namespace Stockfish::Eval::NNUE::Features {
}; };
static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = { static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = {
// convention: W - us, B - them // Convention: W - us, B - them
// viewed from other side, W and B are reversed // 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_KING, PS_NONE, {PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE,
PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE}, PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE},
{PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE, {PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE,
PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE } PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE}};
};
// Index of a feature for a given king position and another piece on some square // Index of a feature for a given king position and another piece on some square
template<Color Perspective> template<Color Perspective>
@@ -78,6 +79,7 @@ namespace Stockfish::Eval::NNUE::Features {
static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_NB) / 2; static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_NB) / 2;
#define B(v) (v * PS_NB) #define B(v) (v * PS_NB)
// clang-format off
static constexpr int KingBuckets[COLOR_NB][SQUARE_NB] = { static constexpr int KingBuckets[COLOR_NB][SQUARE_NB] = {
{ B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28), { B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28),
B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24), B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24),
@@ -96,8 +98,9 @@ namespace Stockfish::Eval::NNUE::Features {
B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24), B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24),
B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28) } B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28) }
}; };
// clang-format on
#undef B #undef B
// clang-format off
// Orient a square according to perspective (rotates by 180 for black) // Orient a square according to perspective (rotates by 180 for black)
static constexpr int OrientTBL[COLOR_NB][SQUARE_NB] = { static constexpr int OrientTBL[COLOR_NB][SQUARE_NB] = {
{ SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, { SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1,
@@ -117,6 +120,7 @@ namespace Stockfish::Eval::NNUE::Features {
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8,
SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8 } SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8 }
}; };
// clang-format on
// Maximum number of simultaneously active features. // Maximum number of simultaneously active features.
static constexpr IndexType MaxActiveDimensions = 32; static constexpr IndexType MaxActiveDimensions = 32;
@@ -124,26 +128,20 @@ namespace Stockfish::Eval::NNUE::Features {
// Get a list of indices for active features // Get a list of indices for active features
template<Color Perspective> template<Color Perspective>
static void append_active_indices( static void append_active_indices(const Position& pos, IndexList& active);
const Position& pos,
IndexList& active);
// Get a list of indices for recently changed features // Get a list of indices for recently changed features
template<Color Perspective> template<Color Perspective>
static void append_changed_indices( static void
Square ksq, append_changed_indices(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added);
const DirtyPiece& dp,
IndexList& removed,
IndexList& added
);
// Returns the cost of updating one perspective, the most costly one. // Returns the cost of updating one perspective, the most costly one.
// Assumes no refresh needed. // Assumes no refresh needed.
static int update_cost(const StateInfo* st); static int update_cost(const StateInfo* st);
static int refresh_cost(const Position& pos); static int refresh_cost(const Position& pos);
// Returns whether the change stored in this StateInfo means that // Returns whether the change stored in this StateInfo means
// a full accumulator refresh is required. // that a full accumulator refresh is required.
static bool requires_refresh(const StateInfo* st, Color perspective); static bool requires_refresh(const StateInfo* st, Color perspective);
}; };
+80 -335
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,33 +21,18 @@
#ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED
#define NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED #define NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED
#include <cstdint>
#include <iostream> #include <iostream>
#include <algorithm>
#include <type_traits>
#include "../nnue_common.h" #include "../nnue_common.h"
#include "simd.h" #include "simd.h"
/* /*
This file contains the definition for a fully connected layer (aka affine transform). This file contains the definition for a fully connected layer (aka affine transform).
Two approaches are employed, depending on the sizes of the transform.
Approach 1 (a specialization for large inputs):
- used when the PaddedInputDimensions >= 128
- uses AVX512 if possible
- processes inputs in batches of 2*InputSimdWidth
- so in batches of 128 for AVX512
- the weight blocks of size InputSimdWidth are transposed such that
access is sequential
- N columns of the weight matrix are processed a time, where N
depends on the architecture (the amount of registers)
- accumulate + hadd is used
Approach 2 (a specialization for small inputs):
- used when the PaddedInputDimensions < 128
- expected use-case is for when PaddedInputDimensions == 32 and InputDimensions <= 32. - expected use-case is for when PaddedInputDimensions == 32 and InputDimensions <= 32.
- that's why AVX512 is hard to implement - that's why AVX512 is hard to implement
- expected use-case is small layers - expected use-case is small layers
- not optimized as well as the approach 1
- inputs are processed in chunks of 4, weights are respectively transposed - inputs are processed in chunks of 4, weights are respectively transposed
- accumulation happens directly to int32s - accumulation happens directly to int32s
*/ */
@@ -55,22 +40,20 @@
namespace Stockfish::Eval::NNUE::Layers { namespace Stockfish::Eval::NNUE::Layers {
// Fallback implementation for older/other architectures. // Fallback implementation for older/other architectures.
// Identical for both approaches. Requires the input to be padded to at least 16 values. // Requires the input to be padded to at least 16 values.
#if !defined(USE_SSSE3) #if !defined(USE_SSSE3)
template<IndexType InputDimensions, IndexType PaddedInputDimensions, IndexType OutputDimensions> template<IndexType InputDimensions, IndexType PaddedInputDimensions, IndexType OutputDimensions>
static void affine_transform_non_ssse3(std::int32_t* output, const std::int8_t* weights, const std::int32_t* biases, const std::uint8_t* input) static void affine_transform_non_ssse3(std::int32_t* output,
{ const std::int8_t* weights,
const std::int32_t* biases,
const std::uint8_t* input) {
#if defined(USE_SSE2) || defined(USE_NEON_DOTPROD) || defined(USE_NEON)
#if defined(USE_SSE2) #if defined(USE_SSE2)
// At least a multiple of 16, with SSE2. // At least a multiple of 16, with SSE2.
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 16) / 16; constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 16) / 16;
const __m128i Zeros = _mm_setzero_si128(); const __m128i Zeros = _mm_setzero_si128();
const auto inputVector = reinterpret_cast<const __m128i*>(input); const auto inputVector = reinterpret_cast<const __m128i*>(input);
# elif defined(USE_MMX)
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 8) / 8;
const __m64 Zeros = _mm_setzero_si64();
const auto inputVector = reinterpret_cast<const __m64*>(input);
#elif defined(USE_NEON_DOTPROD) #elif defined(USE_NEON_DOTPROD)
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 16) / 16; constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 16) / 16;
const auto inputVector = reinterpret_cast<const int8x16_t*>(input); const auto inputVector = reinterpret_cast<const int8x16_t*>(input);
@@ -80,14 +63,16 @@ namespace Stockfish::Eval::NNUE::Layers {
const auto inputVector = reinterpret_cast<const int8x8_t*>(input); const auto inputVector = reinterpret_cast<const int8x8_t*>(input);
#endif #endif
for (IndexType i = 0; i < OutputDimensions; ++i) { for (IndexType i = 0; i < OutputDimensions; ++i)
{
const IndexType offset = i * PaddedInputDimensions; const IndexType offset = i * PaddedInputDimensions;
#if defined(USE_SSE2) #if defined(USE_SSE2)
__m128i sumLo = _mm_cvtsi32_si128(biases[i]); __m128i sumLo = _mm_cvtsi32_si128(biases[i]);
__m128i sumHi = Zeros; __m128i sumHi = Zeros;
const auto row = reinterpret_cast<const __m128i*>(&weights[offset]); const auto row = reinterpret_cast<const __m128i*>(&weights[offset]);
for (IndexType j = 0; j < NumChunks; ++j) { for (IndexType j = 0; j < NumChunks; ++j)
{
__m128i row_j = _mm_load_si128(&row[j]); __m128i row_j = _mm_load_si128(&row[j]);
__m128i input_j = _mm_load_si128(&inputVector[j]); __m128i input_j = _mm_load_si128(&inputVector[j]);
__m128i extendedRowLo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8); __m128i extendedRowLo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8);
@@ -106,30 +91,11 @@ namespace Stockfish::Eval::NNUE::Layers {
sum = _mm_add_epi32(sum, sum_second_32); sum = _mm_add_epi32(sum, sum_second_32);
output[i] = _mm_cvtsi128_si32(sum); output[i] = _mm_cvtsi128_si32(sum);
# elif defined(USE_MMX)
__m64 sumLo = _mm_cvtsi32_si64(biases[i]);
__m64 sumHi = Zeros;
const auto row = reinterpret_cast<const __m64*>(&weights[offset]);
for (IndexType j = 0; j < NumChunks; ++j) {
__m64 row_j = row[j];
__m64 input_j = inputVector[j];
__m64 extendedRowLo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8);
__m64 extendedRowHi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8);
__m64 extendedInputLo = _mm_unpacklo_pi8(input_j, Zeros);
__m64 extendedInputHi = _mm_unpackhi_pi8(input_j, Zeros);
__m64 productLo = _mm_madd_pi16(extendedRowLo, extendedInputLo);
__m64 productHi = _mm_madd_pi16(extendedRowHi, extendedInputHi);
sumLo = _mm_add_pi32(sumLo, productLo);
sumHi = _mm_add_pi32(sumHi, productHi);
}
__m64 sum = _mm_add_pi32(sumLo, sumHi);
sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum));
output[i] = _mm_cvtsi64_si32(sum);
#elif defined(USE_NEON_DOTPROD) #elif defined(USE_NEON_DOTPROD)
int32x4_t sum = {biases[i]}; int32x4_t sum = {biases[i]};
const auto row = reinterpret_cast<const int8x16_t*>(&weights[offset]); const auto row = reinterpret_cast<const int8x16_t*>(&weights[offset]);
for (IndexType j = 0; j < NumChunks; ++j) { for (IndexType j = 0; j < NumChunks; ++j)
{
sum = vdotq_s32(sum, inputVector[j], row[j]); sum = vdotq_s32(sum, inputVector[j], row[j]);
} }
output[i] = vaddvq_s32(sum); output[i] = vaddvq_s32(sum);
@@ -137,40 +103,34 @@ namespace Stockfish::Eval::NNUE::Layers {
#elif defined(USE_NEON) #elif defined(USE_NEON)
int32x4_t sum = {biases[i]}; int32x4_t sum = {biases[i]};
const auto row = reinterpret_cast<const int8x8_t*>(&weights[offset]); const auto row = reinterpret_cast<const int8x8_t*>(&weights[offset]);
for (IndexType j = 0; j < NumChunks; ++j) { for (IndexType j = 0; j < NumChunks; ++j)
{
int16x8_t product = vmull_s8(inputVector[j * 2], row[j * 2]); int16x8_t product = vmull_s8(inputVector[j * 2], row[j * 2]);
product = vmlal_s8(product, inputVector[j * 2 + 1], row[j * 2 + 1]); product = vmlal_s8(product, inputVector[j * 2 + 1], row[j * 2 + 1]);
sum = vpadalq_s16(sum, product); sum = vpadalq_s16(sum, product);
} }
output[i] = sum[0] + sum[1] + sum[2] + sum[3]; output[i] = sum[0] + sum[1] + sum[2] + sum[3];
#endif
}
#else #else
std::int32_t sum = biases[i]; std::memcpy(output, biases, sizeof(std::int32_t) * OutputDimensions);
for (IndexType j = 0; j < InputDimensions; ++j) {
sum += weights[offset + j] * input[j];
}
output[i] = sum;
# endif
}
# if defined(USE_MMX) // Traverse weights in transpose order to take advantage of input sparsity
_mm_empty(); for (IndexType i = 0; i < InputDimensions; ++i)
if (input[i])
{
const std::int8_t* w = &weights[i];
const int in = input[i];
for (IndexType j = 0; j < OutputDimensions; ++j)
output[j] += w[j * PaddedInputDimensions] * in;
}
#endif #endif
} }
#endif #endif
template <IndexType InDims, IndexType OutDims, typename Enabled = void>
class AffineTransform;
#if defined (USE_AVX512)
constexpr IndexType LargeInputSize = 2 * 64;
#else
constexpr IndexType LargeInputSize = std::numeric_limits<IndexType>::max();
#endif
// A specialization for large inputs
template<IndexType InDims, IndexType OutDims> template<IndexType InDims, IndexType OutDims>
class AffineTransform<InDims, OutDims, std::enable_if_t<(ceil_to_multiple<IndexType>(InDims, MaxSimdWidth) >= LargeInputSize)>> { class AffineTransform {
public: public:
// Input/output type // Input/output type
using InputType = std::uint8_t; using InputType = std::uint8_t;
@@ -187,42 +147,6 @@ namespace Stockfish::Eval::NNUE::Layers {
using OutputBuffer = OutputType[PaddedOutputDimensions]; using OutputBuffer = OutputType[PaddedOutputDimensions];
static_assert(PaddedInputDimensions >= LargeInputSize, "Something went wrong. This specialization (for large inputs) should not have been chosen.");
#if defined (USE_AVX512)
static constexpr IndexType InputSimdWidth = 64;
static constexpr IndexType MaxNumOutputRegs = 16;
#elif defined (USE_AVX2)
static constexpr IndexType InputSimdWidth = 32;
static constexpr IndexType MaxNumOutputRegs = 8;
#elif defined (USE_SSSE3)
static constexpr IndexType InputSimdWidth = 16;
static constexpr IndexType MaxNumOutputRegs = 8;
#elif defined (USE_NEON_DOTPROD)
static constexpr IndexType InputSimdWidth = 16;
static constexpr IndexType MaxNumOutputRegs = 8;
#elif defined (USE_NEON)
static constexpr IndexType InputSimdWidth = 8;
static constexpr IndexType MaxNumOutputRegs = 8;
#else
// The fallback implementation will not have permuted weights.
// We define these to avoid a lot of ifdefs later.
static constexpr IndexType InputSimdWidth = 1;
static constexpr IndexType MaxNumOutputRegs = 1;
#endif
// A big block is a region in the weight matrix of the size [PaddedInputDimensions, NumOutputRegs].
// A small block is a region of size [InputSimdWidth, 1]
static constexpr IndexType NumOutputRegs = std::min(MaxNumOutputRegs, OutputDimensions);
static constexpr IndexType SmallBlockSize = InputSimdWidth;
static constexpr IndexType BigBlockSize = NumOutputRegs * PaddedInputDimensions;
static constexpr IndexType NumSmallBlocksInBigBlock = BigBlockSize / SmallBlockSize;
static constexpr IndexType NumSmallBlocksPerOutput = PaddedInputDimensions / SmallBlockSize;
static constexpr IndexType NumBigBlocks = OutputDimensions / NumOutputRegs;
static_assert(OutputDimensions % NumOutputRegs == 0);
// Hash value embedded in the evaluation file // Hash value embedded in the evaluation file
static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) {
std::uint32_t hashValue = 0xCC03DAE4u; std::uint32_t hashValue = 0xCC03DAE4u;
@@ -232,210 +156,12 @@ namespace Stockfish::Eval::NNUE::Layers {
return hashValue; return hashValue;
} }
/* static constexpr IndexType get_weight_index_scrambled(IndexType i) {
Transposes the small blocks within a block. return (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4
Effectively means that weights can be traversed sequentially during inference. + i / PaddedInputDimensions * 4 + i % 4;
*/
static IndexType get_weight_index(IndexType i)
{
const IndexType smallBlock = (i / SmallBlockSize) % NumSmallBlocksInBigBlock;
const IndexType smallBlockCol = smallBlock / NumSmallBlocksPerOutput;
const IndexType smallBlockRow = smallBlock % NumSmallBlocksPerOutput;
const IndexType bigBlock = i / BigBlockSize;
const IndexType rest = i % SmallBlockSize;
const IndexType idx =
bigBlock * BigBlockSize
+ smallBlockRow * SmallBlockSize * NumOutputRegs
+ smallBlockCol * SmallBlockSize
+ rest;
return idx;
} }
// Read network parameters static constexpr IndexType get_weight_index(IndexType i) {
bool read_parameters(std::istream& stream) {
read_little_endian<BiasType>(stream, biases, OutputDimensions);
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
weights[get_weight_index(i)] = read_little_endian<WeightType>(stream);
return !stream.fail();
}
// Write network parameters
bool write_parameters(std::ostream& stream) const {
write_little_endian<BiasType>(stream, biases, OutputDimensions);
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
write_little_endian<WeightType>(stream, weights[get_weight_index(i)]);
return !stream.fail();
}
// Forward propagation
const OutputType* propagate(
const InputType* input, OutputType* output) const {
#if defined (USE_AVX512)
using acc_vec_t = __m512i;
using bias_vec_t = __m128i;
using weight_vec_t = __m512i;
using in_vec_t = __m512i;
#define vec_zero _mm512_setzero_si512()
#define vec_add_dpbusd_32x2 Simd::m512_add_dpbusd_epi32x2
#define vec_hadd Simd::m512_hadd
#define vec_haddx4 Simd::m512_haddx4
#elif defined (USE_AVX2)
using acc_vec_t = __m256i;
using bias_vec_t = __m128i;
using weight_vec_t = __m256i;
using in_vec_t = __m256i;
#define vec_zero _mm256_setzero_si256()
#define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2
#define vec_hadd Simd::m256_hadd
#define vec_haddx4 Simd::m256_haddx4
#elif defined (USE_SSSE3)
using acc_vec_t = __m128i;
using bias_vec_t = __m128i;
using weight_vec_t = __m128i;
using in_vec_t = __m128i;
#define vec_zero _mm_setzero_si128()
#define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2
#define vec_hadd Simd::m128_hadd
#define vec_haddx4 Simd::m128_haddx4
#elif defined (USE_NEON_DOTPROD)
using acc_vec_t = int32x4_t;
using bias_vec_t = int32x4_t;
using weight_vec_t = int8x16_t;
using in_vec_t = int8x16_t;
#define vec_zero {0}
#define vec_add_dpbusd_32x2 Simd::dotprod_m128_add_dpbusd_epi32x2
#define vec_hadd Simd::neon_m128_hadd
#define vec_haddx4 Simd::neon_m128_haddx4
#elif defined (USE_NEON)
using acc_vec_t = int32x4_t;
using bias_vec_t = int32x4_t;
using weight_vec_t = int8x8_t;
using in_vec_t = int8x8_t;
#define vec_zero {0}
#define vec_add_dpbusd_32x2 Simd::neon_m128_add_dpbusd_epi32x2
#define vec_hadd Simd::neon_m128_hadd
#define vec_haddx4 Simd::neon_m128_haddx4
#endif
#if defined (USE_SSSE3) || defined (USE_NEON)
const in_vec_t* invec = reinterpret_cast<const in_vec_t*>(input);
// Perform accumulation to registers for each big block
for (IndexType bigBlock = 0; bigBlock < NumBigBlocks; ++bigBlock)
{
acc_vec_t acc[NumOutputRegs] = { vec_zero };
// Each big block has NumOutputRegs small blocks in each "row", one per register.
// We process two small blocks at a time to save on one addition without VNNI.
for (IndexType smallBlock = 0; smallBlock < NumSmallBlocksPerOutput; smallBlock += 2)
{
const weight_vec_t* weightvec =
reinterpret_cast<const weight_vec_t*>(
weights
+ bigBlock * BigBlockSize
+ smallBlock * SmallBlockSize * NumOutputRegs);
const in_vec_t in0 = invec[smallBlock + 0];
const in_vec_t in1 = invec[smallBlock + 1];
for (IndexType k = 0; k < NumOutputRegs; ++k)
vec_add_dpbusd_32x2(acc[k], in0, weightvec[k], in1, weightvec[k + NumOutputRegs]);
}
// Horizontally add all accumulators.
if constexpr (NumOutputRegs % 4 == 0)
{
bias_vec_t* outputvec = reinterpret_cast<bias_vec_t*>(output);
const bias_vec_t* biasvec = reinterpret_cast<const bias_vec_t*>(biases);
for (IndexType k = 0; k < NumOutputRegs; k += 4)
{
const IndexType idx = (bigBlock * NumOutputRegs + k) / 4;
outputvec[idx] = vec_haddx4(acc[k+0], acc[k+1], acc[k+2], acc[k+3], biasvec[idx]);
}
}
else
{
for (IndexType k = 0; k < NumOutputRegs; ++k)
{
const IndexType idx = (bigBlock * NumOutputRegs + k);
output[idx] = vec_hadd(acc[k], biases[idx]);
}
}
}
# undef vec_zero
# undef vec_add_dpbusd_32x2
# undef vec_hadd
# undef vec_haddx4
#else
// Use old implementation for the other architectures.
affine_transform_non_ssse3<
InputDimensions,
PaddedInputDimensions,
OutputDimensions>(output, weights, biases, input);
#endif
return output;
}
private:
using BiasType = OutputType;
using WeightType = std::int8_t;
alignas(CacheLineSize) BiasType biases[OutputDimensions];
alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions];
};
// A specialization for small inputs
template <IndexType InDims, IndexType OutDims>
class AffineTransform<InDims, OutDims, std::enable_if_t<(ceil_to_multiple<IndexType>(InDims, MaxSimdWidth) < LargeInputSize)>> {
public:
// Input/output type
// Input/output type
using InputType = std::uint8_t;
using OutputType = std::int32_t;
// Number of input/output dimensions
static constexpr IndexType InputDimensions = InDims;
static constexpr IndexType OutputDimensions = OutDims;
static constexpr IndexType PaddedInputDimensions =
ceil_to_multiple<IndexType>(InputDimensions, MaxSimdWidth);
static constexpr IndexType PaddedOutputDimensions =
ceil_to_multiple<IndexType>(OutputDimensions, MaxSimdWidth);
using OutputBuffer = OutputType[PaddedOutputDimensions];
static_assert(PaddedInputDimensions < LargeInputSize, "Something went wrong. This specialization (for small inputs) should not have been chosen.");
// Hash value embedded in the evaluation file
static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) {
std::uint32_t hashValue = 0xCC03DAE4u;
hashValue += OutputDimensions;
hashValue ^= prevHash >> 1;
hashValue ^= prevHash << 31;
return hashValue;
}
static IndexType get_weight_index_scrambled(IndexType i)
{
return
(i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 +
i / PaddedInputDimensions * 4 +
i % 4;
}
static IndexType get_weight_index(IndexType i)
{
#if defined(USE_SSSE3) #if defined(USE_SSSE3)
return get_weight_index_scrambled(i); return get_weight_index_scrambled(i);
#else #else
@@ -462,41 +188,37 @@ namespace Stockfish::Eval::NNUE::Layers {
return !stream.fail(); return !stream.fail();
} }
// Forward propagation // Forward propagation
const OutputType* propagate( void propagate(const InputType* input, OutputType* output) const {
const InputType* input, OutputType* output) const {
#if defined(USE_SSSE3)
if constexpr (OutputDimensions > 1)
{
#if defined(USE_AVX512) #if defined(USE_AVX512)
using vec_t = __m512i; using vec_t = __m512i;
#define vec_setzero _mm512_setzero_si512 #define vec_setzero _mm512_setzero_si512
#define vec_set_32 _mm512_set1_epi32 #define vec_set_32 _mm512_set1_epi32
#define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32
#define vec_add_dpbusd_32x2 Simd::m512_add_dpbusd_epi32x2
#define vec_hadd Simd::m512_hadd #define vec_hadd Simd::m512_hadd
#elif defined(USE_AVX2) #elif defined(USE_AVX2)
using vec_t = __m256i; using vec_t = __m256i;
#define vec_setzero _mm256_setzero_si256 #define vec_setzero _mm256_setzero_si256
#define vec_set_32 _mm256_set1_epi32 #define vec_set_32 _mm256_set1_epi32
#define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32
#define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2
#define vec_hadd Simd::m256_hadd #define vec_hadd Simd::m256_hadd
#elif defined(USE_SSSE3) #elif defined(USE_SSSE3)
using vec_t = __m128i; using vec_t = __m128i;
#define vec_setzero _mm_setzero_si128 #define vec_setzero _mm_setzero_si128
#define vec_set_32 _mm_set1_epi32 #define vec_set_32 _mm_set1_epi32
#define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32
#define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2
#define vec_hadd Simd::m128_hadd #define vec_hadd Simd::m128_hadd
#endif #endif
#if defined (USE_SSSE3)
const auto inputVector = reinterpret_cast<const vec_t*>(input);
static constexpr IndexType OutputSimdWidth = sizeof(vec_t) / sizeof(OutputType); static constexpr IndexType OutputSimdWidth = sizeof(vec_t) / sizeof(OutputType);
static_assert(OutputDimensions % OutputSimdWidth == 0 || OutputDimensions == 1); static_assert(OutputDimensions % OutputSimdWidth == 0);
if constexpr (OutputDimensions % OutputSimdWidth == 0)
{
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 8) / 4; constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 8) / 4;
constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth;
@@ -506,48 +228,71 @@ namespace Stockfish::Eval::NNUE::Layers {
for (IndexType k = 0; k < NumRegs; ++k) for (IndexType k = 0; k < NumRegs; ++k)
acc[k] = biasvec[k]; acc[k] = biasvec[k];
for (IndexType i = 0; i < NumChunks; i += 2) for (IndexType i = 0; i < NumChunks; ++i)
{ {
const vec_t in0 = vec_set_32(input32[i + 0]); const vec_t in0 = vec_set_32(input32[i]);
const vec_t in1 = vec_set_32(input32[i + 1]); const auto col0 =
const auto col0 = reinterpret_cast<const vec_t*>(&weights[(i + 0) * OutputDimensions * 4]); reinterpret_cast<const vec_t*>(&weights[i * OutputDimensions * 4]);
const auto col1 = reinterpret_cast<const vec_t*>(&weights[(i + 1) * OutputDimensions * 4]);
for (IndexType k = 0; k < NumRegs; ++k) for (IndexType k = 0; k < NumRegs; ++k)
vec_add_dpbusd_32x2(acc[k], in0, col0[k], in1, col1[k]); vec_add_dpbusd_32(acc[k], in0, col0[k]);
} }
vec_t* outptr = reinterpret_cast<vec_t*>(output); vec_t* outptr = reinterpret_cast<vec_t*>(output);
for (IndexType k = 0; k < NumRegs; ++k) for (IndexType k = 0; k < NumRegs; ++k)
outptr[k] = acc[k]; outptr[k] = acc[k];
#undef vec_setzero
#undef vec_set_32
#undef vec_add_dpbusd_32
#undef vec_hadd
} }
else if constexpr (OutputDimensions == 1) else if constexpr (OutputDimensions == 1)
{ {
constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth;
// We cannot use AVX512 for the last layer because there are only 32 inputs
// and the buffer is not padded to 64 elements.
#if defined(USE_AVX2)
using vec_t = __m256i;
#define vec_setzero _mm256_setzero_si256
#define vec_set_32 _mm256_set1_epi32
#define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32
#define vec_hadd Simd::m256_hadd
#elif defined(USE_SSSE3)
using vec_t = __m128i;
#define vec_setzero _mm_setzero_si128
#define vec_set_32 _mm_set1_epi32
#define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32
#define vec_hadd Simd::m128_hadd
#endif
const auto inputVector = reinterpret_cast<const vec_t*>(input);
static constexpr IndexType InputSimdWidth = sizeof(vec_t) / sizeof(InputType);
static_assert(PaddedInputDimensions % InputSimdWidth == 0);
constexpr IndexType NumChunks = PaddedInputDimensions / InputSimdWidth;
vec_t sum0 = vec_setzero(); vec_t sum0 = vec_setzero();
const auto row0 = reinterpret_cast<const vec_t*>(&weights[0]); const auto row0 = reinterpret_cast<const vec_t*>(&weights[0]);
for (int j = 0; j < (int)NumChunks; ++j) for (int j = 0; j < int(NumChunks); ++j)
{ {
const vec_t in = inputVector[j]; const vec_t in = inputVector[j];
vec_add_dpbusd_32(sum0, in, row0[j]); vec_add_dpbusd_32(sum0, in, row0[j]);
} }
output[0] = vec_hadd(sum0, biases[0]); output[0] = vec_hadd(sum0, biases[0]);
}
#undef vec_setzero #undef vec_setzero
#undef vec_set_32 #undef vec_set_32
#undef vec_add_dpbusd_32 #undef vec_add_dpbusd_32
# undef vec_add_dpbusd_32x2
#undef vec_hadd #undef vec_hadd
}
#else #else
// Use old implementation for the other architectures. // Use old implementation for the other architectures.
affine_transform_non_ssse3< affine_transform_non_ssse3<InputDimensions, PaddedInputDimensions, OutputDimensions>(
InputDimensions, output, weights, biases, input);
PaddedInputDimensions,
OutputDimensions>(output, weights, biases, input);
#endif #endif
return output;
} }
private: private:
+87 -95
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,10 +21,12 @@
#ifndef NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED
#define NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED #define NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED
#include <iostream>
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <type_traits> #include <cstdint>
#include <iostream>
#include "../../bitboard.h"
#include "../nnue_common.h" #include "../nnue_common.h"
#include "affine_transform.h" #include "affine_transform.h"
#include "simd.h" #include "simd.h"
@@ -34,58 +36,16 @@
*/ */
namespace Stockfish::Eval::NNUE::Layers { namespace Stockfish::Eval::NNUE::Layers {
#if defined(__GNUC__) // GCC, Clang, ICC
static inline IndexType lsb_(std::uint32_t b) { #if (USE_SSSE3 | (USE_NEON >= 8))
assert(b); alignas(CacheLineSize) static inline const
return IndexType(__builtin_ctzl(b)); std::array<std::array<std::uint16_t, 8>, 256> lookup_indices = []() {
}
#elif defined(_MSC_VER) // MSVC
static inline IndexType lsb_(std::uint32_t b) {
assert(b);
unsigned long idx;
_BitScanForward(&idx, b);
return (IndexType) idx;
}
#else // Compiler is neither GCC nor MSVC compatible
#error "Compiler not supported."
#endif
#if defined(USE_SSSE3)
alignas(CacheLineSize) static inline const std::array<std::array<std::uint16_t, 8>, 256> lookup_indices = [](){
std::array<std::array<std::uint16_t, 8>, 256> v{}; std::array<std::array<std::uint16_t, 8>, 256> v{};
for (int i = 0; i < 256; ++i) for (unsigned i = 0; i < 256; ++i)
{ {
int j = i; std::uint64_t j = i, k = 0;
int k = 0;
while (j) while (j)
{ v[i][k++] = pop_lsb(j);
const IndexType lsbIndex = lsb_(std::uint32_t(j));
j &= j - 1;
v[i][k] = lsbIndex;
++k;
}
}
return v;
}();
alignas(CacheLineSize) static inline const std::array<unsigned, 256> lookup_count = [](){
std::array<unsigned, 256> v;
for (int i = 0; i < 256; ++i)
{
int j = i;
int k = 0;
while(j)
{
j &= j - 1;
++k;
}
v[i] = k;
} }
return v; return v;
}(); }();
@@ -93,15 +53,40 @@ namespace Stockfish::Eval::NNUE::Layers {
// Find indices of nonzero numbers in an int32_t array // Find indices of nonzero numbers in an int32_t array
template<const IndexType InputDimensions> template<const IndexType InputDimensions>
void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_out) { void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_out) {
#if defined(USE_SSSE3)
#if defined(USE_AVX512) #if defined(USE_AVX512)
using vec_t = __m512i; using vec_t = __m512i;
#define vec_nnz(a) _mm512_cmpgt_epi32_mask(a, _mm512_setzero_si512()) #define vec_nnz(a) _mm512_cmpgt_epi32_mask(a, _mm512_setzero_si512())
#elif defined(USE_AVX2) #elif defined(USE_AVX2)
using vec_t = __m256i; using vec_t = __m256i;
#define vec_nnz(a) _mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(a, _mm256_setzero_si256()))) #if defined(USE_VNNI) && !defined(USE_AVXVNNI)
#define vec_nnz(a) _mm256_cmpgt_epi32_mask(a, _mm256_setzero_si256())
#else
#define vec_nnz(a) \
_mm256_movemask_ps( \
_mm256_castsi256_ps(_mm256_cmpgt_epi32(a, _mm256_setzero_si256())))
#endif
#elif defined(USE_SSSE3) #elif defined(USE_SSSE3)
using vec_t = __m128i; using vec_t = __m128i;
#define vec_nnz(a) _mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a, _mm_setzero_si128()))) #define vec_nnz(a) \
_mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a, _mm_setzero_si128())))
#endif
using vec128_t = __m128i;
#define vec128_zero _mm_setzero_si128()
#define vec128_set_16(a) _mm_set1_epi16(a)
#define vec128_load(a) _mm_load_si128(a)
#define vec128_storeu(a, b) _mm_storeu_si128(a, b)
#define vec128_add(a, b) _mm_add_epi16(a, b)
#elif defined(USE_NEON)
using vec_t = uint32x4_t;
static const std::uint32_t Mask[4] = {1, 2, 4, 8};
#define vec_nnz(a) vaddvq_u32(vandq_u32(vtstq_u32(a, a), vld1q_u32(Mask)))
using vec128_t = uint16x8_t;
#define vec128_zero vdupq_n_u16(0)
#define vec128_set_16(a) vdupq_n_u16(a)
#define vec128_load(a) vld1q_u16(reinterpret_cast<const std::uint16_t*>(a))
#define vec128_storeu(a, b) vst1q_u16(reinterpret_cast<std::uint16_t*>(a), b)
#define vec128_add(a, b) vaddq_u16(a, b)
#endif #endif
constexpr IndexType InputSimdWidth = sizeof(vec_t) / sizeof(std::int32_t); constexpr IndexType InputSimdWidth = sizeof(vec_t) / sizeof(std::int32_t);
// Inputs are processed InputSimdWidth at a time and outputs are processed 8 at a time so we process in chunks of max(InputSimdWidth, 8) // Inputs are processed InputSimdWidth at a time and outputs are processed 8 at a time so we process in chunks of max(InputSimdWidth, 8)
@@ -112,8 +97,8 @@ namespace Stockfish::Eval::NNUE::Layers {
const auto inputVector = reinterpret_cast<const vec_t*>(input); const auto inputVector = reinterpret_cast<const vec_t*>(input);
IndexType count = 0; IndexType count = 0;
__m128i base = _mm_set1_epi16(0); vec128_t base = vec128_zero;
__m128i increment = _mm_set1_epi16(8); const vec128_t increment = vec128_set_16(8);
for (IndexType i = 0; i < NumChunks; ++i) for (IndexType i = 0; i < NumChunks; ++i)
{ {
// bitmask of nonzero values in this chunk // bitmask of nonzero values in this chunk
@@ -121,27 +106,32 @@ namespace Stockfish::Eval::NNUE::Layers {
for (IndexType j = 0; j < InputsPerChunk; ++j) for (IndexType j = 0; j < InputsPerChunk; ++j)
{ {
const vec_t inputChunk = inputVector[i * InputsPerChunk + j]; const vec_t inputChunk = inputVector[i * InputsPerChunk + j];
nnz |= (unsigned)vec_nnz(inputChunk) << (j * InputSimdWidth); nnz |= unsigned(vec_nnz(inputChunk)) << (j * InputSimdWidth);
} }
for (IndexType j = 0; j < OutputsPerChunk; ++j) for (IndexType j = 0; j < OutputsPerChunk; ++j)
{ {
const auto lookup = (nnz >> (j * 8)) & 0xFF; const auto lookup = (nnz >> (j * 8)) & 0xFF;
const auto offsets = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&lookup_indices[lookup])); const auto offsets =
_mm_storeu_si128(reinterpret_cast<__m128i*>(out + count), _mm_add_epi16(base, offsets)); vec128_load(reinterpret_cast<const vec128_t*>(&lookup_indices[lookup]));
count += lookup_count[lookup]; vec128_storeu(reinterpret_cast<vec128_t*>(out + count), vec128_add(base, offsets));
base = _mm_add_epi16(base, increment); count += popcount(lookup);
base = vec128_add(base, increment);
} }
} }
count_out = count; count_out = count;
} }
#undef vec_nnz #undef vec_nnz
#undef vec128_zero
#undef vec128_set_16
#undef vec128_load
#undef vec128_storeu
#undef vec128_add
#endif #endif
// Sparse input implementation // Sparse input implementation
template<IndexType InDims, IndexType OutDims> template<IndexType InDims, IndexType OutDims>
class AffineTransformSparseInput { class AffineTransformSparseInput {
public: public:
// Input/output type
// Input/output type // Input/output type
using InputType = std::uint8_t; using InputType = std::uint8_t;
using OutputType = std::int32_t; using OutputType = std::int32_t;
@@ -150,14 +140,15 @@ namespace Stockfish::Eval::NNUE::Layers {
static constexpr IndexType InputDimensions = InDims; static constexpr IndexType InputDimensions = InDims;
static constexpr IndexType OutputDimensions = OutDims; static constexpr IndexType OutputDimensions = OutDims;
static_assert(OutputDimensions % 16 == 0, "Only implemented for OutputDimensions divisible by 16."); static_assert(OutputDimensions % 16 == 0,
"Only implemented for OutputDimensions divisible by 16.");
static constexpr IndexType PaddedInputDimensions = static constexpr IndexType PaddedInputDimensions =
ceil_to_multiple<IndexType>(InputDimensions, MaxSimdWidth); ceil_to_multiple<IndexType>(InputDimensions, MaxSimdWidth);
static constexpr IndexType PaddedOutputDimensions = static constexpr IndexType PaddedOutputDimensions =
ceil_to_multiple<IndexType>(OutputDimensions, MaxSimdWidth); ceil_to_multiple<IndexType>(OutputDimensions, MaxSimdWidth);
#if defined (USE_SSSE3) #if (USE_SSSE3 | (USE_NEON >= 8))
static constexpr IndexType ChunkSize = 4; static constexpr IndexType ChunkSize = 4;
#else #else
static constexpr IndexType ChunkSize = 1; static constexpr IndexType ChunkSize = 1;
@@ -174,17 +165,13 @@ namespace Stockfish::Eval::NNUE::Layers {
return hashValue; return hashValue;
} }
static IndexType get_weight_index_scrambled(IndexType i) static constexpr IndexType get_weight_index_scrambled(IndexType i) {
{ return (i / ChunkSize) % (PaddedInputDimensions / ChunkSize) * OutputDimensions * ChunkSize
return + i / PaddedInputDimensions * ChunkSize + i % ChunkSize;
(i / ChunkSize) % (PaddedInputDimensions / ChunkSize) * OutputDimensions * ChunkSize +
i / PaddedInputDimensions * ChunkSize +
i % ChunkSize;
} }
static IndexType get_weight_index(IndexType i) static constexpr IndexType get_weight_index(IndexType i) {
{ #if (USE_SSSE3 | (USE_NEON >= 8))
#if defined (USE_SSSE3)
return get_weight_index_scrambled(i); return get_weight_index_scrambled(i);
#else #else
return i; return i;
@@ -210,27 +197,36 @@ namespace Stockfish::Eval::NNUE::Layers {
return !stream.fail(); return !stream.fail();
} }
// Forward propagation // Forward propagation
const OutputType* propagate( void propagate(const InputType* input, OutputType* output) const {
const InputType* input, OutputType* output) const {
#if defined (USE_SSSE3) #if (USE_SSSE3 | (USE_NEON >= 8))
#if defined(USE_AVX512) #if defined(USE_AVX512)
using vec_t = __m512i; using invec_t = __m512i;
#define vec_setzero _mm512_setzero_si512 using outvec_t = __m512i;
#define vec_set_32 _mm512_set1_epi32 #define vec_set_32 _mm512_set1_epi32
#define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32
#elif defined(USE_AVX2) #elif defined(USE_AVX2)
using vec_t = __m256i; using invec_t = __m256i;
#define vec_setzero _mm256_setzero_si256 using outvec_t = __m256i;
#define vec_set_32 _mm256_set1_epi32 #define vec_set_32 _mm256_set1_epi32
#define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32
#elif defined(USE_SSSE3) #elif defined(USE_SSSE3)
using vec_t = __m128i; using invec_t = __m128i;
#define vec_setzero _mm_setzero_si128 using outvec_t = __m128i;
#define vec_set_32 _mm_set1_epi32 #define vec_set_32 _mm_set1_epi32
#define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32
#elif defined(USE_NEON_DOTPROD)
using invec_t = int8x16_t;
using outvec_t = int32x4_t;
#define vec_set_32(a) vreinterpretq_s8_u32(vdupq_n_u32(a))
#define vec_add_dpbusd_32 Simd::dotprod_m128_add_dpbusd_epi32
#elif defined(USE_NEON)
using invec_t = int8x16_t;
using outvec_t = int32x4_t;
#define vec_set_32(a) vreinterpretq_s8_u32(vdupq_n_u32(a))
#define vec_add_dpbusd_32 Simd::neon_m128_add_dpbusd_epi32
#endif #endif
static constexpr IndexType OutputSimdWidth = sizeof(vec_t) / sizeof(OutputType); static constexpr IndexType OutputSimdWidth = sizeof(outvec_t) / sizeof(OutputType);
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 8) / ChunkSize; constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 8) / ChunkSize;
constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth;
@@ -239,38 +235,34 @@ namespace Stockfish::Eval::NNUE::Layers {
const auto input32 = reinterpret_cast<const std::int32_t*>(input); const auto input32 = reinterpret_cast<const std::int32_t*>(input);
// Find indices of nonzero 32bit blocks // Find indices of nonzero 32-bit blocks
find_nnz<NumChunks>(input32, nnz, count); find_nnz<NumChunks>(input32, nnz, count);
const vec_t* biasvec = reinterpret_cast<const vec_t*>(biases); const outvec_t* biasvec = reinterpret_cast<const outvec_t*>(biases);
vec_t acc[NumRegs]; outvec_t acc[NumRegs];
for (IndexType k = 0; k < NumRegs; ++k) for (IndexType k = 0; k < NumRegs; ++k)
acc[k] = biasvec[k]; acc[k] = biasvec[k];
for (IndexType j = 0; j < count; ++j) for (IndexType j = 0; j < count; ++j)
{ {
const auto i = nnz[j]; const auto i = nnz[j];
const vec_t in = vec_set_32(input32[i]); const invec_t in = vec_set_32(input32[i]);
const auto col = reinterpret_cast<const vec_t*>(&weights[i * OutputDimensions * ChunkSize]); const auto col =
reinterpret_cast<const invec_t*>(&weights[i * OutputDimensions * ChunkSize]);
for (IndexType k = 0; k < NumRegs; ++k) for (IndexType k = 0; k < NumRegs; ++k)
vec_add_dpbusd_32(acc[k], in, col[k]); vec_add_dpbusd_32(acc[k], in, col[k]);
} }
vec_t* outptr = reinterpret_cast<vec_t*>(output); outvec_t* outptr = reinterpret_cast<outvec_t*>(output);
for (IndexType k = 0; k < NumRegs; ++k) for (IndexType k = 0; k < NumRegs; ++k)
outptr[k] = acc[k]; outptr[k] = acc[k];
# undef vec_setzero
#undef vec_set_32 #undef vec_set_32
#undef vec_add_dpbusd_32 #undef vec_add_dpbusd_32
#else #else
// Use dense implementation for the other architectures. // Use dense implementation for the other architectures.
affine_transform_non_ssse3< affine_transform_non_ssse3<InputDimensions, PaddedInputDimensions, OutputDimensions>(
InputDimensions, output, weights, biases, input);
PaddedInputDimensions,
OutputDimensions>(output, weights, biases, input);
#endif #endif
return output;
} }
private: private:
+48 -60
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,6 +21,10 @@
#ifndef NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED #ifndef NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED
#define NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED #define NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED
#include <algorithm>
#include <cstdint>
#include <iosfwd>
#include "../nnue_common.h" #include "../nnue_common.h"
namespace Stockfish::Eval::NNUE::Layers { namespace Stockfish::Eval::NNUE::Layers {
@@ -49,54 +53,56 @@ namespace Stockfish::Eval::NNUE::Layers {
} }
// Read network parameters // Read network parameters
bool read_parameters(std::istream&) { bool read_parameters(std::istream&) { return true; }
return true;
}
// Write network parameters // Write network parameters
bool write_parameters(std::ostream&) const { bool write_parameters(std::ostream&) const { return true; }
return true;
}
// Forward propagation // Forward propagation
const OutputType* propagate( void propagate(const InputType* input, OutputType* output) const {
const InputType* input, OutputType* output) const {
#if defined(USE_AVX2) #if defined(USE_AVX2)
if constexpr (InputDimensions % SimdWidth == 0) { if constexpr (InputDimensions % SimdWidth == 0)
{
constexpr IndexType NumChunks = InputDimensions / SimdWidth; constexpr IndexType NumChunks = InputDimensions / SimdWidth;
const __m256i Zero = _mm256_setzero_si256(); const __m256i Zero = _mm256_setzero_si256();
const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0);
const auto in = reinterpret_cast<const __m256i*>(input); const auto in = reinterpret_cast<const __m256i*>(input);
const auto out = reinterpret_cast<__m256i*>(output); const auto out = reinterpret_cast<__m256i*>(output);
for (IndexType i = 0; i < NumChunks; ++i) { for (IndexType i = 0; i < NumChunks; ++i)
const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( {
_mm256_load_si256(&in[i * 4 + 0]), const __m256i words0 =
_mm256_load_si256(&in[i * 4 + 1])), WeightScaleBits); _mm256_srai_epi16(_mm256_packs_epi32(_mm256_load_si256(&in[i * 4 + 0]),
const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( _mm256_load_si256(&in[i * 4 + 1])),
_mm256_load_si256(&in[i * 4 + 2]), WeightScaleBits);
_mm256_load_si256(&in[i * 4 + 3])), WeightScaleBits); const __m256i words1 =
_mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( _mm256_srai_epi16(_mm256_packs_epi32(_mm256_load_si256(&in[i * 4 + 2]),
_mm256_packs_epi16(words0, words1), Zero), Offsets)); _mm256_load_si256(&in[i * 4 + 3])),
WeightScaleBits);
_mm256_store_si256(
&out[i], _mm256_permutevar8x32_epi32(
_mm256_max_epi8(_mm256_packs_epi16(words0, words1), Zero), Offsets));
} }
} else { }
else
{
constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2);
const __m128i Zero = _mm_setzero_si128(); const __m128i Zero = _mm_setzero_si128();
const auto in = reinterpret_cast<const __m128i*>(input); const auto in = reinterpret_cast<const __m128i*>(input);
const auto out = reinterpret_cast<__m128i*>(output); const auto out = reinterpret_cast<__m128i*>(output);
for (IndexType i = 0; i < NumChunks; ++i) { for (IndexType i = 0; i < NumChunks; ++i)
const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32( {
_mm_load_si128(&in[i * 4 + 0]), const __m128i words0 = _mm_srai_epi16(
_mm_load_si128(&in[i * 4 + 1])), WeightScaleBits); _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1])),
const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32( WeightScaleBits);
_mm_load_si128(&in[i * 4 + 2]), const __m128i words1 = _mm_srai_epi16(
_mm_load_si128(&in[i * 4 + 3])), WeightScaleBits); _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])),
WeightScaleBits);
const __m128i packedbytes = _mm_packs_epi16(words0, words1); const __m128i packedbytes = _mm_packs_epi16(words0, words1);
_mm_store_si128(&out[i], _mm_max_epi8(packedbytes, Zero)); _mm_store_si128(&out[i], _mm_max_epi8(packedbytes, Zero));
} }
} }
constexpr IndexType Start = constexpr IndexType Start = InputDimensions % SimdWidth == 0
InputDimensions % SimdWidth == 0
? InputDimensions / SimdWidth * SimdWidth ? InputDimensions / SimdWidth * SimdWidth
: InputDimensions / (SimdWidth / 2) * (SimdWidth / 2); : InputDimensions / (SimdWidth / 2) * (SimdWidth / 2);
@@ -111,13 +117,14 @@ namespace Stockfish::Eval::NNUE::Layers {
const auto in = reinterpret_cast<const __m128i*>(input); const auto in = reinterpret_cast<const __m128i*>(input);
const auto out = reinterpret_cast<__m128i*>(output); const auto out = reinterpret_cast<__m128i*>(output);
for (IndexType i = 0; i < NumChunks; ++i) { for (IndexType i = 0; i < NumChunks; ++i)
const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32( {
_mm_load_si128(&in[i * 4 + 0]), const __m128i words0 = _mm_srai_epi16(
_mm_load_si128(&in[i * 4 + 1])), WeightScaleBits); _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1])),
const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32( WeightScaleBits);
_mm_load_si128(&in[i * 4 + 2]), const __m128i words1 = _mm_srai_epi16(
_mm_load_si128(&in[i * 4 + 3])), WeightScaleBits); _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])),
WeightScaleBits);
const __m128i packedbytes = _mm_packs_epi16(words0, words1); const __m128i packedbytes = _mm_packs_epi16(words0, words1);
_mm_store_si128(&out[i], _mm_store_si128(&out[i],
@@ -131,30 +138,13 @@ namespace Stockfish::Eval::NNUE::Layers {
} }
constexpr IndexType Start = NumChunks * SimdWidth; constexpr IndexType Start = NumChunks * SimdWidth;
#elif defined(USE_MMX)
constexpr IndexType NumChunks = InputDimensions / SimdWidth;
const __m64 k0x80s = _mm_set1_pi8(-128);
const auto in = reinterpret_cast<const __m64*>(input);
const auto out = reinterpret_cast<__m64*>(output);
for (IndexType i = 0; i < NumChunks; ++i) {
const __m64 words0 = _mm_srai_pi16(
_mm_packs_pi32(in[i * 4 + 0], in[i * 4 + 1]),
WeightScaleBits);
const __m64 words1 = _mm_srai_pi16(
_mm_packs_pi32(in[i * 4 + 2], in[i * 4 + 3]),
WeightScaleBits);
const __m64 packedbytes = _mm_packs_pi16(words0, words1);
out[i] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s);
}
_mm_empty();
constexpr IndexType Start = NumChunks * SimdWidth;
#elif defined(USE_NEON) #elif defined(USE_NEON)
constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2);
const int8x8_t Zero = {0}; const int8x8_t Zero = {0};
const auto in = reinterpret_cast<const int32x4_t*>(input); const auto in = reinterpret_cast<const int32x4_t*>(input);
const auto out = reinterpret_cast<int8x8_t*>(output); const auto out = reinterpret_cast<int8x8_t*>(output);
for (IndexType i = 0; i < NumChunks; ++i) { for (IndexType i = 0; i < NumChunks; ++i)
{
int16x8_t shifted; int16x8_t shifted;
const auto pack = reinterpret_cast<int16x4_t*>(&shifted); const auto pack = reinterpret_cast<int16x4_t*>(&shifted);
pack[0] = vqshrn_n_s32(in[i * 2 + 0], WeightScaleBits); pack[0] = vqshrn_n_s32(in[i * 2 + 0], WeightScaleBits);
@@ -166,12 +156,10 @@ namespace Stockfish::Eval::NNUE::Layers {
constexpr IndexType Start = 0; constexpr IndexType Start = 0;
#endif #endif
for (IndexType i = Start; i < InputDimensions; ++i) { for (IndexType i = Start; i < InputDimensions; ++i)
output[i] = static_cast<OutputType>( {
std::max(0, std::min(127, input[i] >> WeightScaleBits))); output[i] = static_cast<OutputType>(std::clamp(input[i] >> WeightScaleBits, 0, 127));
} }
return output;
} }
}; };
+18 -254
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -31,28 +31,10 @@
#elif defined(USE_SSE2) #elif defined(USE_SSE2)
#include <emmintrin.h> #include <emmintrin.h>
#elif defined(USE_MMX)
# include <mmintrin.h>
#elif defined(USE_NEON) #elif defined(USE_NEON)
#include <arm_neon.h> #include <arm_neon.h>
#endif #endif
// The inline asm is only safe for GCC, where it is necessary to get good codegen.
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101693
// Clang does fine without it.
// Play around here: https://godbolt.org/z/7EWqrYq51
#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER))
#define USE_INLINE_ASM
#endif
// Use either the AVX512 or AVX-VNNI version of the VNNI instructions.
#if defined(USE_AVXVNNI)
#define VNNI_PREFIX "%{vex%} "
#else
#define VNNI_PREFIX ""
#endif
namespace Stockfish::Simd { namespace Stockfish::Simd {
#if defined(USE_AVX512) #if defined(USE_AVX512)
@@ -76,8 +58,8 @@ namespace Stockfish::Simd {
reduce_add_epi32(zmm0.i128[3]), reduce_add_epi32(zmm1.i128[3]), reduce_add_epi32(zmm2.i128[3]), reduce_add_epi32(zmm3.i128[3]) reduce_add_epi32(zmm0.i128[3]), reduce_add_epi32(zmm1.i128[3]), reduce_add_epi32(zmm2.i128[3]), reduce_add_epi32(zmm3.i128[3])
] ]
*/ */
[[maybe_unused]] static __m512i m512_hadd128x16_interleave( [[maybe_unused]] static __m512i
__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) { m512_hadd128x16_interleave(__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) {
__m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1); __m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1);
__m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1); __m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1);
@@ -94,91 +76,14 @@ namespace Stockfish::Simd {
return _mm512_add_epi32(sum0123a, sum0123b); return _mm512_add_epi32(sum0123a, sum0123b);
} }
[[maybe_unused]] static __m128i m512_haddx4( [[maybe_unused]] static void m512_add_dpbusd_epi32(__m512i& acc, __m512i a, __m512i b) {
__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3,
__m128i bias) {
__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]] static void m512_add_dpbusd_epi32(
__m512i& acc,
__m512i a,
__m512i b) {
#if defined(USE_VNNI) #if defined(USE_VNNI)
# if defined (USE_INLINE_ASM)
asm(
"vpdpbusd %[b], %[a], %[acc]\n\t"
: [acc]"+v"(acc)
: [a]"v"(a), [b]"vm"(b)
);
# else
acc = _mm512_dpbusd_epi32(acc, a, b); acc = _mm512_dpbusd_epi32(acc, a, b);
# endif
# else
# if defined (USE_INLINE_ASM)
__m512i tmp = _mm512_maddubs_epi16(a, b);
asm(
"vpmaddwd %[tmp], %[ones], %[tmp]\n\t"
"vpaddd %[acc], %[tmp], %[acc]\n\t"
: [acc]"+v"(acc), [tmp]"+&v"(tmp)
: [ones]"v"(_mm512_set1_epi16(1))
);
#else #else
__m512i product0 = _mm512_maddubs_epi16(a, b); __m512i product0 = _mm512_maddubs_epi16(a, b);
product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1));
acc = _mm512_add_epi32(acc, product0); acc = _mm512_add_epi32(acc, product0);
# endif
# endif
}
[[maybe_unused]] static void m512_add_dpbusd_epi32x2(
__m512i& acc,
__m512i a0, __m512i b0,
__m512i a1, __m512i b1) {
# if defined (USE_VNNI)
# if defined (USE_INLINE_ASM)
asm(
"vpdpbusd %[b0], %[a0], %[acc]\n\t"
"vpdpbusd %[b1], %[a1], %[acc]\n\t"
: [acc]"+&v"(acc)
: [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1)
);
# else
acc = _mm512_dpbusd_epi32(acc, a0, b0);
acc = _mm512_dpbusd_epi32(acc, a1, b1);
# endif
# else
# if defined (USE_INLINE_ASM)
__m512i tmp0 = _mm512_maddubs_epi16(a0, b0);
__m512i tmp1 = _mm512_maddubs_epi16(a1, b1);
asm(
"vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t"
"vpmaddwd %[tmp1], %[ones], %[tmp1]\n\t"
"vpaddd %[tmp0], %[tmp1], %[tmp0]\n\t"
"vpaddd %[acc], %[tmp0], %[acc]\n\t"
: [acc]"+v"(acc), [tmp0]"+&v"(tmp0), [tmp1]"+&v"(tmp1)
: [ones]"v"(_mm512_set1_epi16(1))
);
# else
__m512i product0 = _mm512_maddubs_epi16(a0, b0);
__m512i product1 = _mm512_maddubs_epi16(a1, b1);
product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1));
product1 = _mm512_madd_epi16(product1, _mm512_set1_epi16(1));
acc = _mm512_add_epi32(acc, _mm512_add_epi32(product0, product1));
# endif
#endif #endif
} }
@@ -193,89 +98,14 @@ namespace Stockfish::Simd {
return _mm_cvtsi128_si32(sum128) + bias; return _mm_cvtsi128_si32(sum128) + bias;
} }
[[maybe_unused]] static __m128i m256_haddx4( [[maybe_unused]] static void m256_add_dpbusd_epi32(__m256i& acc, __m256i a, __m256i b) {
__m256i sum0, __m256i sum1, __m256i sum2, __m256i sum3,
__m128i bias) {
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);
}
[[maybe_unused]] static void m256_add_dpbusd_epi32(
__m256i& acc,
__m256i a,
__m256i b) {
#if defined(USE_VNNI) #if defined(USE_VNNI)
# if defined (USE_INLINE_ASM)
asm(
VNNI_PREFIX "vpdpbusd %[b], %[a], %[acc]\n\t"
: [acc]"+v"(acc)
: [a]"v"(a), [b]"vm"(b)
);
# else
acc = _mm256_dpbusd_epi32(acc, a, b); acc = _mm256_dpbusd_epi32(acc, a, b);
# endif
# else
# if defined (USE_INLINE_ASM)
__m256i tmp = _mm256_maddubs_epi16(a, b);
asm(
"vpmaddwd %[tmp], %[ones], %[tmp]\n\t"
"vpaddd %[acc], %[tmp], %[acc]\n\t"
: [acc]"+v"(acc), [tmp]"+&v"(tmp)
: [ones]"v"(_mm256_set1_epi16(1))
);
#else #else
__m256i product0 = _mm256_maddubs_epi16(a, b); __m256i product0 = _mm256_maddubs_epi16(a, b);
product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1));
acc = _mm256_add_epi32(acc, product0); acc = _mm256_add_epi32(acc, product0);
# endif
# endif
}
[[maybe_unused]] static void m256_add_dpbusd_epi32x2(
__m256i& acc,
__m256i a0, __m256i b0,
__m256i a1, __m256i b1) {
# if defined (USE_VNNI)
# if defined (USE_INLINE_ASM)
asm(
VNNI_PREFIX "vpdpbusd %[b0], %[a0], %[acc]\n\t"
VNNI_PREFIX "vpdpbusd %[b1], %[a1], %[acc]\n\t"
: [acc]"+&v"(acc)
: [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1)
);
# else
acc = _mm256_dpbusd_epi32(acc, a0, b0);
acc = _mm256_dpbusd_epi32(acc, a1, b1);
# endif
# else
# if defined (USE_INLINE_ASM)
__m256i tmp0 = _mm256_maddubs_epi16(a0, b0);
__m256i tmp1 = _mm256_maddubs_epi16(a1, b1);
asm(
"vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t"
"vpmaddwd %[tmp1], %[ones], %[tmp1]\n\t"
"vpaddd %[tmp0], %[tmp1], %[tmp0]\n\t"
"vpaddd %[acc], %[tmp0], %[acc]\n\t"
: [acc]"+v"(acc), [tmp0]"+&v"(tmp0), [tmp1]"+&v"(tmp1)
: [ones]"v"(_mm256_set1_epi16(1))
);
# else
__m256i product0 = _mm256_maddubs_epi16(a0, b0);
__m256i product1 = _mm256_maddubs_epi16(a1, b1);
product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1));
product1 = _mm256_madd_epi16(product1, _mm256_set1_epi16(1));
acc = _mm256_add_epi32(acc, _mm256_add_epi32(product0, product1));
# endif
#endif #endif
} }
@@ -289,74 +119,22 @@ namespace Stockfish::Simd {
return _mm_cvtsi128_si32(sum) + bias; return _mm_cvtsi128_si32(sum) + bias;
} }
[[maybe_unused]] static __m128i m128_haddx4( [[maybe_unused]] static void m128_add_dpbusd_epi32(__m128i& acc, __m128i a, __m128i b) {
__m128i sum0, __m128i sum1, __m128i sum2, __m128i sum3,
__m128i bias) {
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]] static void m128_add_dpbusd_epi32(
__m128i& acc,
__m128i a,
__m128i b) {
# if defined (USE_INLINE_ASM)
__m128i tmp = _mm_maddubs_epi16(a, b);
asm(
"pmaddwd %[ones], %[tmp]\n\t"
"paddd %[tmp], %[acc]\n\t"
: [acc]"+v"(acc), [tmp]"+&v"(tmp)
: [ones]"v"(_mm_set1_epi16(1))
);
# else
__m128i product0 = _mm_maddubs_epi16(a, b); __m128i product0 = _mm_maddubs_epi16(a, b);
product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1));
acc = _mm_add_epi32(acc, product0); acc = _mm_add_epi32(acc, product0);
# endif
}
[[maybe_unused]] static void m128_add_dpbusd_epi32x2(
__m128i& acc,
__m128i a0, __m128i b0,
__m128i a1, __m128i b1) {
# if defined (USE_INLINE_ASM)
__m128i tmp0 = _mm_maddubs_epi16(a0, b0);
__m128i tmp1 = _mm_maddubs_epi16(a1, b1);
asm(
"pmaddwd %[ones], %[tmp0]\n\t"
"pmaddwd %[ones], %[tmp1]\n\t"
"paddd %[tmp1], %[tmp0]\n\t"
"paddd %[tmp0], %[acc]\n\t"
: [acc]"+v"(acc), [tmp0]"+&v"(tmp0), [tmp1]"+&v"(tmp1)
: [ones]"v"(_mm_set1_epi16(1))
);
# else
__m128i product0 = _mm_maddubs_epi16(a0, b0);
__m128i product1 = _mm_maddubs_epi16(a1, b1);
product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1));
product1 = _mm_madd_epi16(product1, _mm_set1_epi16(1));
acc = _mm_add_epi32(acc, _mm_add_epi32(product0, product1));
# endif
} }
#endif #endif
#if defined(USE_NEON_DOTPROD) #if defined(USE_NEON_DOTPROD)
[[maybe_unused]] static void dotprod_m128_add_dpbusd_epi32x2( [[maybe_unused]] static void
int32x4_t& acc, dotprod_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) {
int8x16_t a0, int8x16_t b0,
int8x16_t a1, int8x16_t b1) {
acc = vdotq_s32(acc, a0, b0); acc = vdotq_s32(acc, a, b);
acc = vdotq_s32(acc, a1, b1);
} }
#endif #endif
#if defined(USE_NEON) #if defined(USE_NEON)
@@ -373,31 +151,17 @@ namespace Stockfish::Simd {
return neon_m128_reduce_add_epi32(sum) + bias; return neon_m128_reduce_add_epi32(sum) + bias;
} }
[[maybe_unused]] static int32x4_t neon_m128_haddx4(
int32x4_t sum0, int32x4_t sum1, int32x4_t sum2, int32x4_t sum3,
int32x4_t bias) {
int32x4_t hsums {
neon_m128_reduce_add_epi32(sum0),
neon_m128_reduce_add_epi32(sum1),
neon_m128_reduce_add_epi32(sum2),
neon_m128_reduce_add_epi32(sum3)
};
return vaddq_s32(hsums, bias);
}
[[maybe_unused]] static void neon_m128_add_dpbusd_epi32x2(
int32x4_t& acc,
int8x8_t a0, int8x8_t b0,
int8x8_t a1, int8x8_t b1) {
int16x8_t product = vmull_s8(a0, b0);
product = vmlal_s8(product, a1, b1);
acc = vpadalq_s16(acc, product);
}
#endif #endif
#if USE_NEON >= 8
[[maybe_unused]] static void neon_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) {
int16x8_t product0 = vmull_s8(vget_low_s8(a), vget_low_s8(b));
int16x8_t product1 = vmull_high_s8(a, b);
int16x8_t sum = vpaddq_s16(product0, product1);
acc = vpadalq_s16(acc, sum);
}
#endif
} }
#endif // STOCKFISH_SIMD_H_INCLUDED #endif // STOCKFISH_SIMD_H_INCLUDED
+23 -40
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,6 +21,10 @@
#ifndef NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED #ifndef NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED
#define NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED #define NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED
#include <algorithm>
#include <cstdint>
#include <iosfwd>
#include "../nnue_common.h" #include "../nnue_common.h"
namespace Stockfish::Eval::NNUE::Layers { namespace Stockfish::Eval::NNUE::Layers {
@@ -49,54 +53,34 @@ namespace Stockfish::Eval::NNUE::Layers {
} }
// Read network parameters // Read network parameters
bool read_parameters(std::istream&) { bool read_parameters(std::istream&) { return true; }
return true;
}
// Write network parameters // Write network parameters
bool write_parameters(std::ostream&) const { bool write_parameters(std::ostream&) const { return true; }
return true;
}
// Forward propagation // Forward propagation
const OutputType* propagate( void propagate(const InputType* input, OutputType* output) const {
const InputType* input, OutputType* output) const {
#if defined(USE_SSE2) #if defined(USE_SSE2)
constexpr IndexType NumChunks = InputDimensions / 16; constexpr IndexType NumChunks = InputDimensions / 16;
#ifdef USE_SSE41
const __m128i Zero = _mm_setzero_si128();
#else
const __m128i k0x80s = _mm_set1_epi8(-128);
#endif
static_assert(WeightScaleBits == 6); static_assert(WeightScaleBits == 6);
const auto in = reinterpret_cast<const __m128i*>(input); const auto in = reinterpret_cast<const __m128i*>(input);
const auto out = reinterpret_cast<__m128i*>(output); const auto out = reinterpret_cast<__m128i*>(output);
for (IndexType i = 0; i < NumChunks; ++i) { for (IndexType i = 0; i < NumChunks; ++i)
__m128i words0 = _mm_packs_epi32( {
_mm_load_si128(&in[i * 4 + 0]), __m128i words0 =
_mm_load_si128(&in[i * 4 + 1])); _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1]));
__m128i words1 = _mm_packs_epi32( __m128i words1 =
_mm_load_si128(&in[i * 4 + 2]), _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3]));
_mm_load_si128(&in[i * 4 + 3]));
// Not sure if // We shift by WeightScaleBits * 2 = 12 and divide by 128
// which is an additional shift-right of 7, meaning 19 in total.
// MulHi strips the lower 16 bits so we need to shift out 3 more to match.
words0 = _mm_srli_epi16(_mm_mulhi_epi16(words0, words0), 3); words0 = _mm_srli_epi16(_mm_mulhi_epi16(words0, words0), 3);
words1 = _mm_srli_epi16(_mm_mulhi_epi16(words1, words1), 3); words1 = _mm_srli_epi16(_mm_mulhi_epi16(words1, words1), 3);
const __m128i packedbytes = _mm_packs_epi16(words0, words1); _mm_store_si128(&out[i], _mm_packs_epi16(words0, words1));
_mm_store_si128(&out[i],
#ifdef USE_SSE41
_mm_max_epi8(packedbytes, Zero)
#else
_mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)
#endif
);
} }
constexpr IndexType Start = NumChunks * 16; constexpr IndexType Start = NumChunks * 16;
@@ -104,14 +88,13 @@ namespace Stockfish::Eval::NNUE::Layers {
constexpr IndexType Start = 0; constexpr IndexType Start = 0;
#endif #endif
for (IndexType i = Start; i < InputDimensions; ++i) { for (IndexType i = Start; i < InputDimensions; ++i)
{
output[i] = static_cast<OutputType>( output[i] = static_cast<OutputType>(
// really should be /127 but we need to make it fast // Really should be /127 but we need to make it fast so we right-shift
// needs to be accounted for in the trainer // by an extra 7 bits instead. Needs to be accounted for in the trainer.
std::max(0ll, std::min(127ll, (((long long)input[i] * input[i]) >> (2 * WeightScaleBits)) / 128))); std::min(127ll, ((long long) (input[i]) * input[i]) >> (2 * WeightScaleBits + 7)));
} }
return output;
} }
}; };
+444
View File
@@ -0,0 +1,444 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "network.h"
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <optional>
#include <type_traits>
#include <vector>
#include "../cluster.h"
#include "../evaluate.h"
#include "../incbin/incbin.h"
#include "../misc.h"
#include "../position.h"
#include "../types.h"
#include "nnue_architecture.h"
#include "nnue_common.h"
#include "nnue_misc.h"
namespace {
// Macro to embed the default efficiently updatable neural network (NNUE) file
// data in the engine binary (using incbin.h, by Dale Weiler).
// This macro invocation will declare the following three variables
// const unsigned char gEmbeddedNNUEData[]; // a pointer to the embedded data
// const unsigned char *const gEmbeddedNNUEEnd; // a marker to the end
// const unsigned int gEmbeddedNNUESize; // the size of the embedded file
// Note that this does not work in Microsoft Visual Studio.
#if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF)
INCBIN(EmbeddedNNUEBig, EvalFileDefaultNameBig);
INCBIN(EmbeddedNNUESmall, EvalFileDefaultNameSmall);
#else
const unsigned char gEmbeddedNNUEBigData[1] = {0x0};
const unsigned char* const gEmbeddedNNUEBigEnd = &gEmbeddedNNUEBigData[1];
const unsigned int gEmbeddedNNUEBigSize = 1;
const unsigned char gEmbeddedNNUESmallData[1] = {0x0};
const unsigned char* const gEmbeddedNNUESmallEnd = &gEmbeddedNNUESmallData[1];
const unsigned int gEmbeddedNNUESmallSize = 1;
#endif
struct EmbeddedNNUE {
EmbeddedNNUE(const unsigned char* embeddedData,
const unsigned char* embeddedEnd,
const unsigned int embeddedSize) :
data(embeddedData),
end(embeddedEnd),
size(embeddedSize) {}
const unsigned char* data;
const unsigned char* end;
const unsigned int size;
};
using namespace Stockfish::Eval::NNUE;
EmbeddedNNUE get_embedded(EmbeddedNNUEType type) {
if (type == EmbeddedNNUEType::BIG)
return EmbeddedNNUE(gEmbeddedNNUEBigData, gEmbeddedNNUEBigEnd, gEmbeddedNNUEBigSize);
else
return EmbeddedNNUE(gEmbeddedNNUESmallData, gEmbeddedNNUESmallEnd, gEmbeddedNNUESmallSize);
}
}
namespace Stockfish::Eval::NNUE {
namespace Detail {
// Initialize the evaluation function parameters
template<typename T>
void initialize(AlignedPtr<T>& pointer) {
pointer.reset(reinterpret_cast<T*>(std_aligned_alloc(alignof(T), sizeof(T))));
std::memset(pointer.get(), 0, sizeof(T));
}
template<typename T>
void initialize(LargePagePtr<T>& pointer) {
static_assert(alignof(T) <= 4096,
"aligned_large_pages_alloc() may fail for such a big alignment requirement of T");
pointer.reset(reinterpret_cast<T*>(aligned_large_pages_alloc(sizeof(T))));
std::memset(pointer.get(), 0, sizeof(T));
}
// Read evaluation function parameters
template<typename T>
bool read_parameters(std::istream& stream, T& reference) {
std::uint32_t header;
header = read_little_endian<std::uint32_t>(stream);
if (!stream || header != T::get_hash_value())
return false;
return reference.read_parameters(stream);
}
// Write evaluation function parameters
template<typename T>
bool write_parameters(std::ostream& stream, const T& reference) {
write_little_endian<std::uint32_t>(stream, T::get_hash_value());
return reference.write_parameters(stream);
}
} // namespace Detail
template<typename Arch, typename Transformer>
void Network<Arch, Transformer>::load(const std::string& rootDirectory, std::string evalfilePath) {
#if defined(DEFAULT_NNUE_DIRECTORY)
std::vector<std::string> dirs = {"<internal>", "", rootDirectory,
stringify(DEFAULT_NNUE_DIRECTORY)};
#else
std::vector<std::string> dirs = {"<internal>", "", rootDirectory};
#endif
if (evalfilePath.empty())
evalfilePath = evalFile.defaultName;
for (const auto& directory : dirs)
{
if (evalFile.current != evalfilePath)
{
if (directory != "<internal>")
{
load_user_net(directory, evalfilePath);
}
if (directory == "<internal>" && evalfilePath == evalFile.defaultName)
{
load_internal();
}
}
}
}
template<typename Arch, typename Transformer>
bool Network<Arch, Transformer>::save(const std::optional<std::string>& filename) const {
std::string actualFilename;
std::string msg;
if (filename.has_value())
actualFilename = filename.value();
else
{
if (evalFile.current != evalFile.defaultName)
{
msg = "Failed to export a net. "
"A non-embedded net can only be saved if the filename is specified";
sync_cout << msg << sync_endl;
return false;
}
actualFilename = evalFile.defaultName;
}
std::ofstream stream(actualFilename, std::ios_base::binary);
bool saved = save(stream, evalFile.current, evalFile.netDescription);
msg = saved ? "Network saved successfully to " + actualFilename : "Failed to export a net";
sync_cout << msg << sync_endl;
return saved;
}
template<typename Arch, typename Transformer>
Value Network<Arch, Transformer>::evaluate(const Position& pos,
bool adjusted,
int* complexity,
bool psqtOnly) const {
// We manually align the arrays on the stack because with gcc < 9.3
// overaligning stack variables with alignas() doesn't work correctly.
constexpr uint64_t alignment = CacheLineSize;
constexpr int delta = 24;
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
TransformedFeatureType transformedFeaturesUnaligned
[FeatureTransformer<Arch::TransformedFeatureDimensions, nullptr>::BufferSize
+ alignment / sizeof(TransformedFeatureType)];
auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
#else
alignas(alignment) TransformedFeatureType transformedFeatures
[FeatureTransformer<Arch::TransformedFeatureDimensions, nullptr>::BufferSize];
#endif
ASSERT_ALIGNED(transformedFeatures, alignment);
const int bucket = (pos.count<ALL_PIECES>() - 1) / 4;
const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket, psqtOnly);
const auto positional = !psqtOnly ? (network[bucket]->propagate(transformedFeatures)) : 0;
if (complexity)
*complexity = !psqtOnly ? std::abs(psqt - positional) / OutputScale : 0;
// Give more value to positional evaluation when adjusted flag is set
if (adjusted)
return static_cast<Value>(((1024 - delta) * psqt + (1024 + delta) * positional)
/ (1024 * OutputScale));
else
return static_cast<Value>((psqt + positional) / OutputScale);
}
template<typename Arch, typename Transformer>
void Network<Arch, Transformer>::verify(std::string evalfilePath) const {
if (evalfilePath.empty())
evalfilePath = evalFile.defaultName;
if (evalFile.current != evalfilePath)
{
std::string msg1 =
"Network evaluation parameters compatible with the engine must be available.";
std::string msg2 = "The network file " + evalfilePath + " 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/"
+ evalFile.defaultName;
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;
exit(EXIT_FAILURE);
}
if (Cluster::is_root())
sync_cout << "info string NNUE evaluation using " << evalfilePath << sync_endl;
}
template<typename Arch, typename Transformer>
void Network<Arch, Transformer>::hint_common_access(const Position& pos, bool psqtOnl) const {
featureTransformer->hint_common_access(pos, psqtOnl);
}
template<typename Arch, typename Transformer>
NnueEvalTrace Network<Arch, Transformer>::trace_evaluate(const Position& pos) const {
// We manually align the arrays on the stack because with gcc < 9.3
// overaligning stack variables with alignas() doesn't work correctly.
constexpr uint64_t alignment = CacheLineSize;
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
TransformedFeatureType transformedFeaturesUnaligned
[FeatureTransformer<Arch::TransformedFeatureDimensions, nullptr>::BufferSize
+ alignment / sizeof(TransformedFeatureType)];
auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
#else
alignas(alignment) TransformedFeatureType transformedFeatures
[FeatureTransformer<Arch::TransformedFeatureDimensions, nullptr>::BufferSize];
#endif
ASSERT_ALIGNED(transformedFeatures, alignment);
NnueEvalTrace t{};
t.correctBucket = (pos.count<ALL_PIECES>() - 1) / 4;
for (IndexType bucket = 0; bucket < LayerStacks; ++bucket)
{
const auto materialist =
featureTransformer->transform(pos, transformedFeatures, bucket, false);
const auto positional = network[bucket]->propagate(transformedFeatures);
t.psqt[bucket] = static_cast<Value>(materialist / OutputScale);
t.positional[bucket] = static_cast<Value>(positional / OutputScale);
}
return t;
}
template<typename Arch, typename Transformer>
void Network<Arch, Transformer>::load_user_net(const std::string& dir,
const std::string& evalfilePath) {
std::ifstream stream(dir + evalfilePath, std::ios::binary);
auto description = load(stream);
if (description.has_value())
{
evalFile.current = evalfilePath;
evalFile.netDescription = description.value();
}
}
template<typename Arch, typename Transformer>
void Network<Arch, Transformer>::load_internal() {
// C++ way to prepare a buffer for a memory stream
class MemoryBuffer: public std::basic_streambuf<char> {
public:
MemoryBuffer(char* p, size_t n) {
setg(p, p, p + n);
setp(p, p + n);
}
};
const auto embedded = get_embedded(embeddedType);
MemoryBuffer buffer(const_cast<char*>(reinterpret_cast<const char*>(embedded.data)),
size_t(embedded.size));
std::istream stream(&buffer);
auto description = load(stream);
if (description.has_value())
{
evalFile.current = evalFile.defaultName;
evalFile.netDescription = description.value();
}
}
template<typename Arch, typename Transformer>
void Network<Arch, Transformer>::initialize() {
Detail::initialize(featureTransformer);
for (std::size_t i = 0; i < LayerStacks; ++i)
Detail::initialize(network[i]);
}
template<typename Arch, typename Transformer>
bool Network<Arch, Transformer>::save(std::ostream& stream,
const std::string& name,
const std::string& netDescription) const {
if (name.empty() || name == "None")
return false;
return write_parameters(stream, netDescription);
}
template<typename Arch, typename Transformer>
std::optional<std::string> Network<Arch, Transformer>::load(std::istream& stream) {
initialize();
std::string description;
return read_parameters(stream, description) ? std::make_optional(description) : std::nullopt;
}
// Read network header
template<typename Arch, typename Transformer>
bool Network<Arch, Transformer>::read_header(std::istream& stream,
std::uint32_t* hashValue,
std::string* desc) const {
std::uint32_t version, size;
version = read_little_endian<std::uint32_t>(stream);
*hashValue = read_little_endian<std::uint32_t>(stream);
size = read_little_endian<std::uint32_t>(stream);
if (!stream || version != Version)
return false;
desc->resize(size);
stream.read(&(*desc)[0], size);
return !stream.fail();
}
// Write network header
template<typename Arch, typename Transformer>
bool Network<Arch, Transformer>::write_header(std::ostream& stream,
std::uint32_t hashValue,
const std::string& desc) const {
write_little_endian<std::uint32_t>(stream, Version);
write_little_endian<std::uint32_t>(stream, hashValue);
write_little_endian<std::uint32_t>(stream, std::uint32_t(desc.size()));
stream.write(&desc[0], desc.size());
return !stream.fail();
}
template<typename Arch, typename Transformer>
bool Network<Arch, Transformer>::read_parameters(std::istream& stream,
std::string& netDescription) const {
std::uint32_t hashValue;
if (!read_header(stream, &hashValue, &netDescription))
return false;
if (hashValue != Network::hash)
return false;
if (!Detail::read_parameters(stream, *featureTransformer))
return false;
for (std::size_t i = 0; i < LayerStacks; ++i)
{
if (!Detail::read_parameters(stream, *(network[i])))
return false;
}
return stream && stream.peek() == std::ios::traits_type::eof();
}
template<typename Arch, typename Transformer>
bool Network<Arch, Transformer>::write_parameters(std::ostream& stream,
const std::string& netDescription) const {
if (!write_header(stream, Network::hash, netDescription))
return false;
if (!Detail::write_parameters(stream, *featureTransformer))
return false;
for (std::size_t i = 0; i < LayerStacks; ++i)
{
if (!Detail::write_parameters(stream, *(network[i])))
return false;
}
return bool(stream);
}
// Explicit template instantiation
template class Network<
NetworkArchitecture<TransformedFeatureDimensionsBig, L2Big, L3Big>,
FeatureTransformer<TransformedFeatureDimensionsBig, &StateInfo::accumulatorBig>>;
template class Network<
NetworkArchitecture<TransformedFeatureDimensionsSmall, L2Small, L3Small>,
FeatureTransformer<TransformedFeatureDimensionsSmall, &StateInfo::accumulatorSmall>>;
} // namespace Stockfish::Eval::NNUE
+120
View File
@@ -0,0 +1,120 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NETWORK_H_INCLUDED
#define NETWORK_H_INCLUDED
#include <cstdint>
#include <iostream>
#include <optional>
#include <string>
#include <utility>
#include "../misc.h"
#include "../position.h"
#include "../types.h"
#include "nnue_architecture.h"
#include "nnue_feature_transformer.h"
#include "nnue_misc.h"
namespace Stockfish::Eval::NNUE {
enum class EmbeddedNNUEType {
BIG,
SMALL,
};
template<typename Arch, typename Transformer>
class Network {
public:
Network(EvalFile file, EmbeddedNNUEType type) :
evalFile(file),
embeddedType(type) {}
void load(const std::string& rootDirectory, std::string evalfilePath);
bool save(const std::optional<std::string>& filename) const;
Value evaluate(const Position& pos,
bool adjusted = false,
int* complexity = nullptr,
bool psqtOnly = false) const;
void hint_common_access(const Position& pos, bool psqtOnl) const;
void verify(std::string evalfilePath) const;
NnueEvalTrace trace_evaluate(const Position& pos) const;
private:
void load_user_net(const std::string&, const std::string&);
void load_internal();
void initialize();
bool save(std::ostream&, const std::string&, const std::string&) const;
std::optional<std::string> load(std::istream&);
bool read_header(std::istream&, std::uint32_t*, std::string*) const;
bool write_header(std::ostream&, std::uint32_t, const std::string&) const;
bool read_parameters(std::istream&, std::string&) const;
bool write_parameters(std::ostream&, const std::string&) const;
// Input feature converter
LargePagePtr<Transformer> featureTransformer;
// Evaluation function
AlignedPtr<Arch> network[LayerStacks];
EvalFile evalFile;
EmbeddedNNUEType embeddedType;
// Hash value of evaluation function structure
static constexpr std::uint32_t hash = Transformer::get_hash_value() ^ Arch::get_hash_value();
};
// Definitions of the network types
using SmallFeatureTransformer =
FeatureTransformer<TransformedFeatureDimensionsSmall, &StateInfo::accumulatorSmall>;
using SmallNetworkArchitecture =
NetworkArchitecture<TransformedFeatureDimensionsSmall, L2Small, L3Small>;
using BigFeatureTransformer =
FeatureTransformer<TransformedFeatureDimensionsBig, &StateInfo::accumulatorBig>;
using BigNetworkArchitecture = NetworkArchitecture<TransformedFeatureDimensionsBig, L2Big, L3Big>;
using NetworkBig = Network<BigNetworkArchitecture, BigFeatureTransformer>;
using NetworkSmall = Network<SmallNetworkArchitecture, SmallFeatureTransformer>;
struct Networks {
Networks(NetworkBig&& nB, NetworkSmall&& nS) :
big(std::move(nB)),
small(std::move(nS)) {}
NetworkBig big;
NetworkSmall small;
};
} // namespace Stockfish
#endif
+7 -2
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,15 +21,20 @@
#ifndef NNUE_ACCUMULATOR_H_INCLUDED #ifndef NNUE_ACCUMULATOR_H_INCLUDED
#define NNUE_ACCUMULATOR_H_INCLUDED #define NNUE_ACCUMULATOR_H_INCLUDED
#include <cstdint>
#include "nnue_architecture.h" #include "nnue_architecture.h"
#include "nnue_common.h"
namespace Stockfish::Eval::NNUE { namespace Stockfish::Eval::NNUE {
// Class that holds the result of affine transformation of input features // Class that holds the result of affine transformation of input features
template<IndexType Size>
struct alignas(CacheLineSize) Accumulator { struct alignas(CacheLineSize) Accumulator {
std::int16_t accumulation[2][TransformedFeatureDimensions]; std::int16_t accumulation[2][Size];
std::int32_t psqtAccumulation[2][PSQTBuckets]; std::int32_t psqtAccumulation[2][PSQTBuckets];
bool computed[2]; bool computed[2];
bool computedPSQT[2];
}; };
} // namespace Stockfish::Eval::NNUE } // namespace Stockfish::Eval::NNUE
+39 -39
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,18 +21,16 @@
#ifndef NNUE_ARCHITECTURE_H_INCLUDED #ifndef NNUE_ARCHITECTURE_H_INCLUDED
#define NNUE_ARCHITECTURE_H_INCLUDED #define NNUE_ARCHITECTURE_H_INCLUDED
#include <memory> #include <cstdint>
#include <cstring>
#include "nnue_common.h" #include <iosfwd>
#include "features/half_ka_v2_hm.h" #include "features/half_ka_v2_hm.h"
#include "layers/affine_transform_sparse_input.h"
#include "layers/affine_transform.h" #include "layers/affine_transform.h"
#include "layers/affine_transform_sparse_input.h"
#include "layers/clipped_relu.h" #include "layers/clipped_relu.h"
#include "layers/sqr_clipped_relu.h" #include "layers/sqr_clipped_relu.h"
#include "nnue_common.h"
#include "../misc.h"
namespace Stockfish::Eval::NNUE { namespace Stockfish::Eval::NNUE {
@@ -40,14 +38,22 @@ namespace Stockfish::Eval::NNUE {
using FeatureSet = Features::HalfKAv2_hm; using FeatureSet = Features::HalfKAv2_hm;
// Number of input feature dimensions after conversion // Number of input feature dimensions after conversion
constexpr IndexType TransformedFeatureDimensions = 1536; constexpr IndexType TransformedFeatureDimensionsBig = 3072;
constexpr int L2Big = 15;
constexpr int L3Big = 32;
constexpr IndexType TransformedFeatureDimensionsSmall = 128;
constexpr int L2Small = 15;
constexpr int L3Small = 32;
constexpr IndexType PSQTBuckets = 8; constexpr IndexType PSQTBuckets = 8;
constexpr IndexType LayerStacks = 8; constexpr IndexType LayerStacks = 8;
struct Network template<IndexType L1, int L2, int L3>
{ struct NetworkArchitecture {
static constexpr int FC_0_OUTPUTS = 15; static constexpr IndexType TransformedFeatureDimensions = L1;
static constexpr int FC_1_OUTPUTS = 32; static constexpr int FC_0_OUTPUTS = L2;
static constexpr int FC_1_OUTPUTS = L3;
Layers::AffineTransformSparseInput<TransformedFeatureDimensions, FC_0_OUTPUTS + 1> fc_0; Layers::AffineTransformSparseInput<TransformedFeatureDimensions, FC_0_OUTPUTS + 1> fc_0;
Layers::SqrClippedReLU<FC_0_OUTPUTS + 1> ac_sqr_0; Layers::SqrClippedReLU<FC_0_OUTPUTS + 1> ac_sqr_0;
@@ -73,37 +79,29 @@ struct Network
// Read network parameters // Read network parameters
bool read_parameters(std::istream& stream) { bool read_parameters(std::istream& stream) {
return fc_0.read_parameters(stream) return fc_0.read_parameters(stream) && ac_0.read_parameters(stream)
&& ac_0.read_parameters(stream) && fc_1.read_parameters(stream) && ac_1.read_parameters(stream)
&& fc_1.read_parameters(stream)
&& ac_1.read_parameters(stream)
&& fc_2.read_parameters(stream); && fc_2.read_parameters(stream);
} }
// Write network parameters // Write network parameters
bool write_parameters(std::ostream& stream) const { bool write_parameters(std::ostream& stream) const {
return fc_0.write_parameters(stream) return fc_0.write_parameters(stream) && ac_0.write_parameters(stream)
&& ac_0.write_parameters(stream) && fc_1.write_parameters(stream) && ac_1.write_parameters(stream)
&& fc_1.write_parameters(stream)
&& ac_1.write_parameters(stream)
&& fc_2.write_parameters(stream); && fc_2.write_parameters(stream);
} }
std::int32_t propagate(const TransformedFeatureType* transformedFeatures) std::int32_t propagate(const TransformedFeatureType* transformedFeatures) {
{ struct alignas(CacheLineSize) Buffer {
struct alignas(CacheLineSize) Buffer alignas(CacheLineSize) typename decltype(fc_0)::OutputBuffer fc_0_out;
{ alignas(CacheLineSize) typename decltype(ac_sqr_0)::OutputType
alignas(CacheLineSize) decltype(fc_0)::OutputBuffer fc_0_out; ac_sqr_0_out[ceil_to_multiple<IndexType>(FC_0_OUTPUTS * 2, 32)];
alignas(CacheLineSize) decltype(ac_sqr_0)::OutputType ac_sqr_0_out[ceil_to_multiple<IndexType>(FC_0_OUTPUTS * 2, 32)]; alignas(CacheLineSize) typename decltype(ac_0)::OutputBuffer ac_0_out;
alignas(CacheLineSize) decltype(ac_0)::OutputBuffer ac_0_out; alignas(CacheLineSize) typename decltype(fc_1)::OutputBuffer fc_1_out;
alignas(CacheLineSize) decltype(fc_1)::OutputBuffer fc_1_out; alignas(CacheLineSize) typename decltype(ac_1)::OutputBuffer ac_1_out;
alignas(CacheLineSize) decltype(ac_1)::OutputBuffer ac_1_out; alignas(CacheLineSize) typename decltype(fc_2)::OutputBuffer fc_2_out;
alignas(CacheLineSize) decltype(fc_2)::OutputBuffer fc_2_out;
Buffer() Buffer() { std::memset(this, 0, sizeof(*this)); }
{
std::memset(this, 0, sizeof(*this));
}
}; };
#if defined(__clang__) && (__APPLE__) #if defined(__clang__) && (__APPLE__)
@@ -118,14 +116,16 @@ struct Network
fc_0.propagate(transformedFeatures, buffer.fc_0_out); fc_0.propagate(transformedFeatures, buffer.fc_0_out);
ac_sqr_0.propagate(buffer.fc_0_out, buffer.ac_sqr_0_out); ac_sqr_0.propagate(buffer.fc_0_out, buffer.ac_sqr_0_out);
ac_0.propagate(buffer.fc_0_out, buffer.ac_0_out); ac_0.propagate(buffer.fc_0_out, buffer.ac_0_out);
std::memcpy(buffer.ac_sqr_0_out + FC_0_OUTPUTS, buffer.ac_0_out, FC_0_OUTPUTS * sizeof(decltype(ac_0)::OutputType)); std::memcpy(buffer.ac_sqr_0_out + FC_0_OUTPUTS, buffer.ac_0_out,
FC_0_OUTPUTS * sizeof(typename decltype(ac_0)::OutputType));
fc_1.propagate(buffer.ac_sqr_0_out, buffer.fc_1_out); fc_1.propagate(buffer.ac_sqr_0_out, buffer.fc_1_out);
ac_1.propagate(buffer.fc_1_out, buffer.ac_1_out); ac_1.propagate(buffer.fc_1_out, buffer.ac_1_out);
fc_2.propagate(buffer.ac_1_out, buffer.fc_2_out); fc_2.propagate(buffer.ac_1_out, buffer.fc_2_out);
// buffer.fc_0_out[FC_0_OUTPUTS] is such that 1.0 is equal to 127*(1<<WeightScaleBits) in quantized form // buffer.fc_0_out[FC_0_OUTPUTS] is such that 1.0 is equal to 127*(1<<WeightScaleBits) in
// but we want 1.0 to be equal to 600*OutputScale // quantized form, but we want 1.0 to be equal to 600*OutputScale
std::int32_t fwdOut = int(buffer.fc_0_out[FC_0_OUTPUTS]) * (600*OutputScale) / (127*(1<<WeightScaleBits)); std::int32_t fwdOut =
(buffer.fc_0_out[FC_0_OUTPUTS]) * (600 * OutputScale) / (127 * (1 << WeightScaleBits));
std::int32_t outputValue = buffer.fc_2_out[0] + fwdOut; std::int32_t outputValue = buffer.fc_2_out[0] + fwdOut;
return outputValue; return outputValue;
+75 -32
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,10 +21,14 @@
#ifndef NNUE_COMMON_H_INCLUDED #ifndef NNUE_COMMON_H_INCLUDED
#define NNUE_COMMON_H_INCLUDED #define NNUE_COMMON_H_INCLUDED
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <type_traits>
#include "../misc.h" // for IsLittleEndian #include "../misc.h"
#if defined(USE_AVX2) #if defined(USE_AVX2)
#include <immintrin.h> #include <immintrin.h>
@@ -38,9 +42,6 @@
#elif defined(USE_SSE2) #elif defined(USE_SSE2)
#include <emmintrin.h> #include <emmintrin.h>
#elif defined(USE_MMX)
#include <mmintrin.h>
#elif defined(USE_NEON) #elif defined(USE_NEON)
#include <arm_neon.h> #include <arm_neon.h>
#endif #endif
@@ -67,9 +68,6 @@ namespace Stockfish::Eval::NNUE {
#elif defined(USE_SSE2) #elif defined(USE_SSE2)
constexpr std::size_t SimdWidth = 16; constexpr std::size_t SimdWidth = 16;
#elif defined(USE_MMX)
constexpr std::size_t SimdWidth = 8;
#elif defined(USE_NEON) #elif defined(USE_NEON)
constexpr std::size_t SimdWidth = 16; constexpr std::size_t SimdWidth = 16;
#endif #endif
@@ -86,7 +84,8 @@ namespace Stockfish::Eval::NNUE {
return (n + base - 1) / base * base; return (n + base - 1) / base * base;
} }
// read_little_endian() is our utility to read an integer (signed or unsigned, any size)
// Utility to read an integer (signed or unsigned, any size)
// from a stream in little-endian order. We swap the byte order after the read if // from a stream in little-endian order. We swap the byte order after the read if
// necessary to return a result with the byte ordering of the compiling machine. // necessary to return a result with the byte ordering of the compiling machine.
template<typename IntType> template<typename IntType>
@@ -98,7 +97,7 @@ namespace Stockfish::Eval::NNUE {
else else
{ {
std::uint8_t u[sizeof(IntType)]; std::uint8_t u[sizeof(IntType)];
typename std::make_unsigned<IntType>::type v = 0; std::make_unsigned_t<IntType> v = 0;
stream.read(reinterpret_cast<char*>(u), sizeof(IntType)); stream.read(reinterpret_cast<char*>(u), sizeof(IntType));
for (std::size_t i = 0; i < sizeof(IntType); ++i) for (std::size_t i = 0; i < sizeof(IntType); ++i)
@@ -110,9 +109,10 @@ namespace Stockfish::Eval::NNUE {
return result; return result;
} }
// write_little_endian() is our utility to write an integer (signed or unsigned, any size)
// Utility to write an integer (signed or unsigned, any size)
// to a stream in little-endian order. We swap the byte order before the write if // to a stream in little-endian order. We swap the byte order before the write if
// necessary to always write in little endian order, independently of the byte // necessary to always write in little-endian order, independently of the byte
// ordering of the compiling machine. // ordering of the compiling machine.
template<typename IntType> template<typename IntType>
inline void write_little_endian(std::ostream& stream, IntType value) { inline void write_little_endian(std::ostream& stream, IntType value) {
@@ -122,7 +122,7 @@ namespace Stockfish::Eval::NNUE {
else else
{ {
std::uint8_t u[sizeof(IntType)]; std::uint8_t u[sizeof(IntType)];
typename std::make_unsigned<IntType>::type v = value; std::make_unsigned_t<IntType> v = value;
std::size_t i = 0; std::size_t i = 0;
// if constexpr to silence the warning about shift by 8 // if constexpr to silence the warning about shift by 8
@@ -130,18 +130,19 @@ namespace Stockfish::Eval::NNUE {
{ {
for (; i + 1 < sizeof(IntType); ++i) for (; i + 1 < sizeof(IntType); ++i)
{ {
u[i] = (std::uint8_t)v; u[i] = std::uint8_t(v);
v >>= 8; v >>= 8;
} }
} }
u[i] = (std::uint8_t)v; u[i] = std::uint8_t(v);
stream.write(reinterpret_cast<char*>(u), sizeof(IntType)); stream.write(reinterpret_cast<char*>(u), sizeof(IntType));
} }
} }
// read_little_endian(s, out, N) : read integers in bulk from a little indian stream.
// This reads N integers from stream s and put them in array out. // Read integers in bulk from a little-endian stream.
// This reads N integers from stream s and puts them in array out.
template<typename IntType> template<typename IntType>
inline void read_little_endian(std::istream& stream, IntType* out, std::size_t count) { inline void read_little_endian(std::istream& stream, IntType* out, std::size_t count) {
if (IsLittleEndian) if (IsLittleEndian)
@@ -151,7 +152,8 @@ namespace Stockfish::Eval::NNUE {
out[i] = read_little_endian<IntType>(stream); out[i] = read_little_endian<IntType>(stream);
} }
// write_little_endian(s, values, N) : write integers in bulk to a little indian stream.
// Write integers in bulk to a little-endian stream.
// This takes N integers from array values and writes them on stream s. // This takes N integers from array values and writes them on stream s.
template<typename IntType> template<typename IntType>
inline void write_little_endian(std::ostream& stream, const IntType* values, std::size_t count) { inline void write_little_endian(std::ostream& stream, const IntType* values, std::size_t count) {
@@ -162,77 +164,118 @@ namespace Stockfish::Eval::NNUE {
write_little_endian<IntType>(stream, values[i]); write_little_endian<IntType>(stream, values[i]);
} }
// Read N signed integers from the stream s, putting them in the array out.
// The stream is assumed to be compressed using the signed LEB128 format.
// See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme.
template<typename IntType> template<typename IntType>
inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count) { inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count) {
static_assert(std::is_signed_v<IntType>, "Not implemented for unsigned types");
// Check the presence of our LEB128 magic string
char leb128MagicString[Leb128MagicStringSize]; char leb128MagicString[Leb128MagicStringSize];
stream.read(leb128MagicString, Leb128MagicStringSize); stream.read(leb128MagicString, Leb128MagicStringSize);
assert(strncmp(Leb128MagicString, leb128MagicString, Leb128MagicStringSize) == 0); assert(strncmp(Leb128MagicString, leb128MagicString, Leb128MagicStringSize) == 0);
static_assert(std::is_signed_v<IntType>, "Not implemented for unsigned types");
const std::uint32_t BUF_SIZE = 4096; const std::uint32_t BUF_SIZE = 4096;
std::uint8_t buf[BUF_SIZE]; std::uint8_t buf[BUF_SIZE];
auto bytes_left = read_little_endian<std::uint32_t>(stream); auto bytes_left = read_little_endian<std::uint32_t>(stream);
std::uint32_t buf_pos = BUF_SIZE; std::uint32_t buf_pos = BUF_SIZE;
for (std::size_t i = 0; i < count; ++i) { for (std::size_t i = 0; i < count; ++i)
{
IntType result = 0; IntType result = 0;
size_t shift = 0; size_t shift = 0;
do { do
if (buf_pos == BUF_SIZE) { {
if (buf_pos == BUF_SIZE)
{
stream.read(reinterpret_cast<char*>(buf), std::min(bytes_left, BUF_SIZE)); stream.read(reinterpret_cast<char*>(buf), std::min(bytes_left, BUF_SIZE));
buf_pos = 0; buf_pos = 0;
} }
std::uint8_t byte = buf[buf_pos++]; std::uint8_t byte = buf[buf_pos++];
--bytes_left; --bytes_left;
result |= (byte & 0x7f) << shift; result |= (byte & 0x7f) << shift;
shift += 7; shift += 7;
if ((byte & 0x80) == 0) {
out[i] = sizeof(IntType) * 8 <= shift || (byte & 0x40) == 0 ? result : result | ~((1 << shift) - 1); if ((byte & 0x80) == 0)
{
out[i] = (sizeof(IntType) * 8 <= shift || (byte & 0x40) == 0)
? result
: result | ~((1 << shift) - 1);
break; break;
} }
} while (shift < sizeof(IntType) * 8); } while (shift < sizeof(IntType) * 8);
} }
assert(bytes_left == 0); assert(bytes_left == 0);
} }
// Write signed integers to a stream with LEB128 compression.
// This takes N integers from array values, compresses them with
// the LEB128 algorithm and writes the result on the stream s.
// See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme.
template<typename IntType> template<typename IntType>
inline void write_leb_128(std::ostream& stream, const IntType* values, std::size_t count) { inline void write_leb_128(std::ostream& stream, const IntType* values, std::size_t count) {
static_assert(std::is_signed_v<IntType>, "Not implemented for unsigned types");
// Write our LEB128 magic string
stream.write(Leb128MagicString, Leb128MagicStringSize); stream.write(Leb128MagicString, Leb128MagicStringSize);
static_assert(std::is_signed_v<IntType>, "Not implemented for unsigned types");
std::uint32_t byte_count = 0; std::uint32_t byte_count = 0;
for (std::size_t i = 0; i < count; ++i) { for (std::size_t i = 0; i < count; ++i)
{
IntType value = values[i]; IntType value = values[i];
std::uint8_t byte; std::uint8_t byte;
do { do
{
byte = value & 0x7f; byte = value & 0x7f;
value >>= 7; value >>= 7;
++byte_count; ++byte_count;
} while ((byte & 0x40) == 0 ? value != 0 : value != -1); } while ((byte & 0x40) == 0 ? value != 0 : value != -1);
} }
write_little_endian(stream, byte_count); write_little_endian(stream, byte_count);
const std::uint32_t BUF_SIZE = 4096; const std::uint32_t BUF_SIZE = 4096;
std::uint8_t buf[BUF_SIZE]; std::uint8_t buf[BUF_SIZE];
std::uint32_t buf_pos = 0; std::uint32_t buf_pos = 0;
auto flush = [&]() { auto flush = [&]() {
if (buf_pos > 0) { if (buf_pos > 0)
{
stream.write(reinterpret_cast<char*>(buf), buf_pos); stream.write(reinterpret_cast<char*>(buf), buf_pos);
buf_pos = 0; buf_pos = 0;
} }
}; };
auto write = [&](std::uint8_t byte) { auto write = [&](std::uint8_t byte) {
buf[buf_pos++] = byte; buf[buf_pos++] = byte;
if (buf_pos == BUF_SIZE) flush(); if (buf_pos == BUF_SIZE)
flush();
}; };
for (std::size_t i = 0; i < count; ++i) {
for (std::size_t i = 0; i < count; ++i)
{
IntType value = values[i]; IntType value = values[i];
while (true) { while (true)
{
std::uint8_t byte = value & 0x7f; std::uint8_t byte = value & 0x7f;
value >>= 7; value >>= 7;
if ((byte & 0x40) == 0 ? value == 0 : value == -1) { if ((byte & 0x40) == 0 ? value == 0 : value == -1)
{
write(byte); write(byte);
break; break;
} }
write(byte | 0x80); write(byte | 0x80);
} }
} }
flush(); flush();
} }
+237 -141
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,11 +21,18 @@
#ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED
#define NNUE_FEATURE_TRANSFORMER_H_INCLUDED #define NNUE_FEATURE_TRANSFORMER_H_INCLUDED
#include "nnue_common.h" #include <algorithm>
#include "nnue_architecture.h" #include <cassert>
#include <cstdint>
#include <cstring>
#include <iosfwd>
#include <utility>
#include <cstring> // std::memset() #include "../position.h"
#include <utility> // std::pair #include "../types.h"
#include "nnue_accumulator.h"
#include "nnue_architecture.h"
#include "nnue_common.h"
namespace Stockfish::Eval::NNUE { namespace Stockfish::Eval::NNUE {
@@ -62,7 +69,7 @@ namespace Stockfish::Eval::NNUE {
#define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b) #define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b)
#define vec_sub_psqt_32(a, b) _mm256_sub_epi32(a, b) #define vec_sub_psqt_32(a, b) _mm256_sub_epi32(a, b)
#define vec_zero_psqt() _mm256_setzero_si256() #define vec_zero_psqt() _mm256_setzero_si256()
#define NumRegistersSIMD 32 #define NumRegistersSIMD 16
#define MaxChunkSize 64 #define MaxChunkSize 64
#elif USE_AVX2 #elif USE_AVX2
@@ -110,34 +117,6 @@ namespace Stockfish::Eval::NNUE {
#define NumRegistersSIMD (Is64Bit ? 16 : 8) #define NumRegistersSIMD (Is64Bit ? 16 : 8)
#define MaxChunkSize 16 #define MaxChunkSize 16
#elif USE_MMX
using vec_t = __m64;
using psqt_vec_t = __m64;
#define vec_load(a) (*(a))
#define vec_store(a,b) *(a)=(b)
#define vec_add_16(a,b) _mm_add_pi16(a,b)
#define vec_sub_16(a,b) _mm_sub_pi16(a,b)
#define vec_mul_16(a,b) _mm_mullo_pi16(a,b)
#define vec_zero() _mm_setzero_si64()
#define vec_set_16(a) _mm_set1_pi16(a)
inline vec_t vec_max_16(vec_t a,vec_t b){
vec_t comparison = _mm_cmpgt_pi16(a,b);
return _mm_or_si64(_mm_and_si64(comparison, a), _mm_andnot_si64(comparison, b));
}
inline vec_t vec_min_16(vec_t a,vec_t b){
vec_t comparison = _mm_cmpgt_pi16(a,b);
return _mm_or_si64(_mm_and_si64(comparison, b), _mm_andnot_si64(comparison, a));
}
#define vec_msb_pack_16(a,b) _mm_packs_pi16(_mm_srli_pi16(a,7),_mm_srli_pi16(b,7))
#define vec_load_psqt(a) (*(a))
#define vec_store_psqt(a,b) *(a)=(b)
#define vec_add_psqt_32(a,b) _mm_add_pi32(a,b)
#define vec_sub_psqt_32(a,b) _mm_sub_pi32(a,b)
#define vec_zero_psqt() _mm_setzero_si64()
#define vec_cleanup() _mm_empty()
#define NumRegistersSIMD 8
#define MaxChunkSize 8
#elif USE_NEON #elif USE_NEON
using vec_t = int16x8_t; using vec_t = int16x8_t;
using psqt_vec_t = int32x4_t; using psqt_vec_t = int32x4_t;
@@ -146,7 +125,8 @@ namespace Stockfish::Eval::NNUE {
#define vec_add_16(a, b) vaddq_s16(a, b) #define vec_add_16(a, b) vaddq_s16(a, b)
#define vec_sub_16(a, b) vsubq_s16(a, b) #define vec_sub_16(a, b) vsubq_s16(a, b)
#define vec_mul_16(a, b) vmulq_s16(a, b) #define vec_mul_16(a, b) vmulq_s16(a, b)
#define vec_zero() vec_t{0} #define vec_zero() \
vec_t { 0 }
#define vec_set_16(a) vdupq_n_s16(a) #define vec_set_16(a) vdupq_n_s16(a)
#define vec_max_16(a, b) vmaxq_s16(a, b) #define vec_max_16(a, b) vmaxq_s16(a, b)
#define vec_min_16(a, b) vminq_s16(a, b) #define vec_min_16(a, b) vminq_s16(a, b)
@@ -160,7 +140,8 @@ namespace Stockfish::Eval::NNUE {
#define vec_store_psqt(a, b) *(a) = (b) #define vec_store_psqt(a, b) *(a) = (b)
#define vec_add_psqt_32(a, b) vaddq_s32(a, b) #define vec_add_psqt_32(a, b) vaddq_s32(a, b)
#define vec_sub_psqt_32(a, b) vsubq_s32(a, b) #define vec_sub_psqt_32(a, b) vsubq_s32(a, b)
#define vec_zero_psqt() psqt_vec_t{0} #define vec_zero_psqt() \
psqt_vec_t { 0 }
#define NumRegistersSIMD 16 #define NumRegistersSIMD 16
#define MaxChunkSize 16 #define MaxChunkSize 16
@@ -182,12 +163,8 @@ namespace Stockfish::Eval::NNUE {
#pragma GCC diagnostic ignored "-Wignored-attributes" #pragma GCC diagnostic ignored "-Wignored-attributes"
#endif #endif
template <typename SIMDRegisterType, template<typename SIMDRegisterType, typename LaneType, int NumLanes, int MaxRegisters>
typename LaneType, static constexpr int BestRegisterCount() {
int NumLanes,
int MaxRegisters>
static constexpr int BestRegisterCount()
{
#define RegisterSize sizeof(SIMDRegisterType) #define RegisterSize sizeof(SIMDRegisterType)
#define LaneSize sizeof(LaneType) #define LaneSize sizeof(LaneType)
@@ -209,17 +186,15 @@ namespace Stockfish::Eval::NNUE {
return 1; return 1;
} }
static constexpr int NumRegs = BestRegisterCount<vec_t, WeightType, TransformedFeatureDimensions, NumRegistersSIMD>();
static constexpr int NumPsqtRegs = BestRegisterCount<psqt_vec_t, PSQTWeightType, PSQTBuckets, NumRegistersSIMD>();
#if defined(__GNUC__) #if defined(__GNUC__)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
#endif #endif
// Input feature converter // Input feature converter
template<IndexType TransformedFeatureDimensions,
Accumulator<TransformedFeatureDimensions> StateInfo::*accPtr>
class FeatureTransformer { class FeatureTransformer {
private: private:
@@ -227,6 +202,11 @@ namespace Stockfish::Eval::NNUE {
static constexpr IndexType HalfDimensions = TransformedFeatureDimensions; static constexpr IndexType HalfDimensions = TransformedFeatureDimensions;
#ifdef VECTOR #ifdef VECTOR
static constexpr int NumRegs =
BestRegisterCount<vec_t, WeightType, TransformedFeatureDimensions, NumRegistersSIMD>();
static constexpr int NumPsqtRegs =
BestRegisterCount<psqt_vec_t, PSQTWeightType, PSQTBuckets, NumRegistersSIMD>();
static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2; static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2;
static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4; static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4;
static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions"); static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions");
@@ -242,8 +222,7 @@ namespace Stockfish::Eval::NNUE {
static constexpr IndexType OutputDimensions = HalfDimensions; static constexpr IndexType OutputDimensions = HalfDimensions;
// Size of forward propagation buffer // Size of forward propagation buffer
static constexpr std::size_t BufferSize = static constexpr std::size_t BufferSize = OutputDimensions * sizeof(OutputType);
OutputDimensions * sizeof(OutputType);
// Hash value embedded in the evaluation file // Hash value embedded in the evaluation file
static constexpr std::uint32_t get_hash_value() { static constexpr std::uint32_t get_hash_value() {
@@ -271,19 +250,21 @@ namespace Stockfish::Eval::NNUE {
} }
// Convert input features // Convert input features
std::int32_t transform(const Position& pos, OutputType* output, int bucket) const { std::int32_t
update_accumulator<WHITE>(pos); transform(const Position& pos, OutputType* output, int bucket, bool psqtOnly) const {
update_accumulator<BLACK>(pos); update_accumulator<WHITE>(pos, psqtOnly);
update_accumulator<BLACK>(pos, psqtOnly);
const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()};
const auto& accumulation = pos.state()->accumulator.accumulation; const auto& psqtAccumulation = (pos.state()->*accPtr).psqtAccumulation;
const auto& psqtAccumulation = pos.state()->accumulator.psqtAccumulation; const auto psqt =
(psqtAccumulation[perspectives[0]][bucket] - psqtAccumulation[perspectives[1]][bucket])
/ 2;
const auto psqt = ( if (psqtOnly)
psqtAccumulation[perspectives[0]][bucket] return psqt;
- psqtAccumulation[perspectives[1]][bucket]
) / 2;
const auto& accumulation = (pos.state()->*accPtr).accumulation;
for (IndexType p = 0; p < 2; ++p) for (IndexType p = 0; p < 2; ++p)
{ {
@@ -299,10 +280,11 @@ namespace Stockfish::Eval::NNUE {
vec_t One = vec_set_16(127); vec_t One = vec_set_16(127);
const vec_t* in0 = reinterpret_cast<const vec_t*>(&(accumulation[perspectives[p]][0])); const vec_t* in0 = reinterpret_cast<const vec_t*>(&(accumulation[perspectives[p]][0]));
const vec_t* in1 = reinterpret_cast<const vec_t*>(&(accumulation[perspectives[p]][HalfDimensions / 2])); const vec_t* in1 =
reinterpret_cast<const vec_t*>(&(accumulation[perspectives[p]][HalfDimensions / 2]));
vec_t* out = reinterpret_cast<vec_t*>(output + offset); vec_t* out = reinterpret_cast<vec_t*>(output + offset);
for (IndexType j = 0; j < NumOutputChunks; j += 1) for (IndexType j = 0; j < NumOutputChunks; ++j)
{ {
const vec_t sum0a = vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero); const vec_t sum0a = vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero);
const vec_t sum0b = vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero); const vec_t sum0b = vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero);
@@ -317,37 +299,38 @@ namespace Stockfish::Eval::NNUE {
#else #else
for (IndexType j = 0; j < HalfDimensions / 2; ++j) { for (IndexType j = 0; j < HalfDimensions / 2; ++j)
{
BiasType sum0 = accumulation[static_cast<int>(perspectives[p])][j + 0]; BiasType sum0 = accumulation[static_cast<int>(perspectives[p])][j + 0];
BiasType sum1 = accumulation[static_cast<int>(perspectives[p])][j + HalfDimensions / 2]; BiasType sum1 =
sum0 = std::max<int>(0, std::min<int>(127, sum0)); accumulation[static_cast<int>(perspectives[p])][j + HalfDimensions / 2];
sum1 = std::max<int>(0, std::min<int>(127, sum1)); sum0 = std::clamp<BiasType>(sum0, 0, 127);
output[offset + j] = static_cast<OutputType>(sum0 * sum1 / 128); sum1 = std::clamp<BiasType>(sum1, 0, 127);
output[offset + j] = static_cast<OutputType>(unsigned(sum0 * sum1) / 128);
} }
#endif #endif
} }
#if defined(vec_cleanup)
vec_cleanup();
#endif
return psqt; return psqt;
} // end of function transform() } // end of function transform()
void hint_common_access(const Position& pos) const { void hint_common_access(const Position& pos, bool psqtOnly) const {
hint_common_access_for_perspective<WHITE>(pos); hint_common_access_for_perspective<WHITE>(pos, psqtOnly);
hint_common_access_for_perspective<BLACK>(pos); hint_common_access_for_perspective<BLACK>(pos, psqtOnly);
} }
private: private:
template<Color Perspective> template<Color Perspective>
[[nodiscard]] std::pair<StateInfo*, StateInfo*> try_find_computed_accumulator(const Position& pos) const { [[nodiscard]] std::pair<StateInfo*, StateInfo*>
try_find_computed_accumulator(const Position& pos, bool psqtOnly) const {
// Look for a usable accumulator of an earlier position. We keep track // Look for a usable accumulator of an earlier position. We keep track
// of the estimated gain in terms of features to be added/subtracted. // of the estimated gain in terms of features to be added/subtracted.
StateInfo *st = pos.state(), *next = nullptr; StateInfo *st = pos.state(), *next = nullptr;
int gain = FeatureSet::refresh_cost(pos); int gain = FeatureSet::refresh_cost(pos);
while (st->previous && !st->accumulator.computed[Perspective]) while (st->previous
&& (!(st->*accPtr).computedPSQT[Perspective]
|| (!psqtOnly && !(st->*accPtr).computed[Perspective])))
{ {
// This governs when a full feature refresh is needed and how many // This governs when a full feature refresh is needed and how many
// updates are better than just one full refresh. // updates are better than just one full refresh.
@@ -362,10 +345,15 @@ namespace Stockfish::Eval::NNUE {
// NOTE: The parameter states_to_update is an array of position states, ending with nullptr. // NOTE: The parameter states_to_update is an array of position states, ending with nullptr.
// All states must be sequential, that is states_to_update[i] must either be reachable // All states must be sequential, that is states_to_update[i] must either be reachable
// by repeatedly applying ->previous from states_to_update[i+1] or states_to_update[i] == nullptr. // by repeatedly applying ->previous from states_to_update[i+1] or
// computed_st must be reachable by repeatedly applying ->previous on states_to_update[0], if not nullptr. // states_to_update[i] == nullptr.
// computed_st must be reachable by repeatedly applying ->previous on
// states_to_update[0], if not nullptr.
template<Color Perspective, size_t N> template<Color Perspective, size_t N>
void update_accumulator_incremental(const Position& pos, StateInfo* computed_st, StateInfo* states_to_update[N]) const { void update_accumulator_incremental(const Position& pos,
StateInfo* computed_st,
StateInfo* states_to_update[N],
bool psqtOnly) const {
static_assert(N > 0); static_assert(N > 0);
assert(states_to_update[N - 1] == nullptr); assert(states_to_update[N - 1] == nullptr);
@@ -386,12 +374,14 @@ namespace Stockfish::Eval::NNUE {
// The size must be enough to contain the largest possible update. // The size must be enough to contain the largest possible update.
// That might depend on the feature set and generally relies on the // That might depend on the feature set and generally relies on the
// feature set's update cost calculation to be correct and never // feature set's update cost calculation to be correct and never allow
// allow updates with more added/removed features than MaxActiveDimensions. // updates with more added/removed features than MaxActiveDimensions.
FeatureSet::IndexList removed[N - 1], added[N - 1]; FeatureSet::IndexList removed[N - 1], added[N - 1];
{ {
int i = N-2; // last potential state to update. Skip last element because it must be nullptr. int i =
N
- 2; // Last potential state to update. Skip last element because it must be nullptr.
while (states_to_update[i] == nullptr) while (states_to_update[i] == nullptr)
--i; --i;
@@ -399,13 +389,14 @@ namespace Stockfish::Eval::NNUE {
for (; i >= 0; --i) for (; i >= 0; --i)
{ {
states_to_update[i]->accumulator.computed[Perspective] = true; (states_to_update[i]->*accPtr).computed[Perspective] = !psqtOnly;
(states_to_update[i]->*accPtr).computedPSQT[Perspective] = true;
StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1]; const StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1];
for (; st2 != end_state; st2 = st2->previous) for (; st2 != end_state; st2 = st2->previous)
FeatureSet::append_changed_indices<Perspective>( FeatureSet::append_changed_indices<Perspective>(ksq, st2->dirtyPiece,
ksq, st2->dirtyPiece, removed[i], added[i]); removed[i], added[i]);
} }
} }
@@ -413,13 +404,81 @@ namespace Stockfish::Eval::NNUE {
// Now update the accumulators listed in states_to_update[], where the last element is a sentinel. // Now update the accumulators listed in states_to_update[], where the last element is a sentinel.
#ifdef VECTOR #ifdef VECTOR
if (states_to_update[1] == nullptr && (removed[0].size() == 1 || removed[0].size() == 2)
&& added[0].size() == 1)
{
assert(states_to_update[0]);
if (!psqtOnly)
{
auto accIn =
reinterpret_cast<const vec_t*>(&(st->*accPtr).accumulation[Perspective][0]);
auto accOut = reinterpret_cast<vec_t*>(
&(states_to_update[0]->*accPtr).accumulation[Perspective][0]);
const IndexType offsetR0 = HalfDimensions * removed[0][0];
auto columnR0 = reinterpret_cast<const vec_t*>(&weights[offsetR0]);
const IndexType offsetA = HalfDimensions * added[0][0];
auto columnA = reinterpret_cast<const vec_t*>(&weights[offsetA]);
if (removed[0].size() == 1)
{
for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t);
++k)
accOut[k] = vec_add_16(vec_sub_16(accIn[k], columnR0[k]), columnA[k]);
}
else
{
const IndexType offsetR1 = HalfDimensions * removed[0][1];
auto columnR1 = reinterpret_cast<const vec_t*>(&weights[offsetR1]);
for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t);
++k)
accOut[k] = vec_sub_16(vec_add_16(accIn[k], columnA[k]),
vec_add_16(columnR0[k], columnR1[k]));
}
}
auto accPsqtIn =
reinterpret_cast<const psqt_vec_t*>(&(st->*accPtr).psqtAccumulation[Perspective][0]);
auto accPsqtOut = reinterpret_cast<psqt_vec_t*>(
&(states_to_update[0]->*accPtr).psqtAccumulation[Perspective][0]);
const IndexType offsetPsqtR0 = PSQTBuckets * removed[0][0];
auto columnPsqtR0 = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtR0]);
const IndexType offsetPsqtA = PSQTBuckets * added[0][0];
auto columnPsqtA = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtA]);
if (removed[0].size() == 1)
{
for (std::size_t k = 0; k < PSQTBuckets * sizeof(std::int32_t) / sizeof(psqt_vec_t);
++k)
accPsqtOut[k] = vec_add_psqt_32(vec_sub_psqt_32(accPsqtIn[k], columnPsqtR0[k]),
columnPsqtA[k]);
}
else
{
const IndexType offsetPsqtR1 = PSQTBuckets * removed[0][1];
auto columnPsqtR1 = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtR1]);
for (std::size_t k = 0; k < PSQTBuckets * sizeof(std::int32_t) / sizeof(psqt_vec_t);
++k)
accPsqtOut[k] =
vec_sub_psqt_32(vec_add_psqt_32(accPsqtIn[k], columnPsqtA[k]),
vec_add_psqt_32(columnPsqtR0[k], columnPsqtR1[k]));
}
}
else
{
if (!psqtOnly)
for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
{ {
// Load accumulator // Load accumulator
auto accTile = reinterpret_cast<vec_t*>( auto accTileIn = reinterpret_cast<const vec_t*>(
&st->accumulator.accumulation[Perspective][j * TileHeight]); &(st->*accPtr).accumulation[Perspective][j * TileHeight]);
for (IndexType k = 0; k < NumRegs; ++k) for (IndexType k = 0; k < NumRegs; ++k)
acc[k] = vec_load(&accTile[k]); acc[k] = vec_load(&accTileIn[k]);
for (IndexType i = 0; states_to_update[i]; ++i) for (IndexType i = 0; states_to_update[i]; ++i)
{ {
@@ -442,20 +501,21 @@ namespace Stockfish::Eval::NNUE {
} }
// Store accumulator // Store accumulator
accTile = reinterpret_cast<vec_t*>( auto accTileOut =
&states_to_update[i]->accumulator.accumulation[Perspective][j * TileHeight]); reinterpret_cast<vec_t*>(&(states_to_update[i]->*accPtr)
.accumulation[Perspective][j * TileHeight]);
for (IndexType k = 0; k < NumRegs; ++k) for (IndexType k = 0; k < NumRegs; ++k)
vec_store(&accTile[k], acc[k]); vec_store(&accTileOut[k], acc[k]);
} }
} }
for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j)
{ {
// Load accumulator // Load accumulator
auto accTilePsqt = reinterpret_cast<psqt_vec_t*>( auto accTilePsqtIn = reinterpret_cast<const psqt_vec_t*>(
&st->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); &(st->*accPtr).psqtAccumulation[Perspective][j * PsqtTileHeight]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k) for (std::size_t k = 0; k < NumPsqtRegs; ++k)
psqt[k] = vec_load_psqt(&accTilePsqt[k]); psqt[k] = vec_load_psqt(&accTilePsqtIn[k]);
for (IndexType i = 0; states_to_update[i]; ++i) for (IndexType i = 0; states_to_update[i]; ++i)
{ {
@@ -478,58 +538,63 @@ namespace Stockfish::Eval::NNUE {
} }
// Store accumulator // Store accumulator
accTilePsqt = reinterpret_cast<psqt_vec_t*>( auto accTilePsqtOut = reinterpret_cast<psqt_vec_t*>(
&states_to_update[i]->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); &(states_to_update[i]->*accPtr)
.psqtAccumulation[Perspective][j * PsqtTileHeight]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k) for (std::size_t k = 0; k < NumPsqtRegs; ++k)
vec_store_psqt(&accTilePsqt[k], psqt[k]); vec_store_psqt(&accTilePsqtOut[k], psqt[k]);
}
} }
} }
#else #else
for (IndexType i = 0; states_to_update[i]; ++i) for (IndexType i = 0; states_to_update[i]; ++i)
{ {
std::memcpy(states_to_update[i]->accumulator.accumulation[Perspective], if (!psqtOnly)
st->accumulator.accumulation[Perspective], std::memcpy((states_to_update[i]->*accPtr).accumulation[Perspective],
(st->*accPtr).accumulation[Perspective],
HalfDimensions * sizeof(BiasType)); HalfDimensions * sizeof(BiasType));
for (std::size_t k = 0; k < PSQTBuckets; ++k) for (std::size_t k = 0; k < PSQTBuckets; ++k)
states_to_update[i]->accumulator.psqtAccumulation[Perspective][k] = st->accumulator.psqtAccumulation[Perspective][k]; (states_to_update[i]->*accPtr).psqtAccumulation[Perspective][k] =
(st->*accPtr).psqtAccumulation[Perspective][k];
st = states_to_update[i]; st = states_to_update[i];
// Difference calculation for the deactivated features // Difference calculation for the deactivated features
for (const auto index : removed[i]) for (const auto index : removed[i])
{
if (!psqtOnly)
{ {
const IndexType offset = HalfDimensions * index; const IndexType offset = HalfDimensions * index;
for (IndexType j = 0; j < HalfDimensions; ++j) for (IndexType j = 0; j < HalfDimensions; ++j)
st->accumulator.accumulation[Perspective][j] -= weights[offset + j]; (st->*accPtr).accumulation[Perspective][j] -= weights[offset + j];
}
for (std::size_t k = 0; k < PSQTBuckets; ++k) for (std::size_t k = 0; k < PSQTBuckets; ++k)
st->accumulator.psqtAccumulation[Perspective][k] -= psqtWeights[index * PSQTBuckets + k]; (st->*accPtr).psqtAccumulation[Perspective][k] -=
psqtWeights[index * PSQTBuckets + k];
} }
// Difference calculation for the activated features // Difference calculation for the activated features
for (const auto index : added[i]) for (const auto index : added[i])
{
if (!psqtOnly)
{ {
const IndexType offset = HalfDimensions * index; const IndexType offset = HalfDimensions * index;
for (IndexType j = 0; j < HalfDimensions; ++j) for (IndexType j = 0; j < HalfDimensions; ++j)
st->accumulator.accumulation[Perspective][j] += weights[offset + j]; (st->*accPtr).accumulation[Perspective][j] += weights[offset + j];
}
for (std::size_t k = 0; k < PSQTBuckets; ++k) for (std::size_t k = 0; k < PSQTBuckets; ++k)
st->accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; (st->*accPtr).psqtAccumulation[Perspective][k] +=
psqtWeights[index * PSQTBuckets + k];
} }
} }
#endif #endif
#if defined(USE_MMX)
_mm_empty();
#endif
} }
template<Color Perspective> template<Color Perspective>
void update_accumulator_refresh(const Position& pos) const { void update_accumulator_refresh(const Position& pos, bool psqtOnly) const {
#ifdef VECTOR #ifdef VECTOR
// Gcc-10.2 unnecessarily spills AVX2 registers if this array // Gcc-10.2 unnecessarily spills AVX2 registers if this array
// is defined in the VECTOR code below, once in each branch // is defined in the VECTOR code below, once in each branch
@@ -540,21 +605,36 @@ namespace Stockfish::Eval::NNUE {
// Refresh the accumulator // Refresh the accumulator
// Could be extracted to a separate function because it's done in 2 places, // Could be extracted to a separate function because it's done in 2 places,
// but it's unclear if compilers would correctly handle register allocation. // but it's unclear if compilers would correctly handle register allocation.
auto& accumulator = pos.state()->accumulator; auto& accumulator = pos.state()->*accPtr;
accumulator.computed[Perspective] = true; accumulator.computed[Perspective] = !psqtOnly;
accumulator.computedPSQT[Perspective] = true;
FeatureSet::IndexList active; FeatureSet::IndexList active;
FeatureSet::append_active_indices<Perspective>(pos, active); FeatureSet::append_active_indices<Perspective>(pos, active);
#ifdef VECTOR #ifdef VECTOR
if (!psqtOnly)
for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
{ {
auto biasesTile = reinterpret_cast<const vec_t*>( auto biasesTile = reinterpret_cast<const vec_t*>(&biases[j * TileHeight]);
&biases[j * TileHeight]);
for (IndexType k = 0; k < NumRegs; ++k) for (IndexType k = 0; k < NumRegs; ++k)
acc[k] = biasesTile[k]; acc[k] = biasesTile[k];
for (const auto index : active) int i = 0;
for (; i < int(active.size()) - 1; i += 2)
{ {
IndexType index0 = active[i];
IndexType index1 = active[i + 1];
const IndexType offset0 = HalfDimensions * index0 + j * TileHeight;
const IndexType offset1 = HalfDimensions * index1 + j * TileHeight;
auto column0 = reinterpret_cast<const vec_t*>(&weights[offset0]);
auto column1 = reinterpret_cast<const vec_t*>(&weights[offset1]);
for (unsigned k = 0; k < NumRegs; ++k)
acc[k] = vec_add_16(acc[k], vec_add_16(column0[k], column1[k]));
}
for (; i < int(active.size()); ++i)
{
IndexType index = active[i];
const IndexType offset = HalfDimensions * index + j * TileHeight; const IndexType offset = HalfDimensions * index + j * TileHeight;
auto column = reinterpret_cast<const vec_t*>(&weights[offset]); auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
@@ -562,8 +642,8 @@ namespace Stockfish::Eval::NNUE {
acc[k] = vec_add_16(acc[k], column[k]); acc[k] = vec_add_16(acc[k], column[k]);
} }
auto accTile = reinterpret_cast<vec_t*>( auto accTile =
&accumulator.accumulation[Perspective][j * TileHeight]); reinterpret_cast<vec_t*>(&accumulator.accumulation[Perspective][j * TileHeight]);
for (unsigned k = 0; k < NumRegs; k++) for (unsigned k = 0; k < NumRegs; k++)
vec_store(&accTile[k], acc[k]); vec_store(&accTile[k], acc[k]);
} }
@@ -573,8 +653,23 @@ namespace Stockfish::Eval::NNUE {
for (std::size_t k = 0; k < NumPsqtRegs; ++k) for (std::size_t k = 0; k < NumPsqtRegs; ++k)
psqt[k] = vec_zero_psqt(); psqt[k] = vec_zero_psqt();
for (const auto index : active) int i = 0;
for (; i < int(active.size()) - 1; i += 2)
{ {
IndexType index0 = active[i];
IndexType index1 = active[i + 1];
const IndexType offset0 = PSQTBuckets * index0 + j * PsqtTileHeight;
const IndexType offset1 = PSQTBuckets * index1 + j * PsqtTileHeight;
auto columnPsqt0 = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset0]);
auto columnPsqt1 = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset1]);
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
psqt[k] =
vec_add_psqt_32(psqt[k], vec_add_psqt_32(columnPsqt0[k], columnPsqt1[k]));
}
for (; i < int(active.size()); ++i)
{
IndexType index = active[i];
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]); auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
@@ -589,6 +684,7 @@ namespace Stockfish::Eval::NNUE {
} }
#else #else
if (!psqtOnly)
std::memcpy(accumulator.accumulation[Perspective], biases, std::memcpy(accumulator.accumulation[Perspective], biases,
HalfDimensions * sizeof(BiasType)); HalfDimensions * sizeof(BiasType));
@@ -596,24 +692,23 @@ namespace Stockfish::Eval::NNUE {
accumulator.psqtAccumulation[Perspective][k] = 0; accumulator.psqtAccumulation[Perspective][k] = 0;
for (const auto index : active) for (const auto index : active)
{
if (!psqtOnly)
{ {
const IndexType offset = HalfDimensions * index; const IndexType offset = HalfDimensions * index;
for (IndexType j = 0; j < HalfDimensions; ++j) for (IndexType j = 0; j < HalfDimensions; ++j)
accumulator.accumulation[Perspective][j] += weights[offset + j]; accumulator.accumulation[Perspective][j] += weights[offset + j];
}
for (std::size_t k = 0; k < PSQTBuckets; ++k) for (std::size_t k = 0; k < PSQTBuckets; ++k)
accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; accumulator.psqtAccumulation[Perspective][k] +=
psqtWeights[index * PSQTBuckets + k];
} }
#endif #endif
#if defined(USE_MMX)
_mm_empty();
#endif
} }
template<Color Perspective> template<Color Perspective>
void hint_common_access_for_perspective(const Position& pos) const { void hint_common_access_for_perspective(const Position& pos, bool psqtOnly) const {
// Works like update_accumulator, but performs less work. // Works like update_accumulator, but performs less work.
// Updates ONLY the accumulator for pos. // Updates ONLY the accumulator for pos.
@@ -621,29 +716,31 @@ namespace Stockfish::Eval::NNUE {
// Look for a usable accumulator of an earlier position. We keep track // Look for a usable accumulator of an earlier position. We keep track
// of the estimated gain in terms of features to be added/subtracted. // of the estimated gain in terms of features to be added/subtracted.
// Fast early exit. // Fast early exit.
if (pos.state()->accumulator.computed[Perspective]) if ((pos.state()->*accPtr).computed[Perspective]
|| (psqtOnly && (pos.state()->*accPtr).computedPSQT[Perspective]))
return; return;
auto [oldest_st, _] = try_find_computed_accumulator<Perspective>(pos); auto [oldest_st, _] = try_find_computed_accumulator<Perspective>(pos, psqtOnly);
if (oldest_st->accumulator.computed[Perspective]) if ((oldest_st->*accPtr).computed[Perspective]
|| (psqtOnly && (oldest_st->*accPtr).computedPSQT[Perspective]))
{ {
// Only update current position accumulator to minimize work. // Only update current position accumulator to minimize work.
StateInfo* states_to_update[2] = {pos.state(), nullptr}; StateInfo* states_to_update[2] = {pos.state(), nullptr};
update_accumulator_incremental<Perspective, 2>(pos, oldest_st, states_to_update); update_accumulator_incremental<Perspective, 2>(pos, oldest_st, states_to_update,
psqtOnly);
} }
else else
{ update_accumulator_refresh<Perspective>(pos, psqtOnly);
update_accumulator_refresh<Perspective>(pos);
}
} }
template<Color Perspective> template<Color Perspective>
void update_accumulator(const Position& pos) const { void update_accumulator(const Position& pos, bool psqtOnly) const {
auto [oldest_st, next] = try_find_computed_accumulator<Perspective>(pos); auto [oldest_st, next] = try_find_computed_accumulator<Perspective>(pos, psqtOnly);
if (oldest_st->accumulator.computed[Perspective]) if ((oldest_st->*accPtr).computed[Perspective]
|| (psqtOnly && (oldest_st->*accPtr).computedPSQT[Perspective]))
{ {
if (next == nullptr) if (next == nullptr)
return; return;
@@ -653,15 +750,14 @@ namespace Stockfish::Eval::NNUE {
// 1. for the current position // 1. for the current position
// 2. the next accumulator after the computed one // 2. the next accumulator after the computed one
// The heuristic may change in the future. // The heuristic may change in the future.
StateInfo *states_to_update[3] = StateInfo* states_to_update[3] = {next, next == pos.state() ? nullptr : pos.state(),
{ next, next == pos.state() ? nullptr : pos.state(), nullptr }; nullptr};
update_accumulator_incremental<Perspective, 3>(pos, oldest_st, states_to_update); update_accumulator_incremental<Perspective, 3>(pos, oldest_st, states_to_update,
psqtOnly);
} }
else else
{ update_accumulator_refresh<Perspective>(pos, psqtOnly);
update_accumulator_refresh<Perspective>(pos);
}
} }
alignas(CacheLineSize) BiasType biases[HalfDimensions]; alignas(CacheLineSize) BiasType biases[HalfDimensions];
+203
View File
@@ -0,0 +1,203 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Code for calculating NNUE evaluation function
#include "nnue_misc.h"
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iosfwd>
#include <iostream>
#include <sstream>
#include <string_view>
#include "../evaluate.h"
#include "../position.h"
#include "../types.h"
#include "../uci.h"
#include "network.h"
#include "nnue_accumulator.h"
namespace Stockfish::Eval::NNUE {
constexpr std::string_view PieceToChar(" PNBRQK pnbrqk");
void hint_common_parent_position(const Position& pos, const Networks& networks) {
int simpleEvalAbs = std::abs(simple_eval(pos, pos.side_to_move()));
if (simpleEvalAbs > Eval::SmallNetThreshold)
networks.small.hint_common_access(pos, simpleEvalAbs > Eval::PsqtOnlyThreshold);
else
networks.big.hint_common_access(pos, false);
}
namespace {
// Converts a Value into (centi)pawns and writes it in a buffer.
// The buffer must have capacity for at least 5 chars.
void format_cp_compact(Value v, char* buffer, const Position& pos) {
buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' ');
int cp = std::abs(UCI::to_cp(v, pos));
if (cp >= 10000)
{
buffer[1] = '0' + cp / 10000;
cp %= 10000;
buffer[2] = '0' + cp / 1000;
cp %= 1000;
buffer[3] = '0' + cp / 100;
buffer[4] = ' ';
}
else if (cp >= 1000)
{
buffer[1] = '0' + cp / 1000;
cp %= 1000;
buffer[2] = '0' + cp / 100;
cp %= 100;
buffer[3] = '.';
buffer[4] = '0' + cp / 10;
}
else
{
buffer[1] = '0' + cp / 100;
cp %= 100;
buffer[2] = '.';
buffer[3] = '0' + cp / 10;
cp %= 10;
buffer[4] = '0' + cp / 1;
}
}
// Converts a Value into pawns, always keeping two decimals
void format_cp_aligned_dot(Value v, std::stringstream& stream, const Position& pos) {
const double pawns = std::abs(0.01 * UCI::to_cp(v, pos));
stream << (v < 0 ? '-'
: v > 0 ? '+'
: ' ')
<< std::setiosflags(std::ios::fixed) << std::setw(6) << std::setprecision(2) << pawns;
}
}
// Returns a string with the value of each piece on a board,
// and a table for (PSQT, Layers) values bucket by bucket.
std::string trace(Position& pos, const Eval::NNUE::Networks& networks) {
std::stringstream ss;
char board[3 * 8 + 1][8 * 8 + 2];
std::memset(board, ' ', sizeof(board));
for (int row = 0; row < 3 * 8 + 1; ++row)
board[row][8 * 8 + 1] = '\0';
// A lambda to output one box of the board
auto writeSquare = [&board, &pos](File file, Rank rank, Piece pc, Value value) {
const int x = int(file) * 8;
const int y = (7 - int(rank)) * 3;
for (int i = 1; i < 8; ++i)
board[y][x + i] = board[y + 3][x + i] = '-';
for (int i = 1; i < 3; ++i)
board[y + i][x] = board[y + i][x + 8] = '|';
board[y][x] = board[y][x + 8] = board[y + 3][x + 8] = board[y + 3][x] = '+';
if (pc != NO_PIECE)
board[y + 1][x + 4] = PieceToChar[pc];
if (value != VALUE_NONE)
format_cp_compact(value, &board[y + 2][x + 2], pos);
};
// We estimate the value of each piece by doing a differential evaluation from
// the current base eval, simulating the removal of the piece from its square.
Value base = networks.big.evaluate(pos);
base = pos.side_to_move() == WHITE ? base : -base;
for (File f = FILE_A; f <= FILE_H; ++f)
for (Rank r = RANK_1; r <= RANK_8; ++r)
{
Square sq = make_square(f, r);
Piece pc = pos.piece_on(sq);
Value v = VALUE_NONE;
if (pc != NO_PIECE && type_of(pc) != KING)
{
auto st = pos.state();
pos.remove_piece(sq);
st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] =
st->accumulatorBig.computedPSQT[WHITE] = st->accumulatorBig.computedPSQT[BLACK] =
false;
Value eval = networks.big.evaluate(pos);
eval = pos.side_to_move() == WHITE ? eval : -eval;
v = base - eval;
pos.put_piece(pc, sq);
st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] =
st->accumulatorBig.computedPSQT[WHITE] = st->accumulatorBig.computedPSQT[BLACK] =
false;
}
writeSquare(f, r, pc, v);
}
ss << " NNUE derived piece values:\n";
for (int row = 0; row < 3 * 8 + 1; ++row)
ss << board[row] << '\n';
ss << '\n';
auto t = networks.big.trace_evaluate(pos);
ss << " NNUE network contributions "
<< (pos.side_to_move() == WHITE ? "(White to move)" : "(Black to move)") << std::endl
<< "+------------+------------+------------+------------+\n"
<< "| Bucket | Material | Positional | Total |\n"
<< "| | (PSQT) | (Layers) | |\n"
<< "+------------+------------+------------+------------+\n";
for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket)
{
ss << "| " << bucket << " ";
ss << " | ";
format_cp_aligned_dot(t.psqt[bucket], ss, pos);
ss << " "
<< " | ";
format_cp_aligned_dot(t.positional[bucket], ss, pos);
ss << " "
<< " | ";
format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss, pos);
ss << " "
<< " |";
if (bucket == t.correctBucket)
ss << " <-- this bucket is used";
ss << '\n';
}
ss << "+------------+------------+------------+------------+\n";
return ss.str();
}
} // namespace Stockfish::Eval::NNUE
+63
View File
@@ -0,0 +1,63 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NNUE_MISC_H_INCLUDED
#define NNUE_MISC_H_INCLUDED
#include <cstddef>
#include <string>
#include "../types.h"
#include "nnue_architecture.h"
namespace Stockfish {
class Position;
namespace Eval::NNUE {
struct EvalFile {
// Default net name, will use one of the EvalFileDefaultName* macros defined
// in evaluate.h
std::string defaultName;
// Selected net name, either via uci option or default
std::string current;
// Net description extracted from the net file
std::string netDescription;
};
struct NnueEvalTrace {
static_assert(LayerStacks == PSQTBuckets);
Value psqt[LayerStacks];
Value positional[LayerStacks];
std::size_t correctBucket;
};
struct Networks;
std::string trace(Position& pos, const Networks& networks);
void hint_common_parent_position(const Position& pos, const Networks& networks);
} // namespace Stockfish::Eval::NNUE
} // namespace Stockfish
#endif // #ifndef NNUE_MISC_H_INCLUDED
-305
View File
@@ -1,305 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include "bitboard.h"
#include "pawns.h"
#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( 6, 19);
constexpr Score Doubled = S(11, 51);
constexpr Score DoubledEarly = S(17, 7);
constexpr Score Isolated = S( 1, 20);
constexpr Score WeakLever = S( 2, 57);
constexpr Score WeakUnopposed = S(15, 18);
// Bonus for blocked pawns at 5th or 6th rank
constexpr Score BlockedPawn[2] = { S(-19, -8), S(-7, 3) };
constexpr Score BlockedStorm[RANK_NB] = {
S(0, 0), S(0, 0), S(64, 75), S(-3, 14), S(-12, 19), S(-7, 4), S(-10, 5)
};
// Connected pawn bonus
constexpr int Connected[RANK_NB] = { 0, 3, 7, 7, 15, 54, 86 };
// Strength of pawn shelter for our king by [distance from edge][rank].
// RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king.
constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = {
{ V(-2), V(85), V(95), V(53), V(39), V(23), V(25) },
{ V(-55), V(64), V(32), V(-55), V(-30), V(-11), V(-61) },
{ V(-11), V(75), V(19), V(-6), V(26), V(9), V(-47) },
{ V(-41), V(-11), V(-27), V(-58), V(-42), V(-66), V(-163) }
};
// Danger of enemy pawns moving toward our king by [distance from edge][rank].
// RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn
// is behind our king. Note that UnblockedStorm[0][1-2] accommodate opponent pawn
// on edge, likely blocked by our king.
constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = {
{ V(94), V(-280), V(-170), V(90), V(59), V(47), V(53) },
{ V(43), V(-17), V(128), V(39), V(26), V(-17), V(15) },
{ V(-9), V(62), V(170), V(34), V(-5), V(-20), V(-11) },
{ V(-27), V(-19), V(106), V(10), V(2), V(-13), V(-24) }
};
// 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(-18,11), S(-6,-3) },
{ S( 0, 0), S( 5,-4) }};
#undef S
#undef V
/// evaluate() calculates a score for the static pawn structure of the given position.
/// We cannot use the location of pieces or king in this function, as the evaluation
/// of the pawn structure will be stored in a small cache for speed reasons, and will
/// be re-used even when the pieces have moved.
template<Color Us>
Score evaluate(const Position& pos, Pawns::Entry* e) {
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;
Bitboard b = pos.pieces(Us, PAWN);
Bitboard ourPawns = pos.pieces( Us, PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN);
Bitboard doubleAttackThem = pawn_double_attacks_bb<Them>(theirPawns);
e->passedPawns[Us] = 0;
e->kingSquares[Us] = SQ_NONE;
e->pawnAttacks[Us] = e->pawnAttacksSpan[Us] = pawn_attacks_bb<Us>(ourPawns);
e->blockedCount += popcount(shift<Up>(ourPawns) & (theirPawns | doubleAttackThem));
// Loop through all pawns of the current color and score each pawn
while (b)
{
s = pop_lsb(b);
assert(pos.piece_on(s) == make_piece(Us, PAWN));
Rank r = relative_rank(Us, s);
// Flag the pawn
opposed = theirPawns & forward_file_bb(Us, s);
blocked = theirPawns & (s + Up);
stoppers = theirPawns & passed_pawn_span(Us, s);
lever = theirPawns & pawn_attacks_bb(Us, s);
leverPush = theirPawns & pawn_attacks_bb(Us, s + Up);
doubled = ourPawns & (s - Up);
neighbours = ourPawns & adjacent_files_bb(s);
phalanx = neighbours & rank_bb(s);
support = neighbours & rank_bb(s - Up);
if (doubled)
{
// Additional doubled penalty if none of their pawns is fixed
if (!(ourPawns & shift<Down>(theirPawns | pawn_attacks_bb<Them>(theirPawns))))
score -= DoubledEarly;
}
// A pawn is backward when it is behind all pawns of the same color on
// the adjacent files and cannot safely advance.
backward = !(neighbours & forward_ranks_bb(Them, s + Up))
&& (leverPush | blocked);
// Compute additional span if pawn is not backward nor blocked
if (!backward && !blocked)
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
// A pawn is passed if one of the three following conditions is true:
// (a) there is no stoppers except some levers
// (b) the only stoppers are the leverPush, but we outnumber them
// (c) there is only one front stopper which can be levered.
// (Refined in Evaluation::passed)
passed = !(stoppers ^ lever)
|| ( !(stoppers ^ leverPush)
&& popcount(phalanx) >= popcount(leverPush))
|| ( stoppers == blocked && r >= RANK_5
&& (shift<Up>(support) & ~(theirPawns | doubleAttackThem)));
passed &= !(forward_file_bb(Us, s) & ourPawns);
// Passed pawns will be properly scored later in evaluation when we have
// full attack info.
if (passed)
e->passedPawns[Us] |= s;
// Score this pawn
if (support | phalanx)
{
int v = Connected[r] * (2 + bool(phalanx) - bool(opposed))
+ 22 * popcount(support);
score += make_score(v, v * (r - 2) / 4);
}
else if (!neighbours)
{
if ( opposed
&& (ourPawns & forward_file_bb(Them, s))
&& !(theirPawns & adjacent_files_bb(s)))
score -= Doubled;
else
score -= Isolated
+ WeakUnopposed * !opposed;
}
else if (backward)
score -= Backward
+ WeakUnopposed * !opposed * bool(~(FileABB | FileHBB) & s);
if (!support)
score -= Doubled * doubled
+ WeakLever * more_than_one(lever);
if (blocked && r >= RANK_5)
score += BlockedPawn[r - RANK_5];
}
return score;
}
} // namespace
namespace Pawns {
/// Pawns::probe() looks up the current position's pawns configuration in
/// the pawns hash table. It returns a pointer to the Entry if the position
/// is found. Otherwise a new Entry is computed and stored there, so we don't
/// have to recompute all when the same pawns configuration occurs again.
Entry* probe(const Position& pos) {
Key key = pos.pawn_key();
Entry* e = pos.this_thread()->pawnsTable[key];
if (e->key == key)
return e;
e->key = key;
e->blockedCount = 0;
e->scores[WHITE] = evaluate<WHITE>(pos, e);
e->scores[BLACK] = evaluate<BLACK>(pos, e);
return e;
}
/// Entry::evaluate_shelter() calculates the shelter bonus and the storm
/// penalty for a king, looking at the king file and the two closest files.
template<Color Us>
Score Entry::evaluate_shelter(const Position& pos, Square ksq) const {
constexpr Color Them = ~Us;
Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq);
Bitboard ourPawns = b & pos.pieces(Us) & ~pawnAttacks[Them];
Bitboard theirPawns = b & pos.pieces(Them);
Score bonus = make_score(5, 5);
File center = std::clamp(file_of(ksq), FILE_B, FILE_G);
for (File f = File(center - 1); f <= File(center + 1); ++f)
{
b = ourPawns & file_bb(f);
int ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0;
b = theirPawns & file_bb(f);
int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0;
int d = edge_distance(f);
bonus += make_score(ShelterStrength[d][ourRank], 0);
if (ourRank && (ourRank == theirRank - 1))
bonus -= BlockedStorm[theirRank];
else
bonus -= make_score(UnblockedStorm[d][theirRank], 0);
}
// King On File
bonus -= KingOnFile[pos.is_on_semiopen_file(Us, ksq)][pos.is_on_semiopen_file(Them, ksq)];
return bonus;
}
/// Entry::do_king_safety() calculates a bonus for king safety. It is called only
/// when king square changes, which is about 20% of total king_safety() calls.
template<Color Us>
Score Entry::do_king_safety(const Position& pos) {
Square ksq = pos.square<KING>(Us);
kingSquares[Us] = ksq;
castlingRights[Us] = pos.castling_rights(Us);
auto compare = [](Score a, Score b) { return mg_value(a) < mg_value(b); };
Score shelter = evaluate_shelter<Us>(pos, ksq);
// If we can castle use the bonus after castling if it is bigger
if (pos.can_castle(Us & KING_SIDE))
shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1)), compare);
if (pos.can_castle(Us & QUEEN_SIDE))
shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1)), compare);
// In endgame we like to bring our king near our closest pawn
Bitboard pawns = pos.pieces(Us, PAWN);
int minPawnDist = 6;
if (pawns & attacks_bb<KING>(ksq))
minPawnDist = 1;
else while (pawns)
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(pawns)));
return shelter - make_score(0, 16 * minPawnDist);
}
// Explicit template instantiation
template Score Entry::do_king_safety<WHITE>(const Position& pos);
template Score Entry::do_king_safety<BLACK>(const Position& pos);
} // namespace Pawns
} // namespace Stockfish
-70
View File
@@ -1,70 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PAWNS_H_INCLUDED
#define PAWNS_H_INCLUDED
#include "misc.h"
#include "position.h"
#include "types.h"
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
/// pointer to an Entry object.
struct Entry {
Score pawn_score(Color c) const { return scores[c]; }
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); }
int blocked_count() const { return blockedCount; }
template<Color Us>
Score king_safety(const Position& pos) {
return kingSquares[Us] == pos.square<KING>(Us) && castlingRights[Us] == pos.castling_rights(Us)
? kingSafety[Us] : (kingSafety[Us] = do_king_safety<Us>(pos));
}
template<Color Us>
Score do_king_safety(const Position& pos);
template<Color Us>
Score evaluate_shelter(const Position& pos, Square ksq) const;
Key key;
Score scores[COLOR_NB];
Bitboard passedPawns[COLOR_NB];
Bitboard pawnAttacks[COLOR_NB];
Bitboard pawnAttacksSpan[COLOR_NB];
Square kingSquares[COLOR_NB];
Score kingSafety[COLOR_NB];
int castlingRights[COLOR_NB];
int blockedCount;
};
using Table = HashTable<Entry, 131072>;
Entry* probe(const Position& pos);
} // namespace Stockfish::Pawns
#endif // #ifndef PAWNS_H_INCLUDED
+71
View File
@@ -0,0 +1,71 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PERFT_H_INCLUDED
#define PERFT_H_INCLUDED
#include <cstdint>
#include "cluster.h"
#include "movegen.h"
#include "position.h"
#include "types.h"
#include "uci.h"
namespace Stockfish {
// Utility to verify move generation. All the leaf nodes up
// to the given depth are generated and counted, and the sum is returned.
template<bool Root>
uint64_t perft(Position& pos, Depth depth) {
StateInfo st;
ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize);
uint64_t cnt, nodes = 0;
const bool leaf = (depth == 2);
for (const auto& m : MoveList<LEGAL>(pos))
{
if (Root && depth <= 1)
cnt = 1, nodes++;
else
{
pos.do_move(m, st);
cnt = leaf ? MoveList<LEGAL>(pos).size() : perft<false>(pos, depth - 1);
nodes += cnt;
pos.undo_move(m);
}
if (Root && Cluster::is_root())
sync_cout << UCI::move(m, pos.is_chess960()) << ": " << cnt << sync_endl;
}
return nodes;
}
inline void perft(const std::string& fen, Depth depth, bool isChess960) {
StateListPtr states(new std::deque<StateInfo>(1));
Position p;
p.set(fen, isChess960, &states->back());
uint64_t nodes = perft<true>(p, depth);
if (Cluster::is_root())
sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl;
}
}
#endif // PERFT_H_INCLUDED
+207 -255
View File
File diff suppressed because it is too large Load Diff
+74 -160
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,27 +21,28 @@
#include <cassert> #include <cassert>
#include <deque> #include <deque>
#include <memory> // For std::unique_ptr #include <iosfwd>
#include <memory>
#include <string> #include <string>
#include "bitboard.h" #include "bitboard.h"
#include "evaluate.h"
#include "psqt.h"
#include "types.h"
#include "nnue/nnue_accumulator.h" #include "nnue/nnue_accumulator.h"
#include "nnue/nnue_architecture.h"
#include "types.h"
namespace Stockfish { namespace Stockfish {
/// StateInfo struct stores information needed to restore a Position object to class TranspositionTable;
/// its previous state when we retract a move. Whenever a move is made on the
/// board (by calling Position::do_move), a StateInfo object must be passed. // StateInfo struct stores information needed to restore a Position object to
// its previous state when we retract a move. Whenever a move is made on the
// board (by calling Position::do_move), a StateInfo object must be passed.
struct StateInfo { struct StateInfo {
// Copied when making a move // Copied when making a move
Key pawnKey;
Key materialKey; Key materialKey;
Key pawnKey;
Value nonPawnMaterial[COLOR_NB]; Value nonPawnMaterial[COLOR_NB];
int castlingRights; int castlingRights;
int rule50; int rule50;
@@ -59,24 +60,23 @@ struct StateInfo {
int repetition; int repetition;
// Used by NNUE // Used by NNUE
Eval::NNUE::Accumulator accumulator; Eval::NNUE::Accumulator<Eval::NNUE::TransformedFeatureDimensionsBig> accumulatorBig;
Eval::NNUE::Accumulator<Eval::NNUE::TransformedFeatureDimensionsSmall> accumulatorSmall;
DirtyPiece dirtyPiece; DirtyPiece dirtyPiece;
}; };
/// A list to keep track of the position states along the setup moves (from the // A list to keep track of the position states along the setup moves (from the
/// start position to the position just before the search starts). Needed by // start position to the position just before the search starts). Needed by
/// 'draw by repetition' detection. Use a std::deque because pointers to // 'draw by repetition' detection. Use a std::deque because pointers to
/// elements are not invalidated upon list resizing. // elements are not invalidated upon list resizing.
using StateListPtr = std::unique_ptr<std::deque<StateInfo>>; using StateListPtr = std::unique_ptr<std::deque<StateInfo>>;
/// Position class stores information regarding the board representation as // Position class stores information regarding the board representation as
/// pieces, side to move, hash keys, castling info, etc. Important methods are // pieces, side to move, hash keys, castling info, etc. Important methods are
/// do_move() and undo_move(), used by the search to update node info when // do_move() and undo_move(), used by the search to update node info when
/// traversing the search tree. // traversing the search tree.
class Thread;
class Position { class Position {
public: public:
static void init(); static void init();
@@ -86,22 +86,26 @@ public:
Position& operator=(const Position&) = delete; Position& operator=(const Position&) = delete;
// FEN string input/output // FEN string input/output
Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th); Position& set(const std::string& fenStr, bool isChess960, StateInfo* si);
Position& set(const std::string& code, Color c, StateInfo* si); Position& set(const std::string& code, Color c, StateInfo* si);
std::string fen() const; std::string fen() const;
// Position representation // Position representation
Bitboard pieces(PieceType pt) const; Bitboard pieces(PieceType pt = ALL_PIECES) const;
template<typename ...PieceTypes> Bitboard pieces(PieceType pt, PieceTypes... pts) const; template<typename... PieceTypes>
Bitboard pieces(PieceType pt, PieceTypes... pts) const;
Bitboard pieces(Color c) const; Bitboard pieces(Color c) const;
template<typename ...PieceTypes> Bitboard pieces(Color c, PieceTypes... pts) const; template<typename... PieceTypes>
Bitboard pieces(Color c, PieceTypes... pts) const;
Piece piece_on(Square s) const; Piece piece_on(Square s) const;
Square ep_square() const; Square ep_square() const;
bool empty(Square s) const; bool empty(Square s) const;
template<PieceType Pt> int count(Color c) const; template<PieceType Pt>
template<PieceType Pt> int count() const; int count(Color c) const;
template<PieceType Pt> Square square(Color c) const; template<PieceType Pt>
bool is_on_semiopen_file(Color c, Square s) const; int count() const;
template<PieceType Pt>
Square square(Color c) const;
// Castling // Castling
CastlingRights castling_rights(Color c) const; CastlingRights castling_rights(Color c) const;
@@ -118,8 +122,9 @@ public:
// Attacks to/from a given square // Attacks to/from a given square
Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s) const;
Bitboard attackers_to(Square s, Bitboard occupied) const; Bitboard attackers_to(Square s, Bitboard occupied) const;
Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const; void update_slider_blockers(Color c) const;
template<PieceType Pt> Bitboard attacks_by(Color c) const; template<PieceType Pt>
Bitboard attacks_by(Color c) const;
// Properties of moves // Properties of moves
bool legal(Move m) const; bool legal(Move m) const;
@@ -130,21 +135,15 @@ public:
Piece moved_piece(Move m) const; Piece moved_piece(Move m) const;
Piece captured_piece() const; Piece captured_piece() const;
// Piece specific
bool pawn_passed(Color c, Square s) const;
bool opposite_bishops() const;
int pawns_on_same_color_squares(Color c, Square s) const;
// Doing and undoing moves // Doing and undoing moves
void do_move(Move m, StateInfo& newSt); void do_move(Move m, StateInfo& newSt);
void do_move(Move m, StateInfo& newSt, bool givesCheck); void do_move(Move m, StateInfo& newSt, bool givesCheck);
void undo_move(Move m); void undo_move(Move m);
void do_null_move(StateInfo& newSt); void do_null_move(StateInfo& newSt, TranspositionTable& tt);
void undo_null_move(); void undo_null_move();
// Static Exchange Evaluation // Static Exchange Evaluation
bool see_ge(Move m, Value threshold = VALUE_ZERO) const; bool see_ge(Move m, int threshold = 0) const;
bool see_ge(Move m, Bitboard& occupied, Value threshold = VALUE_ZERO) const;
// Accessing hash keys // Accessing hash keys
Key key() const; Key key() const;
@@ -156,13 +155,10 @@ public:
Color side_to_move() const; Color side_to_move() const;
int game_ply() const; int game_ply() const;
bool is_chess960() const; bool is_chess960() const;
Thread* this_thread() const;
bool is_draw(int ply) const; bool is_draw(int ply) const;
bool has_game_cycle(int ply) const; bool has_game_cycle(int ply) const;
bool has_repeated() const; bool has_repeated() const;
int rule50_count() const; int rule50_count() const;
Score psq_score() const;
Value psq_eg_stm() const;
Value non_pawn_material(Color c) const; Value non_pawn_material(Color c) const;
Value non_pawn_material() const; Value non_pawn_material() const;
@@ -197,75 +193,58 @@ private:
int castlingRightsMask[SQUARE_NB]; int castlingRightsMask[SQUARE_NB];
Square castlingRookSquare[CASTLING_RIGHT_NB]; Square castlingRookSquare[CASTLING_RIGHT_NB];
Bitboard castlingPath[CASTLING_RIGHT_NB]; Bitboard castlingPath[CASTLING_RIGHT_NB];
Thread* thisThread;
StateInfo* st; StateInfo* st;
int gamePly; int gamePly;
Color sideToMove; Color sideToMove;
Score psq;
bool chess960; bool chess960;
}; };
std::ostream& operator<<(std::ostream& os, const Position& pos); std::ostream& operator<<(std::ostream& os, const Position& pos);
inline Color Position::side_to_move() const { inline Color Position::side_to_move() const { return sideToMove; }
return sideToMove;
}
inline Piece Position::piece_on(Square s) const { inline Piece Position::piece_on(Square s) const {
assert(is_ok(s)); assert(is_ok(s));
return board[s]; return board[s];
} }
inline bool Position::empty(Square s) const { inline bool Position::empty(Square s) const { return piece_on(s) == NO_PIECE; }
return piece_on(s) == NO_PIECE;
}
inline Piece Position::moved_piece(Move m) const { inline Piece Position::moved_piece(Move m) const { return piece_on(m.from_sq()); }
return piece_on(from_sq(m));
}
inline Bitboard Position::pieces(PieceType pt = ALL_PIECES) const { inline Bitboard Position::pieces(PieceType pt) const { return byTypeBB[pt]; }
return byTypeBB[pt];
}
template<typename... PieceTypes> template<typename... PieceTypes>
inline Bitboard Position::pieces(PieceType pt, PieceTypes... pts) const { inline Bitboard Position::pieces(PieceType pt, PieceTypes... pts) const {
return pieces(pt) | pieces(pts...); return pieces(pt) | pieces(pts...);
} }
inline Bitboard Position::pieces(Color c) const { inline Bitboard Position::pieces(Color c) const { return byColorBB[c]; }
return byColorBB[c];
}
template<typename... PieceTypes> template<typename... PieceTypes>
inline Bitboard Position::pieces(Color c, PieceTypes... pts) const { inline Bitboard Position::pieces(Color c, PieceTypes... pts) const {
return pieces(c) & pieces(pts...); return pieces(c) & pieces(pts...);
} }
template<PieceType Pt> inline int Position::count(Color c) const { template<PieceType Pt>
inline int Position::count(Color c) const {
return pieceCount[make_piece(c, Pt)]; return pieceCount[make_piece(c, Pt)];
} }
template<PieceType Pt> inline int Position::count() const { template<PieceType Pt>
inline int Position::count() const {
return count<Pt>(WHITE) + count<Pt>(BLACK); return count<Pt>(WHITE) + count<Pt>(BLACK);
} }
template<PieceType Pt> inline Square Position::square(Color c) const { template<PieceType Pt>
inline Square Position::square(Color c) const {
assert(count<Pt>(c) == 1); assert(count<Pt>(c) == 1);
return lsb(pieces(c, Pt)); return lsb(pieces(c, Pt));
} }
inline Square Position::ep_square() const { inline Square Position::ep_square() const { return st->epSquare; }
return st->epSquare;
}
inline bool Position::is_on_semiopen_file(Color c, Square s) const { inline bool Position::can_castle(CastlingRights cr) const { return st->castlingRights & cr; }
return !(pieces(c, PAWN) & file_bb(s));
}
inline bool Position::can_castle(CastlingRights cr) const {
return st->castlingRights & cr;
}
inline CastlingRights Position::castling_rights(Color c) const { inline CastlingRights Position::castling_rights(Color c) const {
return c & CastlingRights(st->castlingRights); return c & CastlingRights(st->castlingRights);
@@ -273,19 +252,15 @@ inline CastlingRights Position::castling_rights(Color c) const {
inline bool Position::castling_impeded(CastlingRights cr) const { inline bool Position::castling_impeded(CastlingRights cr) const {
assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO); assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO);
return pieces() & castlingPath[cr]; return pieces() & castlingPath[cr];
} }
inline Square Position::castling_rook_square(CastlingRights cr) const { inline Square Position::castling_rook_square(CastlingRights cr) const {
assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO); assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO);
return castlingRookSquare[cr]; return castlingRookSquare[cr];
} }
inline Bitboard Position::attackers_to(Square s) const { inline Bitboard Position::attackers_to(Square s) const { return attackers_to(s, pieces()); }
return attackers_to(s, pieces());
}
template<PieceType Pt> template<PieceType Pt>
inline Bitboard Position::attacks_by(Color c) const { inline Bitboard Position::attacks_by(Color c) const {
@@ -303,104 +278,51 @@ inline Bitboard Position::attacks_by(Color c) const {
} }
} }
inline Bitboard Position::checkers() const { inline Bitboard Position::checkers() const { return st->checkersBB; }
return st->checkersBB;
}
inline Bitboard Position::blockers_for_king(Color c) const { inline Bitboard Position::blockers_for_king(Color c) const { return st->blockersForKing[c]; }
return st->blockersForKing[c];
}
inline Bitboard Position::pinners(Color c) const { inline Bitboard Position::pinners(Color c) const { return st->pinners[c]; }
return st->pinners[c];
}
inline Bitboard Position::check_squares(PieceType pt) const { inline Bitboard Position::check_squares(PieceType pt) const { return st->checkSquares[pt]; }
return st->checkSquares[pt];
}
inline bool Position::pawn_passed(Color c, Square s) const { inline Key Position::key() const { return adjust_key50<false>(st->key); }
return !(pieces(~c, PAWN) & passed_pawn_span(c, s));
}
inline int Position::pawns_on_same_color_squares(Color c, Square s) const {
return popcount(pieces(c, PAWN) & ((DarkSquares & s) ? DarkSquares : ~DarkSquares));
}
inline Key Position::key() const {
return adjust_key50<false>(st->key);
}
template<bool AfterMove> template<bool AfterMove>
inline Key Position::adjust_key50(Key k) const inline Key Position::adjust_key50(Key k) const {
{ return st->rule50 < 14 - AfterMove ? k : k ^ make_key((st->rule50 - (14 - AfterMove)) / 8);
return st->rule50 < 14 - AfterMove
? k : k ^ make_key((st->rule50 - (14 - AfterMove)) / 8);
} }
inline Key Position::pawn_key() const { inline Key Position::pawn_key() const { return st->pawnKey; }
return st->pawnKey;
}
inline Key Position::material_key() const { inline Key Position::material_key() const { return st->materialKey; }
return st->materialKey;
}
inline Score Position::psq_score() const { inline Value Position::non_pawn_material(Color c) const { return st->nonPawnMaterial[c]; }
return psq;
}
inline Value Position::psq_eg_stm() const {
return (sideToMove == WHITE ? 1 : -1) * eg_value(psq);
}
inline Value Position::non_pawn_material(Color c) const {
return st->nonPawnMaterial[c];
}
inline Value Position::non_pawn_material() const { inline Value Position::non_pawn_material() const {
return non_pawn_material(WHITE) + non_pawn_material(BLACK); return non_pawn_material(WHITE) + non_pawn_material(BLACK);
} }
inline int Position::game_ply() const { inline int Position::game_ply() const { return gamePly; }
return gamePly;
}
inline int Position::rule50_count() const { inline int Position::rule50_count() const { return st->rule50; }
return st->rule50;
}
inline bool Position::opposite_bishops() const { inline bool Position::is_chess960() const { return chess960; }
return count<BISHOP>(WHITE) == 1
&& count<BISHOP>(BLACK) == 1
&& opposite_colors(square<BISHOP>(WHITE), square<BISHOP>(BLACK));
}
inline bool Position::is_chess960() const {
return chess960;
}
inline bool Position::capture(Move m) const { inline bool Position::capture(Move m) const {
assert(is_ok(m)); assert(m.is_ok());
return (!empty(to_sq(m)) && type_of(m) != CASTLING) return (!empty(m.to_sq()) && m.type_of() != CASTLING) || m.type_of() == EN_PASSANT;
|| type_of(m) == EN_PASSANT;
} }
// returns true if a move is generated from the capture stage // Returns true if a move is generated from the capture stage, having also
// having also queen promotions covered, i.e. consistency with the capture stage move generation // queen promotions covered, i.e. consistency with the capture stage move generation
// is needed to avoid the generation of duplicate moves. // is needed to avoid the generation of duplicate moves.
inline bool Position::capture_stage(Move m) const { inline bool Position::capture_stage(Move m) const {
assert(is_ok(m)); assert(m.is_ok());
return capture(m) || promotion_type(m) == QUEEN; return capture(m) || m.promotion_type() == QUEEN;
} }
inline Piece Position::captured_piece() const { inline Piece Position::captured_piece() const { return st->capturedPiece; }
return st->capturedPiece;
}
inline Thread* Position::this_thread() const {
return thisThread;
}
inline void Position::put_piece(Piece pc, Square s) { inline void Position::put_piece(Piece pc, Square s) {
@@ -409,7 +331,6 @@ inline void Position::put_piece(Piece pc, Square s) {
byColorBB[color_of(pc)] |= s; byColorBB[color_of(pc)] |= s;
pieceCount[pc]++; pieceCount[pc]++;
pieceCount[make_piece(color_of(pc), ALL_PIECES)]++; pieceCount[make_piece(color_of(pc), ALL_PIECES)]++;
psq += PSQT::psq[pc][s];
} }
inline void Position::remove_piece(Square s) { inline void Position::remove_piece(Square s) {
@@ -421,7 +342,6 @@ inline void Position::remove_piece(Square s) {
board[s] = NO_PIECE; board[s] = NO_PIECE;
pieceCount[pc]--; pieceCount[pc]--;
pieceCount[make_piece(color_of(pc), ALL_PIECES)]--; pieceCount[make_piece(color_of(pc), ALL_PIECES)]--;
psq -= PSQT::psq[pc][s];
} }
inline void Position::move_piece(Square from, Square to) { inline void Position::move_piece(Square from, Square to) {
@@ -433,17 +353,11 @@ inline void Position::move_piece(Square from, Square to) {
byColorBB[color_of(pc)] ^= fromTo; byColorBB[color_of(pc)] ^= fromTo;
board[from] = NO_PIECE; board[from] = NO_PIECE;
board[to] = pc; board[to] = pc;
psq += PSQT::psq[pc][to] - PSQT::psq[pc][from];
} }
inline void Position::do_move(Move m, StateInfo& newSt) { inline void Position::do_move(Move m, StateInfo& newSt) { do_move(m, newSt, gives_check(m)); }
do_move(m, newSt, gives_check(m));
}
inline StateInfo* Position::state() const { inline StateInfo* Position::state() const { return st; }
return st;
}
} // namespace Stockfish } // namespace Stockfish
-131
View File
@@ -1,131 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "psqt.h"
#include <algorithm>
#include "bitboard.h"
#include "types.h"
namespace Stockfish {
namespace
{
auto constexpr S = make_score;
// 'Bonus' contains Piece-Square parameters.
// Scores are explicit for files A to D, implicitly mirrored for E to H.
constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
{ },
{ },
{ // Knight
{ S(-175, -96), S(-92,-65), S(-74,-49), S(-73,-21) },
{ S( -77, -67), S(-41,-54), S(-27,-18), S(-15, 8) },
{ S( -61, -40), S(-17,-27), S( 6, -8), S( 12, 29) },
{ S( -35, -35), S( 8, -2), S( 40, 13), S( 49, 28) },
{ S( -34, -45), S( 13,-16), S( 44, 9), S( 51, 39) },
{ S( -9, -51), S( 22,-44), S( 58,-16), S( 53, 17) },
{ S( -67, -69), S(-27,-50), S( 4,-51), S( 37, 12) },
{ S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) }
},
{ // Bishop
{ S(-37,-40), S(-4 ,-21), S( -6,-26), S(-16, -8) },
{ S(-11,-26), S( 6, -9), S( 13,-12), S( 3, 1) },
{ S(-5 ,-11), S( 15, -1), S( -4, -1), S( 12, 7) },
{ S(-4 ,-14), S( 8, -4), S( 18, 0), S( 27, 12) },
{ S(-8 ,-12), S( 20, -1), S( 15,-10), S( 22, 11) },
{ S(-11,-21), S( 4, 4), S( 1, 3), S( 8, 4) },
{ S(-12,-22), S(-10,-14), S( 4, -1), S( 0, 1) },
{ S(-34,-32), S( 1,-29), S(-10,-26), S(-16,-17) }
},
{ // Rook
{ S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) },
{ S(-21,-12), S(-13, -9), S( -8, -1), S( 6, -2) },
{ S(-25, 6), S(-11, -8), S( -1, -2), S( 3, -6) },
{ S(-13, -6), S( -5, 1), S( -4, -9), S(-6, 7) },
{ S(-27, -5), S(-15, 8), S( -4, 7), S( 3, -6) },
{ S(-22, 6), S( -2, 1), S( 6, -7), S(12, 10) },
{ S( -2, 4), S( 12, 5), S( 16, 20), S(18, -5) },
{ S(-17, 18), S(-19, 0), S( -1, 19), S( 9, 13) }
},
{ // Queen
{ S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) },
{ S(-3,-54), S( 5,-31), S( 8,-22), S(12, -4) },
{ S(-3,-39), S( 6,-18), S(13, -9), S( 7, 3) },
{ S( 4,-23), S( 5, -3), S( 9, 13), S( 8, 24) },
{ S( 0,-29), S(14, -6), S(12, 9), S( 5, 21) },
{ S(-4,-38), S(10,-18), S( 6,-11), S( 8, 1) },
{ S(-5,-50), S( 6,-27), S(10,-24), S( 8, -8) },
{ S(-2,-74), S(-2,-52), S( 1,-43), S(-2,-34) }
},
{ // King
{ S(271, 1), S(327, 45), S(271, 85), S(198, 76) },
{ S(278, 53), S(303,100), S(234,133), S(179,135) },
{ S(195, 88), S(258,130), S(169,169), S(120,175) },
{ S(164,103), S(190,156), S(138,172), S( 98,172) },
{ S(154, 96), S(179,166), S(105,199), S( 70,199) },
{ S(123, 92), S(145,172), S( 81,184), S( 31,191) },
{ S( 88, 47), S(120,121), S( 65,116), S( 33,131) },
{ S( 59, 11), S( 89, 59), S( 45, 73), S( -1, 78) }
}
};
constexpr Score PBonus[RANK_NB][FILE_NB] =
{ // Pawn (asymmetric distribution)
{ },
{ S( 2, -8), S( 4, -6), S( 11, 9), S( 18, 5), S( 16, 16), S( 21, 6), S( 9, -6), S( -3,-18) },
{ S( -9, -9), S(-15, -7), S( 11,-10), S( 15, 5), S( 31, 2), S( 23, 3), S( 6, -8), S(-20, -5) },
{ S( -3, 7), S(-20, 1), S( 8, -8), S( 19, -2), S( 39,-14), S( 17,-13), S( 2,-11), S( -5, -6) },
{ S( 11, 12), S( -4, 6), S(-11, 2), S( 2, -6), S( 11, -5), S( 0, -4), S(-12, 14), S( 5, 9) },
{ S( 3, 27), S(-11, 18), S( -6, 19), S( 22, 29), S( -8, 30), S( -5, 9), S(-14, 8), S(-11, 14) },
{ S( -7, -1), S( 6,-14), S( -2, 13), S(-11, 22), S( 4, 24), S(-14, 17), S( 10, 7), S( -9, 7) }
};
} // namespace
namespace PSQT
{
Score psq[PIECE_NB][SQUARE_NB];
// PSQT::init() initializes piece-square tables: the white halves of the tables are
// copied from Bonus[] and PBonus[], adding the piece value, then the black halves of
// the tables are initialized by flipping and changing the sign of the white scores.
void init() {
for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING})
{
Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
File f = File(edge_distance(file_of(s)));
psq[ pc][s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)]
: Bonus[pc][rank_of(s)][f]);
psq[~pc][flip_rank(s)] = -psq[pc][s];
}
}
}
} // namespace PSQT
} // namespace Stockfish
-38
View File
@@ -1,38 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PSQT_H_INCLUDED
#define PSQT_H_INCLUDED
#include "types.h"
namespace Stockfish::PSQT
{
extern Score psq[PIECE_NB][SQUARE_NB];
// Fill psqt array from a set of internally linked parameters
void init();
} // namespace Stockfish::PSQT
#endif // PSQT_H_INCLUDED
+847 -836
View File
File diff suppressed because it is too large Load Diff
+207 -27
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,23 +19,46 @@
#ifndef SEARCH_H_INCLUDED #ifndef SEARCH_H_INCLUDED
#define SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED
#include <array>
#include <atomic>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <vector> #include <vector>
#include <mutex>
#include "cluster.h"
#include "misc.h" #include "misc.h"
#include "movepick.h" #include "movepick.h"
#include "position.h"
#include "syzygy/tbprobe.h"
#include "timeman.h"
#include "types.h" #include "types.h"
namespace Stockfish { namespace Stockfish {
class Position; namespace Eval::NNUE {
struct Networks;
}
// Different node types, used as a template parameter
enum NodeType {
NonPV,
PV,
Root
};
class TranspositionTable;
class ThreadPool;
class OptionsMap;
namespace Search { namespace Search {
// Stack struct keeps track of the information we need to remember from nodes
/// Stack struct keeps track of the information we need to remember from nodes // shallower and deeper in the tree during the search. Each search thread has
/// shallower and deeper in the tree during the search. Each search thread has // its own array of Stack objects, indexed by the current ply.
/// its own array of Stack objects, indexed by the current ply.
struct Stack { struct Stack {
Move* pv; Move* pv;
PieceToHistory* continuationHistory; PieceToHistory* continuationHistory;
@@ -49,25 +72,26 @@ struct Stack {
bool inCheck; bool inCheck;
bool ttPv; bool ttPv;
bool ttHit; bool ttHit;
int doubleExtensions; int multipleExtensions;
int cutoffCnt; int cutoffCnt;
}; };
/// RootMove struct is used for moves at the root of the tree. For each root move // RootMove struct is used for moves at the root of the tree. For each root move
/// we store a score and a PV (really a refutation in the case of moves which // we store a score and a PV (really a refutation in the case of moves which
/// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves. // fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves.
struct RootMove { struct RootMove {
explicit RootMove(Move m) : pv(1, m) {} explicit RootMove(Move m) :
bool extract_ponder_from_tt(Position& pos); pv(1, m) {}
bool extract_ponder_from_tt(const TranspositionTable& tt, Position& pos);
bool operator==(const Move& m) const { return pv[0] == m; } bool operator==(const Move& m) const { return pv[0] == m; }
bool operator<(const RootMove& m) const { // Sort in descending order // Sort in descending order
return m.score != score ? m.score < score bool operator<(const RootMove& m) const {
: m.previousScore < previousScore; return m.score != score ? m.score < score : m.previousScore < previousScore;
} }
uint64_t effort = 0;
Value score = -VALUE_INFINITE; Value score = -VALUE_INFINITE;
Value previousScore = -VALUE_INFINITE; Value previousScore = -VALUE_INFINITE;
Value averageScore = -VALUE_INFINITE; Value averageScore = -VALUE_INFINITE;
@@ -83,32 +107,188 @@ struct RootMove {
using RootMoves = std::vector<RootMove>; using RootMoves = std::vector<RootMove>;
/// LimitsType struct stores information sent by GUI about available time to // LimitsType struct stores information sent by GUI about available time to
/// search the current move, maximum depth/time, or if we are in analysis mode. // search the current move, maximum depth/time, or if we are in analysis mode.
struct LimitsType { struct LimitsType {
LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC // Init explicitly due to broken value-initialization of non POD in MSVC
LimitsType() {
time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movetime = TimePoint(0); time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movetime = TimePoint(0);
movestogo = depth = mate = perft = infinite = 0; movestogo = depth = mate = perft = infinite = 0;
nodes = 0; nodes = 0;
ponderMode = false;
} }
bool use_time_management() const { bool use_time_management() const { return Cluster::is_root() && (time[WHITE] || time[BLACK]); }
return time[WHITE] || time[BLACK];
}
std::vector<Move> searchmoves; std::vector<Move> searchmoves;
TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime; TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime;
int movestogo, depth, mate, perft, infinite; int movestogo, depth, mate, perft, infinite;
int64_t nodes; uint64_t nodes;
bool ponderMode;
}; };
extern LimitsType Limits;
void init(); // The UCI stores the uci options, thread pool, and transposition table.
// This struct is used to easily forward data to the Search::Worker class.
struct SharedState {
SharedState(const OptionsMap& optionsMap,
ThreadPool& threadPool,
TranspositionTable& transpositionTable,
const Eval::NNUE::Networks& nets) :
options(optionsMap),
threads(threadPool),
tt(transpositionTable),
networks(nets) {}
const OptionsMap& options;
ThreadPool& threads;
TranspositionTable& tt;
const Eval::NNUE::Networks& networks;
};
class Worker;
// Null Object Pattern, implement a common interface for the SearchManagers.
// A Null Object will be given to non-mainthread workers.
class ISearchManager {
public:
virtual ~ISearchManager() {}
virtual void check_time(Search::Worker&) = 0;
};
// SearchManager manages the search from the main thread. It is responsible for
// keeping track of the time, and storing data strictly related to the main thread.
class SearchManager: public ISearchManager {
public:
void check_time(Search::Worker& worker) override;
std::string pv(const Search::Worker& worker,
const ThreadPool& threads,
const TranspositionTable& tt,
Depth depth) const;
Stockfish::TimeManagement tm;
int callsCnt;
std::atomic_bool ponder;
std::array<Value, 4> iterValue;
double previousTimeReduction;
Value bestPreviousScore;
Value bestPreviousAverageScore;
bool stopOnPonderhit;
size_t id;
};
class NullSearchManager: public ISearchManager {
public:
void check_time(Search::Worker&) override {}
};
// Search::Worker is the class that does the actual search.
// It is instantiated once per thread, and it is responsible for keeping track
// of the search history, and storing data required for the search.
class Worker {
public:
Worker(SharedState&, std::unique_ptr<ISearchManager>, size_t);
// Called at instantiation to initialize Reductions tables
// Reset histories, usually before a new game
void clear(); void clear();
// Called when the program receives the UCI 'go' command.
// It searches from the root position and outputs the "bestmove".
void start_searching();
bool is_mainthread() const { return thread_idx == 0; }
// Public because they need to be updatable by the stats
CounterMoveHistory counterMoves;
ButterflyHistory mainHistory;
CapturePieceToHistory captureHistory;
ContinuationHistory continuationHistory[2][2];
PawnHistory pawnHistory;
CorrectionHistory correctionHistory;
#ifdef USE_MPI
struct {
std::mutex mutex;
Cluster::TTCache<Cluster::TTCacheSize> buffer = {};
} ttCache;
#endif
std::atomic<uint64_t> TTsaves;
friend void Cluster::save(TranspositionTable&,
ThreadPool&,
Search::Worker*,
TTEntry* tte,
Key k,
Value v,
bool PvHit,
Bound b,
Depth d,
Move m,
Value ev,
uint8_t generation8);
private:
void iterative_deepening();
// Main search function for both PV and non-PV nodes
template<NodeType nodeType>
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode);
// Quiescence search function, which is called by the main search
template<NodeType nodeType>
Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = 0);
Depth reduction(bool i, Depth d, int mn, int delta);
// Get a pointer to the search manager, only allowed to be called by the
// main thread.
SearchManager* main_manager() const {
assert(thread_idx == 0);
return static_cast<SearchManager*>(manager.get());
}
LimitsType limits;
size_t pvIdx, pvLast;
std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
int selDepth, nmpMinPly;
Value optimism[COLOR_NB];
Position rootPos;
StateInfo rootState;
RootMoves rootMoves;
Depth rootDepth, completedDepth;
Value rootDelta;
size_t thread_idx;
// Reductions lookup table initialized at startup
std::array<int, MAX_MOVES> reductions; // [depth or moveNumber]
// The main thread has a SearchManager, the others have a NullSearchManager
std::unique_ptr<ISearchManager> manager;
Tablebases::Config tbConfig;
const OptionsMap& options;
ThreadPool& threads;
TranspositionTable& tt;
const Eval::NNUE::Networks& networks;
friend class Stockfish::ThreadPool;
friend class SearchManager;
};
} // namespace Search } // namespace Search
} // namespace Stockfish } // namespace Stockfish
+311 -195
View File
File diff suppressed because it is too large Load Diff
+26 -27
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,12 +19,31 @@
#ifndef TBPROBE_H #ifndef TBPROBE_H
#define TBPROBE_H #define TBPROBE_H
#include <ostream> #include <string>
#include <vector>
#include "../search.h"
namespace Stockfish {
class Position;
class OptionsMap;
using Depth = int;
namespace Search {
struct RootMove;
using RootMoves = std::vector<RootMove>;
}
}
namespace Stockfish::Tablebases { namespace Stockfish::Tablebases {
struct Config {
int cardinality = 0;
bool rootInTB = false;
bool useRule50 = false;
Depth probeDepth = 0;
};
enum WDLScore { enum WDLScore {
WDLLoss = -2, // Loss WDLLoss = -2, // Loss
WDLBlessedLoss = -1, // Loss, but draw under 50-move rule WDLBlessedLoss = -1, // Loss, but draw under 50-move rule
@@ -43,33 +62,13 @@ enum ProbeState {
extern int MaxCardinality; extern int MaxCardinality;
void init(const std::string& paths); void init(const std::string& paths);
WDLScore probe_wdl(Position& pos, ProbeState* result); WDLScore probe_wdl(Position& pos, ProbeState* result);
int probe_dtz(Position& pos, ProbeState* result); int probe_dtz(Position& pos, ProbeState* result);
bool root_probe(Position& pos, Search::RootMoves& rootMoves); bool root_probe(Position& pos, Search::RootMoves& rootMoves, bool rule50);
bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves); bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, bool rule50);
void rank_root_moves(Position& pos, Search::RootMoves& rootMoves); Config rank_root_moves(const OptionsMap& options, Position& pos, Search::RootMoves& rootMoves);
inline std::ostream& operator<<(std::ostream& os, const WDLScore v) {
os << (v == WDLLoss ? "Loss" :
v == WDLBlessedLoss ? "Blessed loss" :
v == WDLDraw ? "Draw" :
v == WDLCursedWin ? "Cursed win" :
v == WDLWin ? "Win" : "None");
return os;
}
inline std::ostream& operator<<(std::ostream& os, const ProbeState v) {
os << (v == FAIL ? "Failed" :
v == OK ? "Success" :
v == CHANGE_STM ? "Probed opponent side" :
v == ZEROING_BEST_MOVE ? "Best move zeroes DTZ" : "None");
return os;
}
} // namespace Stockfish::Tablebases } // namespace Stockfish::Tablebases
+137 -93
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -16,33 +16,43 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cassert> #include "thread.h"
#include <algorithm> // For std::count #include <algorithm>
#include <cassert>
#include <deque>
#include <memory>
#include <unordered_map>
#include <utility>
#include "cluster.h"
#include "misc.h"
#include "movegen.h" #include "movegen.h"
#include "search.h" #include "search.h"
#include "thread.h"
#include "uci.h"
#include "syzygy/tbprobe.h" #include "syzygy/tbprobe.h"
#include "timeman.h"
#include "tt.h" #include "tt.h"
#include "types.h"
#include "ucioption.h"
namespace Stockfish { namespace Stockfish {
ThreadPool Threads; // Global object // Constructor launches the thread and waits until it goes to sleep
// in idle_loop(). Note that 'searching' and 'exit' should be already set.
Thread::Thread(Search::SharedState& sharedState,
/// Thread constructor launches the thread and waits until it goes to sleep std::unique_ptr<Search::ISearchManager> sm,
/// in idle_loop(). Note that 'searching' and 'exit' should be already set. size_t n) :
worker(std::make_unique<Search::Worker>(sharedState, std::move(sm), n)),
Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) { idx(n),
nthreads(sharedState.options["Threads"]),
stdThread(&Thread::idle_loop, this) {
wait_for_search_finished(); wait_for_search_finished();
} }
/// Thread destructor wakes up the thread in idle_loop() and waits // Destructor wakes up the thread in idle_loop() and waits
/// for its termination. Thread should be already waiting. // for its termination. Thread should be already waiting.
Thread::~Thread() { Thread::~Thread() {
assert(!searching); assert(!searching);
@@ -53,24 +63,7 @@ Thread::~Thread() {
} }
/// Thread::clear() reset histories, usually before a new game // Wakes up the thread that will start the search
void Thread::clear() {
counterMoves.fill(MOVE_NONE);
mainHistory.fill(0);
captureHistory.fill(0);
for (bool inCheck : { false, true })
for (StatsType c : { NoCaptures, Captures })
for (auto& to : continuationHistory[inCheck][c])
for (auto& h : to)
h->fill(-71);
}
/// Thread::start_searching() wakes up the thread that will start the search
void Thread::start_searching() { void Thread::start_searching() {
mutex.lock(); mutex.lock();
searching = true; searching = true;
@@ -79,9 +72,8 @@ void Thread::start_searching() {
} }
/// Thread::wait_for_search_finished() blocks on the condition variable // Blocks on the condition variable
/// until the thread has finished searching. // until the thread has finished searching.
void Thread::wait_for_search_finished() { void Thread::wait_for_search_finished() {
std::unique_lock<std::mutex> lk(mutex); std::unique_lock<std::mutex> lk(mutex);
@@ -89,18 +81,18 @@ void Thread::wait_for_search_finished() {
} }
/// Thread::idle_loop() is where the thread is parked, blocked on the // Thread gets parked here, blocked on the
/// condition variable, when it has no work to do. // condition variable, when it has no work to do.
void Thread::idle_loop() { void Thread::idle_loop() {
// If OS already scheduled us on a different group than 0 then don't overwrite // If OS already scheduled us on a different group than 0 then don't overwrite
// the choice, eventually we are one of many one-threaded processes running on // the choice, eventually we are one of many one-threaded processes running on
// some Windows NUMA hardware, for instance in fishtest. To make it simple, // some Windows NUMA hardware, for instance in fishtest. To make it simple,
// just check if running threads are below a threshold, in this case all this // just check if running threads are below a threshold, in this case, all this
// NUMA machinery is not needed. // NUMA machinery is not needed.
if (Options["Threads"] > 8) if (nthreads > 8)
WinProcGroup::bindThisThread(idx); WinProcGroup::bind_this_thread(idx);
while (true) while (true)
{ {
@@ -114,67 +106,84 @@ void Thread::idle_loop() {
lk.unlock(); lk.unlock();
search(); worker->start_searching();
} }
} }
/// ThreadPool::set() creates/destroys threads to match the requested number. Search::SearchManager* ThreadPool::main_manager() {
/// Created and launched threads will immediately go to sleep in idle_loop. return static_cast<Search::SearchManager*>(main_thread()->worker.get()->manager.get());
/// Upon resizing, threads are recreated to allow for binding if necessary. }
void ThreadPool::set(size_t requested) { uint64_t ThreadPool::nodes_searched() const { return accumulate(&Search::Worker::nodes); }
uint64_t ThreadPool::tb_hits() const { return accumulate(&Search::Worker::tbHits); }
uint64_t ThreadPool::TT_saves() const { return accumulate(&Search::Worker::TTsaves); }
// Creates/destroys threads to match the requested number.
// Created and launched threads will immediately go to sleep in idle_loop.
// Upon resizing, threads are recreated to allow for binding if necessary.
void ThreadPool::set(Search::SharedState sharedState) {
if (threads.size() > 0) // destroy any existing thread(s) if (threads.size() > 0) // destroy any existing thread(s)
{ {
main()->wait_for_search_finished(); main_thread()->wait_for_search_finished();
while (threads.size() > 0) while (threads.size() > 0)
delete threads.back(), threads.pop_back(); delete threads.back(), threads.pop_back();
} }
const size_t requested = sharedState.options["Threads"];
if (requested > 0) // create new thread(s) if (requested > 0) // create new thread(s)
{ {
threads.push_back(new MainThread(0)); threads.push_back(new Thread(
sharedState, std::unique_ptr<Search::ISearchManager>(new Search::SearchManager()), 0));
while (threads.size() < requested) while (threads.size() < requested)
threads.push_back(new Thread(threads.size())); threads.push_back(new Thread(
sharedState, std::unique_ptr<Search::ISearchManager>(new Search::NullSearchManager()),
threads.size()));
clear(); clear();
main_thread()->wait_for_search_finished();
// Reallocate the hash with the new threadpool size // Reallocate the hash with the new threadpool size
TT.resize(size_t(Options["Hash"])); sharedState.tt.resize(sharedState.options["Hash"], requested);
// Init thread number dependent search params. // Adjust cluster buffers
Search::init(); Cluster::ttSendRecvBuff_resize(requested);
} }
} }
/// ThreadPool::clear() sets threadPool data to initial values // Sets threadPool data to initial values
void ThreadPool::clear() { void ThreadPool::clear() {
for (Thread* th : threads) for (Thread* th : threads)
th->clear(); th->worker->clear();
main()->callsCnt = 0; main_manager()->callsCnt = 0;
main()->bestPreviousScore = VALUE_INFINITE; main_manager()->bestPreviousScore = VALUE_INFINITE;
main()->bestPreviousAverageScore = VALUE_INFINITE; main_manager()->bestPreviousAverageScore = VALUE_INFINITE;
main()->previousTimeReduction = 1.0; main_manager()->previousTimeReduction = 1.0;
main_manager()->tm.clear();
} }
/// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and // Wakes up main thread waiting in idle_loop() and
/// returns immediately. Main thread will wake up other threads and start the search. // returns immediately. Main thread will wake up other threads and start the search.
void ThreadPool::start_thinking(const OptionsMap& options,
Position& pos,
StateListPtr& states,
Search::LimitsType limits) {
void ThreadPool::start_thinking(Position& pos, StateListPtr& states, main_thread()->wait_for_search_finished();
const Search::LimitsType& limits, bool ponderMode) {
main()->wait_for_search_finished(); main_manager()->stopOnPonderhit = stop = abortedSearch = false;
main_manager()->ponder = limits.ponderMode;
main()->stopOnPonderhit = stop = false;
increaseDepth = true; increaseDepth = true;
main()->ponder = ponderMode;
Search::Limits = limits;
Search::RootMoves rootMoves; Search::RootMoves rootMoves;
for (const auto& m : MoveList<LEGAL>(pos)) for (const auto& m : MoveList<LEGAL>(pos))
@@ -182,8 +191,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
|| std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m)) || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m))
rootMoves.emplace_back(m); rootMoves.emplace_back(m);
if (!rootMoves.empty()) Tablebases::Config tbConfig = Tablebases::rank_root_moves(options, pos, rootMoves);
Tablebases::rank_root_moves(pos, rootMoves);
// After ownership transfer 'states' becomes empty, so if we stop the search // After ownership transfer 'states' becomes empty, so if we stop the search
// and call 'go' again without setting a new position states.get() == nullptr. // and call 'go' again without setting a new position states.get() == nullptr.
@@ -199,55 +207,91 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
// since they are read-only. // since they are read-only.
for (Thread* th : threads) for (Thread* th : threads)
{ {
th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0; th->worker->limits = limits;
th->rootDepth = th->completedDepth = 0; th->worker->nodes = th->worker->tbHits = th->worker->nmpMinPly =
th->rootMoves = rootMoves; th->worker->bestMoveChanges = 0;
th->rootPos.set(pos.fen(), pos.is_chess960(), &th->rootState, th); th->worker->TTsaves = 0;
th->rootState = setupStates->back(); th->worker->rootDepth = th->worker->completedDepth = 0;
th->worker->rootMoves = rootMoves;
th->worker->rootPos.set(pos.fen(), pos.is_chess960(), &th->worker->rootState);
th->worker->rootState = setupStates->back();
th->worker->tbConfig = tbConfig;
} }
main()->start_searching(); Cluster::signals_init();
main_thread()->start_searching();
} }
Thread* ThreadPool::get_best_thread() const { Thread* ThreadPool::get_best_thread() const {
Thread* bestThread = threads.front(); Thread* bestThread = threads.front();
std::map<Move, int64_t> votes;
Value minScore = VALUE_NONE; Value minScore = VALUE_NONE;
// Find minimum score of all threads std::unordered_map<Move, int64_t, Move::MoveHash> votes(
2 * std::min(size(), bestThread->worker->rootMoves.size()));
// Find the minimum score of all threads
for (Thread* th : threads) for (Thread* th : threads)
minScore = std::min(minScore, th->rootMoves[0].score); minScore = std::min(minScore, th->worker->rootMoves[0].score);
// Vote according to score and depth, and select the best thread // Vote according to score and depth, and select the best thread
auto thread_value = [minScore](Thread* th) { auto thread_voting_value = [minScore](Thread* th) {
return (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); return (th->worker->rootMoves[0].score - minScore + 14) * int(th->worker->completedDepth);
}; };
for (Thread* th : threads) for (Thread* th : threads)
votes[th->rootMoves[0].pv[0]] += thread_value(th); votes[th->worker->rootMoves[0].pv[0]] += thread_voting_value(th);
for (Thread* th : threads) for (Thread* th : threads)
if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY)
{ {
// Make sure we pick the shortest mate / TB conversion or stave off mate the longest const auto bestThreadScore = bestThread->worker->rootMoves[0].score;
if (th->rootMoves[0].score > bestThread->rootMoves[0].score) const auto newThreadScore = th->worker->rootMoves[0].score;
const auto& bestThreadPV = bestThread->worker->rootMoves[0].pv;
const auto& newThreadPV = th->worker->rootMoves[0].pv;
const auto bestThreadMoveVote = votes[bestThreadPV[0]];
const auto newThreadMoveVote = votes[newThreadPV[0]];
const bool bestThreadInProvenWin = bestThreadScore >= VALUE_TB_WIN_IN_MAX_PLY;
const bool newThreadInProvenWin = newThreadScore >= VALUE_TB_WIN_IN_MAX_PLY;
const bool bestThreadInProvenLoss =
bestThreadScore != -VALUE_INFINITE && bestThreadScore <= VALUE_TB_LOSS_IN_MAX_PLY;
const bool newThreadInProvenLoss =
newThreadScore != -VALUE_INFINITE && newThreadScore <= VALUE_TB_LOSS_IN_MAX_PLY;
// Note that we make sure not to pick a thread with truncated-PV for better viewer experience.
const bool betterVotingValue =
thread_voting_value(th) * int(newThreadPV.size() > 2)
> thread_voting_value(bestThread) * int(bestThreadPV.size() > 2);
if (bestThreadInProvenWin)
{
// Make sure we pick the shortest mate / TB conversion
if (newThreadScore > bestThreadScore)
bestThread = th; bestThread = th;
} }
else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY else if (bestThreadInProvenLoss)
|| ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY {
&& ( votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]] // Make sure we pick the shortest mated / TB conversion
|| ( votes[th->rootMoves[0].pv[0]] == votes[bestThread->rootMoves[0].pv[0]] if (newThreadInProvenLoss && newThreadScore < bestThreadScore)
&& thread_value(th) * int(th->rootMoves[0].pv.size() > 2)
> thread_value(bestThread) * int(bestThread->rootMoves[0].pv.size() > 2)))))
bestThread = th; bestThread = th;
}
else if (newThreadInProvenWin || newThreadInProvenLoss
|| (newThreadScore > VALUE_TB_LOSS_IN_MAX_PLY
&& (newThreadMoveVote > bestThreadMoveVote
|| (newThreadMoveVote == bestThreadMoveVote && betterVotingValue))))
bestThread = th;
}
return bestThread; return bestThread;
} }
/// Start non-main threads // Start non-main threads
// Will be invoked by main thread after it has started searching
void ThreadPool::start_searching() { void ThreadPool::start_searching() {
for (Thread* th : threads) for (Thread* th : threads)
@@ -256,7 +300,7 @@ void ThreadPool::start_searching() {
} }
/// Wait for non-main threads // Wait for non-main threads
void ThreadPool::wait_for_search_finished() const { void ThreadPool::wait_for_search_finished() const {
+45 -65
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,98 +21,80 @@
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <mutex> #include <mutex>
#include <thread>
#include <vector> #include <vector>
#include "material.h"
#include "movepick.h" #include "movepick.h"
#include "pawns.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "thread_win32_osx.h" #include "thread_win32_osx.h"
namespace Stockfish { namespace Stockfish {
/// Thread class keeps together all the thread-related stuff. We use
/// per-thread pawn and material hash tables so that once we get a
/// pointer to an entry its life time is unlimited and we don't have
/// to care about someone changing the entry under our feet.
class OptionsMap;
using Value = int;
// Abstraction of a thread. It contains a pointer to the worker and a native thread.
// After construction, the native thread is started with idle_loop()
// waiting for a signal to start searching.
// When the signal is received, the thread starts searching and when
// the search is finished, it goes back to idle_loop() waiting for a new signal.
class Thread { class Thread {
std::mutex mutex;
std::condition_variable cv;
size_t idx;
bool exit = false, searching = true; // Set before starting std::thread
NativeThread stdThread;
public: public:
explicit Thread(size_t); Thread(Search::SharedState&, std::unique_ptr<Search::ISearchManager>, size_t);
virtual ~Thread(); virtual ~Thread();
virtual void search();
void clear();
void idle_loop(); void idle_loop();
void start_searching(); void start_searching();
void wait_for_search_finished(); void wait_for_search_finished();
size_t id() const { return idx; } size_t id() const { return idx; }
Pawns::Table pawnsTable; std::unique_ptr<Search::Worker> worker;
Material::Table materialTable;
size_t pvIdx, pvLast;
std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
int selDepth, nmpMinPly;
Value bestValue, optimism[COLOR_NB];
Position rootPos; private:
StateInfo rootState; std::mutex mutex;
Search::RootMoves rootMoves; std::condition_variable cv;
Depth rootDepth, completedDepth; size_t idx, nthreads;
Value rootDelta; bool exit = false, searching = true; // Set before starting std::thread
CounterMoveHistory counterMoves; NativeThread stdThread;
ButterflyHistory mainHistory;
CapturePieceToHistory captureHistory;
ContinuationHistory continuationHistory[2][2];
}; };
/// MainThread is a derived class specific for main thread // ThreadPool struct handles all the threads-related stuff like init, starting,
// parking and, most importantly, launching a thread. All the access to threads
// is done through this class.
class ThreadPool {
struct MainThread : public Thread { public:
~ThreadPool() {
// destroy any existing thread(s)
if (threads.size() > 0)
{
main_thread()->wait_for_search_finished();
using Thread::Thread; while (threads.size() > 0)
delete threads.back(), threads.pop_back();
}
}
void search() override; void start_thinking(const OptionsMap&, Position&, StateListPtr&, Search::LimitsType);
void check_time();
double previousTimeReduction;
Value bestPreviousScore;
Value bestPreviousAverageScore;
Value iterValue[4];
int callsCnt;
bool stopOnPonderhit;
std::atomic_bool ponder;
};
/// ThreadPool struct handles all the threads-related stuff like init, starting,
/// parking and, most importantly, launching a thread. All the access to threads
/// is done through this class.
struct ThreadPool {
void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false);
void clear(); void clear();
void set(size_t); void set(Search::SharedState);
MainThread* main() const { return static_cast<MainThread*>(threads.front()); } Search::SearchManager* main_manager();
uint64_t nodes_searched() const { return accumulate(&Thread::nodes); } Thread* main_thread() const { return threads.front(); }
uint64_t tb_hits() const { return accumulate(&Thread::tbHits); } uint64_t nodes_searched() const;
uint64_t tb_hits() const;
uint64_t TT_saves() const;
Thread* get_best_thread() const; Thread* get_best_thread() const;
void start_searching(); void start_searching();
void wait_for_search_finished() const; void wait_for_search_finished() const;
std::atomic_bool stop, increaseDepth; std::atomic_bool stop, abortedSearch, increaseDepth;
auto cbegin() const noexcept { return threads.cbegin(); } auto cbegin() const noexcept { return threads.cbegin(); }
auto begin() noexcept { return threads.begin(); } auto begin() noexcept { return threads.begin(); }
@@ -125,17 +107,15 @@ private:
StateListPtr setupStates; StateListPtr setupStates;
std::vector<Thread*> threads; std::vector<Thread*> threads;
uint64_t accumulate(std::atomic<uint64_t> Thread::* member) const { uint64_t accumulate(std::atomic<uint64_t> Search::Worker::*member) const {
uint64_t sum = 0; uint64_t sum = 0;
for (Thread* th : threads) for (Thread* th : threads)
sum += (th->*member).load(std::memory_order_relaxed); sum += (th->worker.get()->*member).load(std::memory_order_relaxed);
return sum; return sum;
} }
}; };
extern ThreadPool Threads;
} // namespace Stockfish } // namespace Stockfish
#endif // #ifndef THREAD_H_INCLUDED #endif // #ifndef THREAD_H_INCLUDED
+25 -21
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,41 +21,45 @@
#include <thread> #include <thread>
/// On OSX threads other than the main thread are created with a reduced stack // On OSX threads other than the main thread are created with a reduced stack
/// size of 512KB by default, this is too low for deep searches, which require // size of 512KB by default, this is too low for deep searches, which require
/// somewhat more than 1MB stack, so adjust it to TH_STACK_SIZE. // somewhat more than 1MB stack, so adjust it to TH_STACK_SIZE.
/// The implementation calls pthread_create() with the stack size parameter // The implementation calls pthread_create() with the stack size parameter
/// equal to the linux 8MB default, on platforms that support it. // equal to the Linux 8MB default, on platforms that support it.
#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(USE_PTHREADS) #if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(USE_PTHREADS)
#include <pthread.h> #include <pthread.h>
#include <functional>
namespace Stockfish { namespace Stockfish {
static const size_t TH_STACK_SIZE = 8 * 1024 * 1024;
template <class T, class P = std::pair<T*, void(T::*)()>>
void* start_routine(void* ptr)
{
P* p = reinterpret_cast<P*>(ptr);
(p->first->*(p->second))(); // Call member function pointer
delete p;
return nullptr;
}
class NativeThread { class NativeThread {
pthread_t thread; pthread_t thread;
static constexpr size_t TH_STACK_SIZE = 8 * 1024 * 1024;
public: public:
template<class T, class P = std::pair<T*, void(T::*)()>> template<class Function, class... Args>
explicit NativeThread(void(T::*fun)(), T* obj) { explicit NativeThread(Function&& fun, Args&&... args) {
auto func = new std::function<void()>(
std::bind(std::forward<Function>(fun), std::forward<Args>(args)...));
pthread_attr_t attr_storage, *attr = &attr_storage; pthread_attr_t attr_storage, *attr = &attr_storage;
pthread_attr_init(attr); pthread_attr_init(attr);
pthread_attr_setstacksize(attr, TH_STACK_SIZE); pthread_attr_setstacksize(attr, TH_STACK_SIZE);
pthread_create(&thread, attr, start_routine<T>, new P(obj, fun));
auto start_routine = [](void* ptr) -> void* {
auto f = reinterpret_cast<std::function<void()>*>(ptr);
// Call the function
(*f)();
delete f;
return nullptr;
};
pthread_create(&thread, attr, start_routine, func);
} }
void join() { pthread_join(thread, nullptr); } void join() { pthread_join(thread, nullptr); }
}; };
+56 -33
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -16,35 +16,49 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "timeman.h"
#include <algorithm> #include <algorithm>
#include <cfloat> #include <cassert>
#include <cmath> #include <cmath>
#include <cstdint>
#include "search.h" #include "search.h"
#include "timeman.h" #include "ucioption.h"
#include "uci.h"
namespace Stockfish { namespace Stockfish {
TimeManagement Time; // Our global time management object TimePoint TimeManagement::optimum() const { return optimumTime; }
TimePoint TimeManagement::maximum() const { return maximumTime; }
TimePoint TimeManagement::elapsed(size_t nodes) const {
return useNodesTime ? TimePoint(nodes) : now() - startTime;
}
void TimeManagement::clear() {
availableNodes = 0; // When in 'nodes as time' mode
}
/// TimeManagement::init() is called at the beginning of the search and calculates void TimeManagement::advance_nodes_time(std::int64_t nodes) {
/// the bounds of time allowed for the current game ply. We currently support: assert(useNodesTime);
availableNodes += nodes;
}
// Called at the beginning of the search and calculates
// the bounds of time allowed for the current game ply. We currently support:
// 1) x basetime (+ z increment) // 1) x basetime (+ z increment)
// 2) x moves in y seconds (+ z increment) // 2) x moves in y seconds (+ z increment)
void TimeManagement::init(Search::LimitsType& limits,
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { Color us,
int ply,
// if we have no time, no need to initialize TM, except for the start time, const OptionsMap& options) {
// If we have no time, no need to initialize TM, except for the start time,
// which is used by movetime. // which is used by movetime.
startTime = limits.startTime; startTime = limits.startTime;
if (limits.time[us] == 0) if (limits.time[us] == 0)
return; return;
TimePoint moveOverhead = TimePoint(Options["Move Overhead"]); TimePoint moveOverhead = TimePoint(options["Move Overhead"]);
TimePoint slowMover = TimePoint(Options["Slow Mover"]); TimePoint npmsec = TimePoint(options["nodestime"]);
TimePoint npmsec = TimePoint(Options["nodestime"]);
// optScale is a percentage of available time to use for the current move. // optScale is a percentage of available time to use for the current move.
// maxScale is a multiplier applied to optimumTime. // maxScale is a multiplier applied to optimumTime.
@@ -56,6 +70,8 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
// must be much lower than the real engine speed. // must be much lower than the real engine speed.
if (npmsec) if (npmsec)
{ {
useNodesTime = true;
if (!availableNodes) // Only once at game start if (!availableNodes) // Only once at game start
availableNodes = npmsec * limits.time[us]; // Time is in msec availableNodes = npmsec * limits.time[us]; // Time is in msec
@@ -68,41 +84,48 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
// Maximum move horizon of 50 moves // Maximum move horizon of 50 moves
int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50; int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50;
// if less than one second, gradually reduce mtg
if (limits.time[us] < 1000 && (double(mtg) / limits.time[us] > 0.05))
{
mtg = limits.time[us] * 0.05;
}
// Make sure timeLeft is > 0 since we may use it as a divisor // Make sure timeLeft is > 0 since we may use it as a divisor
TimePoint timeLeft = std::max(TimePoint(1), TimePoint timeLeft = std::max(TimePoint(1), limits.time[us] + limits.inc[us] * (mtg - 1)
limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg)); - moveOverhead * (2 + mtg));
// Use extra time with larger increments
double optExtra = std::clamp(1.0 + 12.0 * limits.inc[us] / limits.time[us], 1.0, 1.12);
// A user may scale time usage by setting UCI option "Slow Mover"
// Default is 100 and changing this value will probably lose elo.
timeLeft = slowMover * timeLeft / 100;
// x basetime (+ z increment) // x basetime (+ z increment)
// If there is a healthy increment, timeLeft can exceed actual available // If there is a healthy increment, timeLeft can exceed the actual available
// game time for the current move, so also cap to 20% of available game time. // game time for the current move, so also cap to a percentage of available game time.
if (limits.movestogo == 0) if (limits.movestogo == 0)
{ {
optScale = std::min(0.0120 + std::pow(ply + 3.0, 0.45) * 0.0039, // Use extra time with larger increments
0.2 * limits.time[us] / double(timeLeft)) double optExtra = limits.inc[us] < 500 ? 1.0 : 1.13;
// Calculate time constants based on current time left.
double optConstant =
std::min(0.00308 + 0.000319 * std::log10(limits.time[us] / 1000.0), 0.00506);
double maxConstant = std::max(3.39 + 3.01 * std::log10(limits.time[us] / 1000.0), 2.93);
optScale = std::min(0.0122 + std::pow(ply + 2.95, 0.462) * optConstant,
0.213 * limits.time[us] / double(timeLeft))
* optExtra; * optExtra;
maxScale = std::min(7.0, 4.0 + ply / 12.0); maxScale = std::min(6.64, maxConstant + ply / 12.0);
} }
// x moves in y seconds (+ z increment) // x moves in y seconds (+ z increment)
else else
{ {
optScale = std::min((0.88 + ply / 116.4) / mtg, optScale = std::min((0.88 + ply / 116.4) / mtg, 0.88 * limits.time[us] / double(timeLeft));
0.88 * limits.time[us] / double(timeLeft));
maxScale = std::min(6.3, 1.5 + 0.11 * mtg); maxScale = std::min(6.3, 1.5 + 0.11 * mtg);
} }
// Never use more than 80% of the available time for this move // Limit the maximum possible time for this move
optimumTime = TimePoint(optScale * timeLeft); optimumTime = TimePoint(optScale * timeLeft);
maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime)); maximumTime =
TimePoint(std::min(0.825 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10;
if (Options["Ponder"]) if (options["Ponder"])
optimumTime += optimumTime / 4; optimumTime += optimumTime / 4;
} }
+22 -13
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,32 +19,41 @@
#ifndef TIMEMAN_H_INCLUDED #ifndef TIMEMAN_H_INCLUDED
#define TIMEMAN_H_INCLUDED #define TIMEMAN_H_INCLUDED
#include <cstddef>
#include <cstdint>
#include "cluster.h"
#include "misc.h" #include "misc.h"
#include "search.h"
#include "thread.h"
namespace Stockfish { namespace Stockfish {
/// The TimeManagement class computes the optimal time to think depending on class OptionsMap;
/// the maximum available time, the game move number and other parameters.
namespace Search {
struct LimitsType;
}
// The TimeManagement class computes the optimal time to think depending on
// the maximum available time, the game move number, and other parameters.
class TimeManagement { class TimeManagement {
public: public:
void init(Search::LimitsType& limits, Color us, int ply); void init(Search::LimitsType& limits, Color us, int ply, const OptionsMap& options);
TimePoint optimum() const { return optimumTime; }
TimePoint maximum() const { return maximumTime; }
TimePoint elapsed() const { return Search::Limits.npmsec ?
TimePoint(Threads.nodes_searched()) : now() - startTime; }
int64_t availableNodes; // When in 'nodes as time' mode TimePoint optimum() const;
TimePoint maximum() const;
TimePoint elapsed(std::size_t nodes) const;
void clear();
void advance_nodes_time(std::int64_t nodes);
private: private:
TimePoint startTime; TimePoint startTime;
TimePoint optimumTime; TimePoint optimumTime;
TimePoint maximumTime; TimePoint maximumTime;
};
extern TimeManagement Time; std::int64_t availableNodes = 0; // When in 'nodes as time' mode
bool useNodesTime = false; // True if we are in 'nodes as time' mode
};
} // namespace Stockfish } // namespace Stockfish
+61 -64
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -16,54 +16,60 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cstring> // For std::memset #include "tt.h"
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iostream> #include <iostream>
#include <thread> #include <thread>
#include <vector>
#include "bitboard.h"
#include "misc.h" #include "misc.h"
#include "thread.h"
#include "tt.h"
#include "uci.h"
namespace Stockfish { namespace Stockfish {
TranspositionTable TT; // Our global transposition table // Populates the TTEntry with a new node's data, possibly
// overwriting an old position. The update is not atomic and can be racy.
/// TTEntry::save() populates the TTEntry with a new node's data, possibly void TTEntry::save(
/// overwriting an old position. Update is not atomic and can be racy. Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev, uint8_t generation8) {
void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) {
// Preserve any existing move for the same position // Preserve any existing move for the same position
if (m || (uint16_t)k != key16) if (m || uint16_t(k) != key16)
move16 = (uint16_t)m; move16 = m;
// Overwrite less valuable entries (cheapest checks first) // Overwrite less valuable entries (cheapest checks first)
if ( b == BOUND_EXACT if (b == BOUND_EXACT || uint16_t(k) != key16 || d - DEPTH_OFFSET + 2 * pv > depth8 - 4)
|| (uint16_t)k != key16
|| d - DEPTH_OFFSET + 2 * pv > depth8 - 4)
{ {
assert(d > DEPTH_OFFSET); assert(d > DEPTH_OFFSET);
assert(d < 256 + DEPTH_OFFSET); assert(d < 256 + DEPTH_OFFSET);
key16 = (uint16_t)k; key16 = uint16_t(k);
depth8 = (uint8_t)(d - DEPTH_OFFSET); depth8 = uint8_t(d - DEPTH_OFFSET);
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); genBound8 = uint8_t(generation8 | uint8_t(pv) << 2 | b);
value16 = (int16_t)v; value16 = int16_t(v);
eval16 = (int16_t)ev; eval16 = int16_t(ev);
} }
} }
/// TranspositionTable::resize() sets the size of the transposition table, uint8_t TTEntry::relative_age(const uint8_t generation8) const {
/// measured in megabytes. Transposition table consists of a power of 2 number // Due to our packed storage format for generation and its cyclic
/// of clusters and each cluster consists of ClusterSize number of TTEntry. // nature we add GENERATION_CYCLE (256 is the modulus, plus what
// is needed to keep the unrelated lowest n bits from affecting
// the result) to calculate the entry age correctly even after
// generation8 overflows into the next cycle.
void TranspositionTable::resize(size_t mbSize) { return (TranspositionTable::GENERATION_CYCLE + generation8 - genBound8)
& TranspositionTable::GENERATION_MASK;
}
Threads.main()->wait_for_search_finished();
// Sets the size of the transposition table,
// measured in megabytes. Transposition table consists of a power of 2 number
// of clusters and each cluster consists of ClusterSize number of TTEntry.
void TranspositionTable::resize(size_t mbSize, int threadCount) {
aligned_large_pages_free(table); aligned_large_pages_free(table);
clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster);
@@ -71,35 +77,29 @@ void TranspositionTable::resize(size_t mbSize) {
table = static_cast<Cluster*>(aligned_large_pages_alloc(clusterCount * sizeof(Cluster))); table = static_cast<Cluster*>(aligned_large_pages_alloc(clusterCount * sizeof(Cluster)));
if (!table) if (!table)
{ {
std::cerr << "Failed to allocate " << mbSize std::cerr << "Failed to allocate " << mbSize << "MB for transposition table." << std::endl;
<< "MB for transposition table." << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
clear(); clear(threadCount);
} }
/// TranspositionTable::clear() initializes the entire transposition table to zero, // Initializes the entire transposition table to zero,
// in a multi-threaded way. // in a multi-threaded way.
void TranspositionTable::clear(size_t threadCount) {
void TranspositionTable::clear() {
std::vector<std::thread> threads; std::vector<std::thread> threads;
for (size_t idx = 0; idx < size_t(Options["Threads"]); ++idx) for (size_t idx = 0; idx < size_t(threadCount); ++idx)
{ {
threads.emplace_back([this, idx]() { threads.emplace_back([this, idx, threadCount]() {
// Thread binding gives faster search on systems with a first-touch policy // Thread binding gives faster search on systems with a first-touch policy
if (Options["Threads"] > 8) if (threadCount > 8)
WinProcGroup::bindThisThread(idx); WinProcGroup::bind_this_thread(idx);
// Each thread will zero its part of the hash table // Each thread will zero its part of the hash table
const size_t stride = size_t(clusterCount / Options["Threads"]), const size_t stride = size_t(clusterCount / threadCount), start = size_t(stride * idx),
start = size_t(stride * idx), len = idx != size_t(threadCount) - 1 ? stride : clusterCount - start;
len = idx != size_t(Options["Threads"]) - 1 ?
stride : clusterCount - start;
std::memset(&table[start], 0, len * sizeof(Cluster)); std::memset(&table[start], 0, len * sizeof(Cluster));
}); });
@@ -110,51 +110,48 @@ void TranspositionTable::clear() {
} }
/// TranspositionTable::probe() looks up the current position in the transposition // Looks up the current position in the transposition
/// table. It returns true and a pointer to the TTEntry if the position is found. // table. It returns true and a pointer to the TTEntry if the position is found.
/// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry // Otherwise, it returns false and a pointer to an empty or least valuable TTEntry
/// to be replaced later. The replace value of an entry is calculated as its depth // to be replaced later. The replace value of an entry is calculated as its depth
/// minus 8 times its relative age. TTEntry t1 is considered more valuable than // minus 8 times its relative age. TTEntry t1 is considered more valuable than
/// TTEntry t2 if its replace value is greater than that of t2. // TTEntry t2 if its replace value is greater than that of t2.
TTEntry* TranspositionTable::probe(const Key key, bool& found) const { TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
TTEntry* const tte = first_entry(key); TTEntry* const tte = first_entry(key);
const uint16_t key16 = (uint16_t)key; // Use the low 16 bits as key inside the cluster const uint16_t key16 = uint16_t(key); // Use the low 16 bits as key inside the cluster
for (int i = 0; i < ClusterSize; ++i) for (int i = 0; i < ClusterSize; ++i)
if (tte[i].key16 == key16 || !tte[i].depth8) if (tte[i].key16 == key16 || !tte[i].depth8)
{ {
tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & (GENERATION_DELTA - 1))); // Refresh constexpr uint8_t lowerBits = GENERATION_DELTA - 1;
return found = (bool)tte[i].depth8, &tte[i]; // Refresh with new generation, keeping the lower bits the same.
tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & lowerBits));
return found = bool(tte[i].depth8), &tte[i];
} }
// Find an entry to be replaced according to the replacement strategy // Find an entry to be replaced according to the replacement strategy
TTEntry* replace = tte; TTEntry* replace = tte;
for (int i = 1; i < ClusterSize; ++i) for (int i = 1; i < ClusterSize; ++i)
// Due to our packed storage format for generation and its cyclic if (replace->depth8 - replace->relative_age(generation8) * 2
// nature we add GENERATION_CYCLE (256 is the modulus, plus what > tte[i].depth8 - tte[i].relative_age(generation8) * 2)
// is needed to keep the unrelated lowest n bits from affecting
// the result) to calculate the entry age correctly even after
// generation8 overflows into the next cycle.
if ( replace->depth8 - ((GENERATION_CYCLE + generation8 - replace->genBound8) & GENERATION_MASK)
> tte[i].depth8 - ((GENERATION_CYCLE + generation8 - tte[i].genBound8) & GENERATION_MASK))
replace = &tte[i]; replace = &tte[i];
return found = false, replace; return found = false, replace;
} }
/// TranspositionTable::hashfull() returns an approximation of the hashtable // Returns an approximation of the hashtable
/// occupation during a search. The hash is x permill full, as per UCI protocol. // occupation during a search. The hash is x permill full, as per UCI protocol.
// Only counts entries which match the current generation.
int TranspositionTable::hashfull() const { int TranspositionTable::hashfull() const {
int cnt = 0; int cnt = 0;
for (int i = 0; i < 1000; ++i) for (int i = 0; i < 1000; ++i)
for (int j = 0; j < ClusterSize; ++j) for (int j = 0; j < ClusterSize; ++j)
cnt += table[i].entry[j].depth8 && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8; cnt += table[i].entry[j].depth8
&& (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8;
return cnt / ClusterSize; return cnt / ClusterSize;
} }
+49 -26
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,11 +19,18 @@
#ifndef TT_H_INCLUDED #ifndef TT_H_INCLUDED
#define TT_H_INCLUDED #define TT_H_INCLUDED
#include <cstddef>
#include <cstdint>
#include "misc.h" #include "misc.h"
#include "types.h" #include "types.h"
namespace Stockfish { namespace Stockfish {
namespace Cluster {
void init();
}
/// TTEntry struct is the 10 bytes transposition table entry, defined as below: /// TTEntry struct is the 10 bytes transposition table entry, defined as below:
/// ///
/// key 16 bit /// key 16 bit
@@ -37,34 +44,39 @@ namespace Stockfish {
struct TTEntry { struct TTEntry {
Move move() const { return (Move )move16; } Move move() const { return Move(move16); }
Value value() const { return (Value)value16; } Value value() const { return Value(value16); }
Value eval() const { return (Value)eval16; } Value eval() const { return Value(eval16); }
Depth depth() const { return (Depth)depth8 + DEPTH_OFFSET; } Depth depth() const { return Depth(depth8 + DEPTH_OFFSET); }
bool is_pv() const { return (bool)(genBound8 & 0x4); } bool is_pv() const { return bool(genBound8 & 0x4); }
Bound bound() const { return (Bound)(genBound8 & 0x3); } Bound bound() const { return Bound(genBound8 & 0x3); }
void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev); void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev, uint8_t generation8);
// The returned age is a multiple of TranspositionTable::GENERATION_DELTA
uint8_t relative_age(const uint8_t generation8) const;
private: private:
friend class TranspositionTable; friend class TranspositionTable;
friend void Cluster::init();
uint16_t key16; uint16_t key16;
uint8_t depth8; uint8_t depth8;
uint8_t genBound8; uint8_t genBound8;
uint16_t move16; Move move16;
int16_t value16; int16_t value16;
int16_t eval16; int16_t eval16;
}; };
/// A TranspositionTable is an array of Cluster, of size clusterCount. Each // A TranspositionTable is an array of Cluster, of size clusterCount. Each
/// cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry // cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry
/// contains information on exactly one position. The size of a Cluster should // contains information on exactly one position. The size of a Cluster should
/// divide the size of a cache line for best performance, as the cacheline is // divide the size of a cache line for best performance, as the cacheline is
/// prefetched when possible. // prefetched when possible.
class TranspositionTable { class TranspositionTable {
friend void Cluster::init();
static constexpr int ClusterSize = 3; static constexpr int ClusterSize = 3;
struct Cluster { struct Cluster {
@@ -75,33 +87,44 @@ class TranspositionTable {
static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size"); static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size");
// Constants used to refresh the hash table periodically // Constants used to refresh the hash table periodically
static constexpr unsigned GENERATION_BITS = 3; // nb of bits reserved for other things
static constexpr int GENERATION_DELTA = (1 << GENERATION_BITS); // increment for generation field // We have 8 bits available where the lowest 3 bits are
static constexpr int GENERATION_CYCLE = 255 + (1 << GENERATION_BITS); // cycle length // reserved for other things.
static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF; // mask to pull out generation number static constexpr unsigned GENERATION_BITS = 3;
// increment for generation field
static constexpr int GENERATION_DELTA = (1 << GENERATION_BITS);
// cycle length
static constexpr int GENERATION_CYCLE = 255 + GENERATION_DELTA;
// mask to pull out generation number
static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF;
public: public:
~TranspositionTable() { aligned_large_pages_free(table); } ~TranspositionTable() { aligned_large_pages_free(table); }
void new_search() { generation8 += GENERATION_DELTA; } // Lower bits are used for other things
void new_search() {
// increment by delta to keep lower bits as is
generation8 += GENERATION_DELTA;
}
TTEntry* probe(const Key key, bool& found) const; TTEntry* probe(const Key key, bool& found) const;
int hashfull() const; int hashfull() const;
void resize(size_t mbSize); void resize(size_t mbSize, int threadCount);
void clear(); void clear(size_t threadCount);
TTEntry* first_entry(const Key key) const { TTEntry* first_entry(const Key key) const {
return &table[mul_hi64(key, clusterCount)].entry[0]; return &table[mul_hi64(key, clusterCount)].entry[0];
} }
uint8_t generation() const { return generation8; }
private: private:
friend struct TTEntry; friend struct TTEntry;
size_t clusterCount; size_t clusterCount;
Cluster* table; Cluster* table = nullptr;
uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 uint8_t generation8 = 0; // Size must be not bigger than TTEntry::genBound8
}; };
extern TranspositionTable TT;
} // namespace Stockfish } // namespace Stockfish
#endif // #ifndef TT_H_INCLUDED #endif // #ifndef TT_H_INCLUDED
+48 -57
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -16,48 +16,36 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "tune.h"
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
#include <map>
#include <sstream> #include <sstream>
#include <string>
#include "types.h" #include "ucioption.h"
#include "misc.h"
#include "uci.h"
using std::string; using std::string;
namespace Stockfish { namespace Stockfish {
bool Tune::update_on_last; bool Tune::update_on_last;
const UCI::Option* LastOption = nullptr; const Option* LastOption = nullptr;
static std::map<std::string, int> TuneResults; OptionsMap* Tune::options;
string Tune::next(string& names, bool pop) {
string name; namespace {
std::map<std::string, int> TuneResults;
do { void on_tune(const Option& o) {
string token = names.substr(0, names.find(','));
if (pop)
names.erase(0, token.size() + 1);
std::stringstream ws(token);
name += (ws >> token, token); // Remove trailing whitespace
} while ( std::count(name.begin(), name.end(), '(')
- std::count(name.begin(), name.end(), ')'));
return name;
}
static void on_tune(const UCI::Option& o) {
if (!Tune::update_on_last || LastOption == &o) if (!Tune::update_on_last || LastOption == &o)
Tune::read_options(); Tune::read_options();
} }
static void make_option(const string& n, int v, const SetRange& r) {
void make_option(OptionsMap* options, const string& n, int v, const SetRange& r) {
// Do not generate option when there is nothing to tune (ie. min = max) // Do not generate option when there is nothing to tune (ie. min = max)
if (r(v).first == r(v).second) if (r(v).first == r(v).second)
@@ -66,48 +54,54 @@ static void make_option(const string& n, int v, const SetRange& r) {
if (TuneResults.count(n)) if (TuneResults.count(n))
v = TuneResults[n]; v = TuneResults[n];
Options[n] << UCI::Option(v, r(v).first, r(v).second, on_tune); (*options)[n] << Option(v, r(v).first, r(v).second, on_tune);
LastOption = &Options[n]; LastOption = &((*options)[n]);
// Print formatted parameters, ready to be copy-pasted in Fishtest // Print formatted parameters, ready to be copy-pasted in Fishtest
std::cout << n << "," std::cout << n << "," << v << "," << r(v).first << "," << r(v).second << ","
<< v << ","
<< r(v).first << "," << r(v).second << ","
<< (r(v).second - r(v).first) / 20.0 << "," << (r(v).second - r(v).first) / 20.0 << ","
<< "0.0020" << "0.0020" << std::endl;
<< std::endl; }
} }
template<> void Tune::Entry<int>::init_option() { make_option(name, value, range); } string Tune::next(string& names, bool pop) {
template<> void Tune::Entry<int>::read_option() { string name;
if (Options.count(name))
value = int(Options[name]); do
{
string token = names.substr(0, names.find(','));
if (pop)
names.erase(0, token.size() + 1);
std::stringstream ws(token);
name += (ws >> token, token); // Remove trailing whitespace
} while (std::count(name.begin(), name.end(), '(') - std::count(name.begin(), name.end(), ')'));
return name;
} }
template<> void Tune::Entry<Value>::init_option() { make_option(name, value, range); }
template<> void Tune::Entry<Value>::read_option() { template<>
if (Options.count(name)) void Tune::Entry<int>::init_option() {
value = Value(int(Options[name])); make_option(options, name, value, range);
} }
template<> void Tune::Entry<Score>::init_option() { template<>
make_option("m" + name, mg_value(value), range); void Tune::Entry<int>::read_option() {
make_option("e" + name, eg_value(value), range); if (options->count(name))
} value = int((*options)[name]);
template<> void Tune::Entry<Score>::read_option() {
if (Options.count("m" + name))
value = make_score(int(Options["m" + name]), eg_value(value));
if (Options.count("e" + name))
value = make_score(mg_value(value), int(Options["e" + name]));
} }
// Instead of a variable here we have a PostUpdate function: just call it // Instead of a variable here we have a PostUpdate function: just call it
template<> void Tune::Entry<Tune::PostUpdate>::init_option() {} template<>
template<> void Tune::Entry<Tune::PostUpdate>::read_option() { value(); } void Tune::Entry<Tune::PostUpdate>::init_option() {}
template<>
void Tune::Entry<Tune::PostUpdate>::read_option() {
value();
}
} // namespace Stockfish } // namespace Stockfish
@@ -121,13 +115,10 @@ template<> void Tune::Entry<Tune::PostUpdate>::read_option() { value(); }
// //
// Then paste the output below, as the function body // Then paste the output below, as the function body
#include <cmath>
namespace Stockfish { namespace Stockfish {
void Tune::read_results() { void Tune::read_results() { /* ...insert your values here... */
/* ...insert your values here... */
} }
} // namespace Stockfish } // namespace Stockfish
+62 -44
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,24 +19,29 @@
#ifndef TUNE_H_INCLUDED #ifndef TUNE_H_INCLUDED
#define TUNE_H_INCLUDED #define TUNE_H_INCLUDED
#include <cstddef>
#include <memory> #include <memory>
#include <string> #include <string>
#include <type_traits> #include <type_traits> // IWYU pragma: keep
#include <utility>
#include <vector> #include <vector>
namespace Stockfish { namespace Stockfish {
class OptionsMap;
using Range = std::pair<int, int>; // Option's min-max values using Range = std::pair<int, int>; // Option's min-max values
using RangeFun = Range(int); using RangeFun = Range(int);
// Default Range function, to calculate Option's min-max values // Default Range function, to calculate Option's min-max values
inline Range default_range(int v) { inline Range default_range(int v) { return v > 0 ? Range(0, 2 * v) : Range(2 * v, 0); }
return v > 0 ? Range(0, 2 * v) : Range(2 * v, 0);
}
struct SetRange { struct SetRange {
explicit SetRange(RangeFun f) : fun(f) {} explicit SetRange(RangeFun f) :
SetRange(int min, int max) : fun(nullptr), range(min, max) {} fun(f) {}
SetRange(int min, int max) :
fun(nullptr),
range(min, max) {}
Range operator()(int v) const { return fun ? fun(v) : range; } Range operator()(int v) const { return fun ? fun(v) : range; }
RangeFun* fun; RangeFun* fun;
@@ -46,32 +51,30 @@ struct SetRange {
#define SetDefaultRange SetRange(default_range) #define SetDefaultRange SetRange(default_range)
/// Tune class implements the 'magic' code that makes the setup of a fishtest // Tune class implements the 'magic' code that makes the setup of a fishtest tuning
/// tuning session as easy as it can be. Mainly you have just to remove const // session as easy as it can be. Mainly you have just to remove const qualifiers
/// qualifiers from the variables you want to tune and flag them for tuning, so // from the variables you want to tune and flag them for tuning, so if you have:
/// if you have: //
/// // const Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } };
/// const Score myScore = S(10, 15); //
/// const Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } }; // If you have a my_post_update() function to run after values have been updated,
/// // and a my_range() function to set custom Option's min-max values, then you just
/// If you have a my_post_update() function to run after values have been updated, // remove the 'const' qualifiers and write somewhere below in the file:
/// and a my_range() function to set custom Option's min-max values, then you just //
/// remove the 'const' qualifiers and write somewhere below in the file: // TUNE(SetRange(my_range), myValue, my_post_update);
/// //
/// TUNE(SetRange(my_range), myScore, myValue, my_post_update); // You can also set the range directly, and restore the default at the end
/// //
/// You can also set the range directly, and restore the default at the end // TUNE(SetRange(-100, 100), myValue, SetDefaultRange);
/// //
/// TUNE(SetRange(-100, 100), myScore, SetDefaultRange); // In case update function is slow and you have many parameters, you can add:
/// //
/// In case update function is slow and you have many parameters, you can add: // UPDATE_ON_LAST();
/// //
/// UPDATE_ON_LAST(); // And the values update, including post update function call, will be done only
/// // once, after the engine receives the last UCI option, that is the one defined
/// And the values update, including post update function call, will be done only // and created as the last one, so the GUI should send the options in the same
/// once, after the engine receives the last UCI option, that is the one defined // order in which have been defined.
/// and created as the last one, so the GUI should send the options in the same
/// order in which have been defined.
class Tune { class Tune {
@@ -82,7 +85,10 @@ class Tune {
void operator=(const Tune&) = delete; void operator=(const Tune&) = delete;
void read_results(); void read_results();
static Tune& instance() { static Tune t; return t; } // Singleton static Tune& instance() {
static Tune t;
return t;
} // Singleton
// Use polymorphism to accommodate Entry of different types in the same vector // Use polymorphism to accommodate Entry of different types in the same vector
struct EntryBase { struct EntryBase {
@@ -94,14 +100,15 @@ class Tune {
template<typename T> template<typename T>
struct Entry: public EntryBase { struct Entry: public EntryBase {
static_assert(!std::is_const<T>::value, "Parameter cannot be const!"); static_assert(!std::is_const_v<T>, "Parameter cannot be const!");
static_assert( std::is_same<T, int>::value static_assert(std::is_same_v<T, int> || std::is_same_v<T, PostUpdate>,
|| std::is_same<T, Value>::value "Parameter type not supported!");
|| std::is_same<T, Score>::value
|| std::is_same<T, PostUpdate>::value, "Parameter type not supported!");
Entry(const std::string& n, T& v, const SetRange& r) : name(n), value(v), range(r) {} Entry(const std::string& n, T& v, const SetRange& r) :
name(n),
value(v),
range(r) {}
void operator=(const Entry&) = delete; // Because 'value' is a reference void operator=(const Entry&) = delete; // Because 'value' is a reference
void init_option() override; void init_option() override;
void read_option() override; void read_option() override;
@@ -143,14 +150,25 @@ class Tune {
public: public:
template<typename... Args> template<typename... Args>
static int add(const std::string& names, Args&&... args) { static int add(const std::string& names, Args&&... args) {
return instance().add(SetDefaultRange, names.substr(1, names.size() - 2), args...); // Remove trailing parenthesis return instance().add(SetDefaultRange, names.substr(1, names.size() - 2),
args...); // Remove trailing parenthesis
} }
static void init() { for (auto& e : instance().list) e->init_option(); read_options(); } // Deferred, due to UCI::Options access static void init(OptionsMap& o) {
static void read_options() { for (auto& e : instance().list) e->read_option(); } options = &o;
for (auto& e : instance().list)
e->init_option();
read_options();
} // Deferred, due to UCI::Options access
static void read_options() {
for (auto& e : instance().list)
e->read_option();
}
static bool update_on_last; static bool update_on_last;
static OptionsMap* options;
}; };
// Some macro magic :-) we define a dummy int variable that compiler initializes calling Tune::add() // Some macro magic :-) we define a dummy int variable that the compiler initializes calling Tune::add()
#define STRINGIFY(x) #x #define STRINGIFY(x) #x
#define UNIQUE2(x, y) x##y #define UNIQUE2(x, y) x##y
#define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__ #define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__
+175 -255
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,45 +19,44 @@
#ifndef TYPES_H_INCLUDED #ifndef TYPES_H_INCLUDED
#define TYPES_H_INCLUDED #define TYPES_H_INCLUDED
/// When compiling with provided Makefile (e.g. for Linux and OSX), configuration // When compiling with provided Makefile (e.g. for Linux and OSX), configuration
/// is done automatically. To get started type 'make help'. // is done automatically. To get started type 'make help'.
/// //
/// When Makefile is not used (e.g. with Microsoft Visual Studio) some switches // When Makefile is not used (e.g. with Microsoft Visual Studio) some switches
/// need to be set manually: // need to be set manually:
/// //
/// -DNDEBUG | Disable debugging mode. Always use this for release. // -DNDEBUG | Disable debugging mode. Always use this for release.
/// //
/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. You may need this to // -DNO_PREFETCH | Disable use of prefetch asm-instruction. You may need this to
/// | run on some very old machines. // | run on some very old machines.
/// //
/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works // -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works
/// | only in 64-bit mode and requires hardware with popcnt support. // | only in 64-bit mode and requires hardware with popcnt support.
/// //
/// -DUSE_PEXT | Add runtime support for use of pext asm-instruction. Works // -DUSE_PEXT | Add runtime support for use of pext asm-instruction. Works
/// | only in 64-bit mode and requires hardware with pext support. // | only in 64-bit mode and requires hardware with pext support.
#include <cassert> #include <cassert>
#include <cctype>
#include <cstdint> #include <cstdint>
#include <cstdlib>
#include <algorithm>
#if defined(_MSC_VER) #if defined(_MSC_VER)
// Disable some silly and noisy warning from MSVC compiler // Disable some silly and noisy warnings from MSVC compiler
#pragma warning(disable: 4127) // Conditional expression is constant #pragma warning(disable: 4127) // Conditional expression is constant
#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type #pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false' #pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
#endif #endif
/// Predefined macros hell: // Predefined macros hell:
/// //
/// __GNUC__ Compiler is gcc, Clang or Intel on Linux // __GNUC__ Compiler is GCC, Clang or ICX
/// __INTEL_COMPILER Compiler is Intel // __clang__ Compiler is Clang or ICX
/// _MSC_VER Compiler is MSVC or Intel on Windows // __INTEL_LLVM_COMPILER Compiler is ICX
/// _WIN32 Building on Windows (any) // _MSC_VER Compiler is MSVC
/// _WIN64 Building on Windows 64 bit // _WIN32 Building on Windows (any)
// _WIN64 Building on Windows 64 bit
#if defined(__GNUC__ ) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <= 2)) && defined(_WIN32) && !defined(__clang__) #if defined(__GNUC__) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <= 2)) \
&& defined(_WIN32) && !defined(__clang__)
#define ALIGNAS_ON_STACK_VARIABLES_BROKEN #define ALIGNAS_ON_STACK_VARIABLES_BROKEN
#endif #endif
@@ -68,12 +67,12 @@
#define IS_64BIT #define IS_64BIT
#endif #endif
#if defined(USE_POPCNT) && (defined(__INTEL_COMPILER) || defined(_MSC_VER)) #if defined(USE_POPCNT) && defined(_MSC_VER)
# include <nmmintrin.h> // Intel and Microsoft header for _mm_popcnt_u64() #include <nmmintrin.h> // Microsoft header for _mm_popcnt_u64()
#endif #endif
#if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER)) #if !defined(NO_PREFETCH) && defined(_MSC_VER)
# include <xmmintrin.h> // Intel and Microsoft header for _mm_prefetch() #include <xmmintrin.h> // Microsoft header for _mm_prefetch()
#endif #endif
#if defined(USE_PEXT) #if defined(USE_PEXT)
@@ -109,32 +108,10 @@ using Bitboard = uint64_t;
constexpr int MAX_MOVES = 256; constexpr int MAX_MOVES = 256;
constexpr int MAX_PLY = 246; constexpr int MAX_PLY = 246;
/// A move needs 16 bits to be stored
///
/// bit 0- 5: destination square (from 0 to 63)
/// bit 6-11: origin square (from 0 to 63)
/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
/// bit 14-15: special move flag: promotion (1), en passant (2), castling (3)
/// NOTE: en passant bit is set only when a pawn can be captured
///
/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in
/// any normal move destination square is always different from origin square
/// while MOVE_NONE and MOVE_NULL have the same origin and destination square.
enum Move : int {
MOVE_NONE,
MOVE_NULL = 65
};
enum MoveType {
NORMAL,
PROMOTION = 1 << 14,
EN_PASSANT = 2 << 14,
CASTLING = 3 << 14
};
enum Color { enum Color {
WHITE, BLACK, COLOR_NB = 2 WHITE,
BLACK,
COLOR_NB = 2
}; };
enum CastlingRights { enum CastlingRights {
@@ -153,19 +130,6 @@ enum CastlingRights {
CASTLING_RIGHT_NB = 16 CASTLING_RIGHT_NB = 16
}; };
enum Phase {
PHASE_ENDGAME,
PHASE_MIDGAME = 128,
MG = 0, EG = 1, PHASE_NB = 2
};
enum ScaleFactor {
SCALE_FACTOR_DRAW = 0,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255
};
enum Bound { enum Bound {
BOUND_NONE, BOUND_NONE,
BOUND_UPPER, BOUND_UPPER,
@@ -173,31 +137,35 @@ enum Bound {
BOUND_EXACT = BOUND_UPPER | BOUND_LOWER BOUND_EXACT = BOUND_UPPER | BOUND_LOWER
}; };
enum Value : int { // Value is used as an alias for int16_t, this is done to differentiate between
VALUE_ZERO = 0, // a search value and any other integer value. The values used in search are always
VALUE_DRAW = 0, // supposed to be in the range (-VALUE_NONE, VALUE_NONE] and should not exceed this range.
VALUE_KNOWN_WIN = 10000, using Value = int;
VALUE_MATE = 32000,
VALUE_INFINITE = 32001,
VALUE_NONE = 32002,
VALUE_TB_WIN_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, constexpr Value VALUE_ZERO = 0;
VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_TB_WIN_IN_MAX_PLY, constexpr Value VALUE_DRAW = 0;
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, constexpr Value VALUE_NONE = 32002;
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY, constexpr Value VALUE_INFINITE = 32001;
constexpr Value VALUE_MATE = 32000;
constexpr Value VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY;
constexpr Value VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY;
constexpr Value VALUE_TB = VALUE_MATE_IN_MAX_PLY - 1;
constexpr Value VALUE_TB_WIN_IN_MAX_PLY = VALUE_TB - MAX_PLY;
constexpr Value VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_TB_WIN_IN_MAX_PLY;
// In the code, we make the assumption that these values // In the code, we make the assumption that these values
// are such that non_pawn_material() can be used to uniquely // are such that non_pawn_material() can be used to uniquely
// identify the material on the board. // identify the material on the board.
PawnValueMg = 126, PawnValueEg = 208, constexpr Value PawnValue = 208;
KnightValueMg = 781, KnightValueEg = 854, constexpr Value KnightValue = 781;
BishopValueMg = 825, BishopValueEg = 915, constexpr Value BishopValue = 825;
RookValueMg = 1276, RookValueEg = 1380, constexpr Value RookValue = 1276;
QueenValueMg = 2538, QueenValueEg = 2682, constexpr Value QueenValue = 2538;
MidgameLimit = 15258, EndgameLimit = 3915
};
// clang-format off
enum PieceType { enum PieceType {
NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING, NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING,
ALL_PIECES = 0, ALL_PIECES = 0,
@@ -210,26 +178,24 @@ enum Piece {
B_PAWN = PAWN + 8, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING, B_PAWN = PAWN + 8, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
PIECE_NB = 16 PIECE_NB = 16
}; };
// clang-format on
constexpr Value PieceValue[PHASE_NB][PIECE_NB] = { constexpr Value PieceValue[PIECE_NB] = {
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, PawnValue, KnightValue, BishopValue, RookValue, QueenValue, VALUE_ZERO, VALUE_ZERO,
VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO }, VALUE_ZERO, PawnValue, KnightValue, BishopValue, RookValue, QueenValue, VALUE_ZERO, VALUE_ZERO};
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, VALUE_ZERO, VALUE_ZERO,
VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, VALUE_ZERO, VALUE_ZERO }
};
using Depth = int; using Depth = int;
enum : int { enum : int {
DEPTH_QS_CHECKS = 0, DEPTH_QS_CHECKS = 0,
DEPTH_QS_NO_CHECKS = -1, DEPTH_QS_NO_CHECKS = -1,
DEPTH_QS_RECAPTURES = -5,
DEPTH_NONE = -6, DEPTH_NONE = -6,
DEPTH_OFFSET = -7 // value used only for TT entry occupancy check DEPTH_OFFSET = -7 // value used only for TT entry occupancy check
}; };
// clang-format off
enum Square : int { enum Square : int {
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1, SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2, SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
@@ -244,6 +210,7 @@ enum Square : int {
SQUARE_ZERO = 0, SQUARE_ZERO = 0,
SQUARE_NB = 64 SQUARE_NB = 64
}; };
// clang-format on
enum Direction : int { enum Direction : int {
NORTH = 8, NORTH = 8,
@@ -258,11 +225,27 @@ enum Direction : int {
}; };
enum File : int { enum File : int {
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB FILE_A,
FILE_B,
FILE_C,
FILE_D,
FILE_E,
FILE_F,
FILE_G,
FILE_H,
FILE_NB
}; };
enum Rank : int { enum Rank : int {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB RANK_1,
RANK_2,
RANK_3,
RANK_4,
RANK_5,
RANK_6,
RANK_7,
RANK_8,
RANK_NB
}; };
// Keep track of what a move changes on the board (used by NNUE) // Keep track of what a move changes on the board (used by NNUE)
@@ -281,209 +264,146 @@ struct DirtyPiece {
Square to[3]; Square to[3];
}; };
/// Score enum stores a middlegame and an endgame value in a single integer (enum).
/// The least significant 16 bits are used to store the middlegame value and the
/// upper 16 bits are used to store the endgame value. We have to take care to
/// avoid left-shifting a signed int to avoid undefined behavior.
enum Score : int { SCORE_ZERO };
constexpr Score make_score(int mg, int eg) {
return Score((int)((unsigned int)eg << 16) + mg);
}
/// Extracting the signed lower and upper 16 bits is not so trivial because
/// according to the standard a simple cast to short is implementation defined
/// and so is a right shift of a signed integer.
inline Value eg_value(Score s) {
union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s + 0x8000) >> 16) };
return Value(eg.s);
}
inline Value mg_value(Score s) {
union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s)) };
return Value(mg.s);
}
#define ENABLE_BASE_OPERATORS_ON(T) \
constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \
constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \
constexpr T operator-(T d) { return T(-int(d)); } \
inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \
inline T& operator-=(T& d1, int d2) { return d1 = d1 - d2; }
#define ENABLE_INCR_OPERATORS_ON(T) \ #define ENABLE_INCR_OPERATORS_ON(T) \
inline T& operator++(T& d) { return d = T(int(d) + 1); } \ inline T& operator++(T& d) { return d = T(int(d) + 1); } \
inline T& operator--(T& d) { return d = T(int(d) - 1); } inline T& operator--(T& d) { return d = T(int(d) - 1); }
#define ENABLE_FULL_OPERATORS_ON(T) \
ENABLE_BASE_OPERATORS_ON(T) \
constexpr T operator*(int i, T d) { return T(i * int(d)); } \
constexpr T operator*(T d, int i) { return T(int(d) * i); } \
constexpr T operator/(T d, int i) { return T(int(d) / i); } \
constexpr int operator/(T d1, T d2) { return int(d1) / int(d2); } \
inline T& operator*=(T& d, int i) { return d = T(int(d) * i); } \
inline T& operator/=(T& d, int i) { return d = T(int(d) / i); }
ENABLE_FULL_OPERATORS_ON(Value)
ENABLE_FULL_OPERATORS_ON(Direction)
ENABLE_INCR_OPERATORS_ON(Piece)
ENABLE_INCR_OPERATORS_ON(PieceType) ENABLE_INCR_OPERATORS_ON(PieceType)
ENABLE_INCR_OPERATORS_ON(Square) ENABLE_INCR_OPERATORS_ON(Square)
ENABLE_INCR_OPERATORS_ON(File) ENABLE_INCR_OPERATORS_ON(File)
ENABLE_INCR_OPERATORS_ON(Rank) ENABLE_INCR_OPERATORS_ON(Rank)
ENABLE_BASE_OPERATORS_ON(Score)
#undef ENABLE_FULL_OPERATORS_ON
#undef ENABLE_INCR_OPERATORS_ON #undef ENABLE_INCR_OPERATORS_ON
#undef ENABLE_BASE_OPERATORS_ON
/// Additional operators to add a Direction to a Square constexpr Direction operator+(Direction d1, Direction d2) { return Direction(int(d1) + int(d2)); }
constexpr Direction operator*(int i, Direction d) { return Direction(i * int(d)); }
// Additional operators to add a Direction to a Square
constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); } constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); }
constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); } constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); }
inline Square& operator+=(Square& s, Direction d) { return s = s + d; } inline Square& operator+=(Square& s, Direction d) { return s = s + d; }
inline Square& operator-=(Square& s, Direction d) { return s = s - d; } inline Square& operator-=(Square& s, Direction d) { return s = s - d; }
/// Only declared but not defined. We don't want to multiply two scores due to // Toggle color
/// a very high risk of overflow. So user should explicitly convert to integer. constexpr Color operator~(Color c) { return Color(c ^ BLACK); }
Score operator*(Score, Score) = delete;
/// Division of a Score must be handled separately for each term // Swap A1 <-> A8
inline Score operator/(Score s, int i) { constexpr Square flip_rank(Square s) { return Square(s ^ SQ_A8); }
return make_score(mg_value(s) / i, eg_value(s) / i);
}
/// Multiplication of a Score by an integer. We check for overflow in debug mode. // Swap A1 <-> H1
inline Score operator*(Score s, int i) { constexpr Square flip_file(Square s) { return Square(s ^ SQ_H1); }
Score result = Score(int(s) * i); // Swap color of piece B_KNIGHT <-> W_KNIGHT
constexpr Piece operator~(Piece pc) { return Piece(pc ^ 8); }
assert(eg_value(result) == (i * eg_value(s)));
assert(mg_value(result) == (i * mg_value(s)));
assert((i == 0) || (result / i) == s);
return result;
}
/// Multiplication of a Score by a boolean
inline Score operator*(Score s, bool b) {
return b ? s : SCORE_ZERO;
}
constexpr Color operator~(Color c) {
return Color(c ^ BLACK); // Toggle color
}
constexpr Square flip_rank(Square s) { // Swap A1 <-> A8
return Square(s ^ SQ_A8);
}
constexpr Square flip_file(Square s) { // Swap A1 <-> H1
return Square(s ^ SQ_H1);
}
constexpr Piece operator~(Piece pc) {
return Piece(pc ^ 8); // Swap color of piece B_KNIGHT <-> W_KNIGHT
}
constexpr CastlingRights operator&(Color c, CastlingRights cr) { constexpr CastlingRights operator&(Color c, CastlingRights cr) {
return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr); return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr);
} }
constexpr Value mate_in(int ply) { constexpr Value mate_in(int ply) { return VALUE_MATE - ply; }
return VALUE_MATE - ply;
}
constexpr Value mated_in(int ply) { constexpr Value mated_in(int ply) { return -VALUE_MATE + ply; }
return -VALUE_MATE + ply;
}
constexpr Square make_square(File f, Rank r) { constexpr Square make_square(File f, Rank r) { return Square((r << 3) + f); }
return Square((r << 3) + f);
}
constexpr Piece make_piece(Color c, PieceType pt) { constexpr Piece make_piece(Color c, PieceType pt) { return Piece((c << 3) + pt); }
return Piece((c << 3) + pt);
}
constexpr PieceType type_of(Piece pc) { constexpr PieceType type_of(Piece pc) { return PieceType(pc & 7); }
return PieceType(pc & 7);
}
inline Color color_of(Piece pc) { inline Color color_of(Piece pc) {
assert(pc != NO_PIECE); assert(pc != NO_PIECE);
return Color(pc >> 3); return Color(pc >> 3);
} }
constexpr bool is_ok(Move m) { constexpr bool is_ok(Square s) { return s >= SQ_A1 && s <= SQ_H8; }
return m != MOVE_NONE && m != MOVE_NULL;
}
constexpr bool is_ok(Square s) { constexpr File file_of(Square s) { return File(s & 7); }
return s >= SQ_A1 && s <= SQ_H8;
}
constexpr File file_of(Square s) { constexpr Rank rank_of(Square s) { return Rank(s >> 3); }
return File(s & 7);
}
constexpr Rank rank_of(Square s) { constexpr Square relative_square(Color c, Square s) { return Square(s ^ (c * 56)); }
return Rank(s >> 3);
}
constexpr Square relative_square(Color c, Square s) { constexpr Rank relative_rank(Color c, Rank r) { return Rank(r ^ (c * 7)); }
return Square(s ^ (c * 56));
}
constexpr Rank relative_rank(Color c, Rank r) { constexpr Rank relative_rank(Color c, Square s) { return relative_rank(c, rank_of(s)); }
return Rank(r ^ (c * 7));
}
constexpr Rank relative_rank(Color c, Square s) { constexpr Direction pawn_push(Color c) { return c == WHITE ? NORTH : SOUTH; }
return relative_rank(c, rank_of(s));
}
constexpr Direction pawn_push(Color c) {
return c == WHITE ? NORTH : SOUTH;
}
constexpr Square from_sq(Move m) { // Based on a congruential pseudo-random number generator
assert(is_ok(m));
return Square((m >> 6) & 0x3F);
}
constexpr Square to_sq(Move m) {
assert(is_ok(m));
return Square(m & 0x3F);
}
constexpr int from_to(Move m) {
return m & 0xFFF;
}
constexpr MoveType type_of(Move m) {
return MoveType(m & (3 << 14));
}
constexpr PieceType promotion_type(Move m) {
return PieceType(((m >> 12) & 3) + KNIGHT);
}
constexpr Move make_move(Square from, Square to) {
return Move((from << 6) + to);
}
template<MoveType T>
constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) {
return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);
}
/// Based on a congruential pseudo random number generator
constexpr Key make_key(uint64_t seed) { constexpr Key make_key(uint64_t seed) {
return seed * 6364136223846793005ULL + 1442695040888963407ULL; return seed * 6364136223846793005ULL + 1442695040888963407ULL;
} }
enum MoveType {
NORMAL,
PROMOTION = 1 << 14,
EN_PASSANT = 2 << 14,
CASTLING = 3 << 14
};
// A move needs 16 bits to be stored
//
// bit 0- 5: destination square (from 0 to 63)
// bit 6-11: origin square (from 0 to 63)
// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
// bit 14-15: special move flag: promotion (1), en passant (2), castling (3)
// NOTE: en passant bit is set only when a pawn can be captured
//
// Special cases are Move::none() and Move::null(). We can sneak these in because in
// any normal move destination square is always different from origin square
// while Move::none() and Move::null() have the same origin and destination square.
class Move {
public:
Move() = default;
constexpr explicit Move(std::uint16_t d) :
data(d) {}
constexpr Move(Square from, Square to) :
data((from << 6) + to) {}
template<MoveType T>
static constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) {
return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);
}
constexpr Square from_sq() const {
assert(is_ok());
return Square((data >> 6) & 0x3F);
}
constexpr Square to_sq() const {
assert(is_ok());
return Square(data & 0x3F);
}
constexpr int from_to() const { return data & 0xFFF; }
constexpr MoveType type_of() const { return MoveType(data & (3 << 14)); }
constexpr PieceType promotion_type() const { return PieceType(((data >> 12) & 3) + KNIGHT); }
constexpr bool is_ok() const { return none().data != data && null().data != data; }
static constexpr Move null() { return Move(65); }
static constexpr Move none() { return Move(0); }
constexpr bool operator==(const Move& m) const { return data == m.data; }
constexpr bool operator!=(const Move& m) const { return data != m.data; }
constexpr explicit operator bool() const { return data != 0; }
constexpr std::uint16_t raw() const { return data; }
struct MoveHash {
std::size_t operator()(const Move& m) const { return make_key(m.data); }
};
protected:
std::uint16_t data;
};
} // namespace Stockfish } // namespace Stockfish
#endif // #ifndef TYPES_H_INCLUDED #endif // #ifndef TYPES_H_INCLUDED
+345 -276
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -16,43 +16,314 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "uci.h"
#include <algorithm>
#include <cassert> #include <cassert>
#include <cctype>
#include <cmath> #include <cmath>
#include <iostream> #include <cstdint>
#include <cstdlib>
#include <deque>
#include <memory>
#include <optional>
#include <sstream> #include <sstream>
#include <string> #include <utility>
#include <vector>
#include "benchmark.h" #include "benchmark.h"
#include "cluster.h"
#include "evaluate.h" #include "evaluate.h"
#include "movegen.h" #include "movegen.h"
#include "nnue/network.h"
#include "nnue/nnue_common.h"
#include "perft.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "thread.h"
#include "timeman.h"
#include "tt.h"
#include "uci.h"
#include "syzygy/tbprobe.h" #include "syzygy/tbprobe.h"
#include "nnue/evaluate_nnue.h" #include "types.h"
#include "ucioption.h"
using namespace std;
namespace Stockfish { namespace Stockfish {
namespace { constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048;
// FEN string for the initial position in standard chess
const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
// position() is called when the engine receives the "position" UCI command. namespace NN = Eval::NNUE;
// It sets up the position that is described in the given FEN string ("fen") or
// the initial position ("startpos") and then makes the moves given in the following
// move list ("moves").
void position(Position& pos, istringstream& is, StateListPtr& states) {
UCI::UCI(int argc, char** argv) :
networks(NN::Networks(
NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG),
NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))),
cli(argc, argv) {
options["Debug Log File"] << Option("", [](const Option& o) { start_logger(o); });
options["Threads"] << Option(1, 1, 1024, [this](const Option&) {
threads.set({options, threads, tt, networks});
});
options["Hash"] << Option(16, 1, MaxHashMB, [this](const Option& o) {
threads.main_thread()->wait_for_search_finished();
tt.resize(o, options["Threads"]);
});
options["Clear Hash"] << Option([this](const Option&) { search_clear(); });
options["Ponder"] << Option(false);
options["MultiPV"] << Option(1, 1, MAX_MOVES);
options["Skill Level"] << Option(20, 0, 20);
options["Move Overhead"] << Option(10, 0, 5000);
options["nodestime"] << Option(0, 0, 10000);
options["UCI_Chess960"] << Option(false);
options["UCI_LimitStrength"] << Option(false);
options["UCI_Elo"] << Option(1320, 1320, 3190);
options["UCI_ShowWDL"] << Option(false);
options["SyzygyPath"] << Option("<empty>", [](const Option& o) { Tablebases::init(o); });
options["SyzygyProbeDepth"] << Option(1, 1, 100);
options["Syzygy50MoveRule"] << Option(true);
options["SyzygyProbeLimit"] << Option(7, 0, 7);
options["EvalFile"] << Option(EvalFileDefaultNameBig, [this](const Option& o) {
networks.big.load(cli.binaryDirectory, o);
});
options["EvalFileSmall"] << Option(EvalFileDefaultNameSmall, [this](const Option& o) {
networks.small.load(cli.binaryDirectory, o);
});
networks.big.load(cli.binaryDirectory, options["EvalFile"]);
networks.small.load(cli.binaryDirectory, options["EvalFileSmall"]);
threads.set({options, threads, tt, networks});
search_clear(); // After threads are up
}
void UCI::loop() {
Position pos;
std::string token, cmd;
StateListPtr states(new std::deque<StateInfo>(1));
pos.set(StartFEN, false, &states->back());
for (int i = 1; i < cli.argc; ++i)
cmd += std::string(cli.argv[i]) + " ";
do
{
if (cli.argc == 1
&& !Cluster::getline(std::cin,
cmd)) // Wait for an input or an end-of-file (EOF) indication
cmd = "quit";
std::istringstream is(cmd);
token.clear(); // Avoid a stale if getline() returns nothing or a blank line
is >> std::skipws >> token;
if (token == "quit" || token == "stop")
threads.stop = true;
// The GUI sends 'ponderhit' to tell that the user has played the expected move.
// So, 'ponderhit' is sent if pondering was done on the same move that the user
// has played. The search should continue, but should also switch from pondering
// to the normal search.
else if (token == "ponderhit")
threads.main_manager()->ponder = false; // Switch to the normal search
else if (token == "uci" && Cluster::is_root())
sync_cout << "id name " << engine_info(true) << "\n"
<< options << "\nuciok" << sync_endl;
else if (token == "setoption")
setoption(is);
else if (token == "go")
go(pos, is, states);
else if (token == "position")
position(pos, is, states);
else if (token == "ucinewgame")
search_clear();
else if (token == "isready" && Cluster::is_root())
sync_cout << "readyok" << sync_endl;
// Add custom non-UCI commands, mainly for debugging purposes.
// These commands must not be used during a search!
else if (token == "flip")
pos.flip();
else if (token == "bench")
bench(pos, is, states);
else if (token == "d" && Cluster::is_root())
sync_cout << pos << sync_endl;
else if (token == "eval" && Cluster::is_root())
trace_eval(pos);
else if (token == "compiler" && Cluster::is_root())
sync_cout << compiler_info() << sync_endl;
else if (token == "export_net" && Cluster::is_root())
{
std::pair<std::optional<std::string>, std::string> files[2];
if (is >> std::skipws >> files[0].second)
files[0].first = files[0].second;
if (is >> std::skipws >> files[1].second)
files[1].first = files[1].second;
networks.big.save(files[0].first);
networks.small.save(files[1].first);
}
else if ((token == "--help" || token == "help" || token == "--license"
|| token == "license")
&& Cluster::is_root())
sync_cout
<< "\nStockfish is a powerful chess engine for playing and analyzing."
"\nIt is released as free software licensed under the GNU GPLv3 License."
"\nStockfish is normally used with a graphical user interface (GUI) and implements"
"\nthe Universal Chess Interface (UCI) protocol to communicate with a GUI, an API, etc."
"\nFor any further information, visit https://github.com/official-stockfish/Stockfish#readme"
"\nor read the corresponding README.md and Copying.txt files distributed along with this program.\n"
<< sync_endl;
else if (!token.empty() && token[0] != '#' && Cluster::is_root())
sync_cout << "Unknown command: '" << cmd << "'. Type help for more information."
<< sync_endl;
} while (token != "quit" && cli.argc == 1); // The command-line arguments are one-shot
}
Search::LimitsType UCI::parse_limits(const Position& pos, std::istream& is) {
Search::LimitsType limits;
std::string token;
limits.startTime = now(); // The search starts as early as possible
while (is >> token)
if (token == "searchmoves") // Needs to be the last command on the line
while (is >> token)
limits.searchmoves.push_back(to_move(pos, token));
else if (token == "wtime")
is >> limits.time[WHITE];
else if (token == "btime")
is >> limits.time[BLACK];
else if (token == "winc")
is >> limits.inc[WHITE];
else if (token == "binc")
is >> limits.inc[BLACK];
else if (token == "movestogo")
is >> limits.movestogo;
else if (token == "depth")
is >> limits.depth;
else if (token == "nodes")
is >> limits.nodes;
else if (token == "movetime")
is >> limits.movetime;
else if (token == "mate")
is >> limits.mate;
else if (token == "perft")
is >> limits.perft;
else if (token == "infinite")
limits.infinite = 1;
else if (token == "ponder")
limits.ponderMode = true;
return limits;
}
void UCI::go(Position& pos, std::istringstream& is, StateListPtr& states) {
Search::LimitsType limits = parse_limits(pos, is);
networks.big.verify(options["EvalFile"]);
networks.small.verify(options["EvalFileSmall"]);
if (limits.perft)
{
perft(pos.fen(), limits.perft, options["UCI_Chess960"]);
return;
}
threads.start_thinking(options, pos, states, limits);
}
void UCI::bench(Position& pos, std::istream& args, StateListPtr& states) {
std::string token;
uint64_t num, nodes = 0, cnt = 1;
std::vector<std::string> list = setup_bench(pos, args);
num = count_if(list.begin(), list.end(),
[](const std::string& s) { return s.find("go ") == 0 || s.find("eval") == 0; });
TimePoint elapsed = now();
for (const auto& cmd : list)
{
std::istringstream is(cmd);
is >> std::skipws >> token;
if (token == "go" || token == "eval")
{
if (Cluster::is_root())
std::cerr << "\nPosition: " << cnt++ << '/' << num << " (" << pos.fen() << ")"
<< std::endl;
if (token == "go")
{
go(pos, is, states);
threads.main_thread()->wait_for_search_finished();
nodes += Cluster::nodes_searched(threads);
}
else if (Cluster::is_root())
trace_eval(pos);
}
else if (token == "setoption")
setoption(is);
else if (token == "position")
position(pos, is, states);
else if (token == "ucinewgame")
{
search_clear(); // Search::clear() may take a while
elapsed = now();
}
}
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
dbg_print();
if (Cluster::is_root())
std::cerr << "\n==========================="
<< "\nTotal time (ms) : " << elapsed << "\nNodes searched : " << nodes
<< "\nNodes/second : " << 1000 * nodes / elapsed << std::endl;
}
void UCI::trace_eval(Position& pos) {
StateListPtr states(new std::deque<StateInfo>(1));
Position p;
p.set(pos.fen(), options["UCI_Chess960"], &states->back());
networks.big.verify(options["EvalFile"]);
networks.small.verify(options["EvalFileSmall"]);
sync_cout << "\n" << Eval::trace(p, networks) << sync_endl;
}
void UCI::search_clear() {
threads.main_thread()->wait_for_search_finished();
tt.clear(options["Threads"]);
threads.clear();
Tablebases::init(options["SyzygyPath"]); // Free mapped files
}
void UCI::setoption(std::istringstream& is) {
threads.main_thread()->wait_for_search_finished();
options.setoption(is);
}
void UCI::position(Position& pos, std::istringstream& is, StateListPtr& states) {
Move m; Move m;
string token, fen; std::string token, fen;
is >> token; is >> token;
@@ -68,258 +339,62 @@ namespace {
return; return;
states = StateListPtr(new std::deque<StateInfo>(1)); // Drop the old state and create a new one states = StateListPtr(new std::deque<StateInfo>(1)); // Drop the old state and create a new one
pos.set(fen, Options["UCI_Chess960"], &states->back(), Threads.main()); pos.set(fen, options["UCI_Chess960"], &states->back());
// Parse the move list, if any // Parse the move list, if any
while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) while (is >> token && (m = to_move(pos, token)) != Move::none())
{ {
states->emplace_back(); states->emplace_back();
pos.do_move(m, states->back()); pos.do_move(m, states->back());
} }
} }
// trace_eval() prints the evaluation of the current position, consistent with namespace {
// the UCI options set so far.
void trace_eval(Position& pos) { struct WinRateParams {
double a;
double b;
};
StateListPtr states(new std::deque<StateInfo>(1)); WinRateParams win_rate_params(const Position& pos) {
Position p;
p.set(pos.fen(), Options["UCI_Chess960"], &states->back(), Threads.main());
Eval::NNUE::verify(); int material = pos.count<PAWN>() + 3 * pos.count<KNIGHT>() + 3 * pos.count<BISHOP>()
+ 5 * pos.count<ROOK>() + 9 * pos.count<QUEEN>();
sync_cout << "\n" << Eval::trace(p) << sync_endl; // The fitted model only uses data for material counts in [10, 78], and is anchored at count 58.
} double m = std::clamp(material, 10, 78) / 58.0;
// Return a = p_a(material) and b = p_b(material), see github.com/official-stockfish/WDL_model
// setoption() is called when the engine receives the "setoption" UCI command. constexpr double as[] = {-185.71965483, 504.85014385, -438.58295743, 474.04604627};
// The function updates the UCI option ("name") to the given value ("value"). constexpr double bs[] = {89.23542728, -137.02141296, 73.28669021, 47.53376190};
void setoption(istringstream& is) {
string token, name, value;
is >> token; // Consume the "name" token
// Read the option name (can contain spaces)
while (is >> token && token != "value")
name += (name.empty() ? "" : " ") + token;
// Read the option value (can contain spaces)
while (is >> token)
value += (value.empty() ? "" : " ") + token;
if (Options.count(name))
Options[name] = value;
else
sync_cout << "No such option: " << name << sync_endl;
}
// go() is called when the engine receives the "go" UCI command. The function
// sets the thinking time and other parameters from the input string, then starts
// with a search.
void go(Position& pos, istringstream& is, StateListPtr& states) {
Search::LimitsType limits;
string token;
bool ponderMode = false;
limits.startTime = now(); // The search starts as early as possible
while (is >> token)
if (token == "searchmoves") // Needs to be the last command on the line
while (is >> token)
limits.searchmoves.push_back(UCI::to_move(pos, token));
else if (token == "wtime") is >> limits.time[WHITE];
else if (token == "btime") is >> limits.time[BLACK];
else if (token == "winc") is >> limits.inc[WHITE];
else if (token == "binc") is >> limits.inc[BLACK];
else if (token == "movestogo") is >> limits.movestogo;
else if (token == "depth") is >> limits.depth;
else if (token == "nodes") is >> limits.nodes;
else if (token == "movetime") is >> limits.movetime;
else if (token == "mate") is >> limits.mate;
else if (token == "perft") is >> limits.perft;
else if (token == "infinite") limits.infinite = 1;
else if (token == "ponder") ponderMode = true;
Threads.start_thinking(pos, states, limits, ponderMode);
}
// bench() is called when the engine receives the "bench" command.
// Firstly, a list of UCI commands is set up according to the bench
// parameters, then it is run one by one, printing a summary at the end.
void bench(Position& pos, istream& args, StateListPtr& states) {
string token;
uint64_t num, nodes = 0, cnt = 1;
vector<string> list = setup_bench(pos, args);
num = count_if(list.begin(), list.end(), [](const string& s) { return s.find("go ") == 0 || s.find("eval") == 0; });
TimePoint elapsed = now();
for (const auto& cmd : list)
{
istringstream is(cmd);
is >> skipws >> token;
if (token == "go" || token == "eval")
{
cerr << "\nPosition: " << cnt++ << '/' << num << " (" << pos.fen() << ")" << endl;
if (token == "go")
{
go(pos, is, states);
Threads.main()->wait_for_search_finished();
nodes += Threads.nodes_searched();
}
else
trace_eval(pos);
}
else if (token == "setoption") setoption(is);
else if (token == "position") position(pos, is, states);
else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } // Search::clear() may take a while
}
elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero'
dbg_print();
cerr << "\n==========================="
<< "\nTotal time (ms) : " << elapsed
<< "\nNodes searched : " << nodes
<< "\nNodes/second : " << 1000 * nodes / elapsed << endl;
}
// The win rate model returns the probability of winning (in per mille units) given an
// eval and a game ply. It fits the LTC fishtest statistics rather accurately.
int win_rate_model(Value v, int ply) {
// The model only captures up to 240 plies, so limit the input and then rescale
double m = std::min(240, ply) / 64.0;
// The coefficients of a third-order polynomial fit is based on the fishtest data
// for two parameters that need to transform eval to the argument of a logistic
// function.
constexpr double as[] = { 0.38036525, -2.82015070, 23.17882135, 307.36768407};
constexpr double bs[] = { -2.29434733, 13.27689788, -14.26828904, 63.45318330 };
// Enforce that NormalizeToPawnValue corresponds to a 50% win rate at ply 64
static_assert(UCI::NormalizeToPawnValue == int(as[0] + as[1] + as[2] + as[3]));
double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3];
double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3];
// Transform the eval to centipawns with limited range return {a, b};
double x = std::clamp(double(v), -4000.0, 4000.0);
// Return the win rate in per mille units rounded to the nearest value
return int(0.5 + 1000 / (1 + std::exp((a - x) / b)));
} }
} // namespace // The win rate model is 1 / (1 + exp((a - eval) / b)), where a = p_a(material) and b = p_b(material).
// It fits the LTC fishtest statistics rather accurately.
int win_rate_model(Value v, const Position& pos) {
auto [a, b] = win_rate_params(pos);
/// UCI::loop() waits for a command from the stdin, parses it and then calls the appropriate // Return the win rate in per mille units, rounded to the nearest integer.
/// function. It also intercepts an end-of-file (EOF) indication from the stdin to ensure a return int(0.5 + 1000 / (1 + std::exp((a - double(v)) / b)));
/// graceful exit if the GUI dies unexpectedly. When called with some command-line arguments,
/// like running 'bench', the function returns immediately after the command is executed.
/// In addition to the UCI ones, some additional debug commands are also supported.
void UCI::loop(int argc, char* argv[]) {
Position pos;
string token, cmd;
StateListPtr states(new std::deque<StateInfo>(1));
pos.set(StartFEN, false, &states->back(), Threads.main());
for (int i = 1; i < argc; ++i)
cmd += std::string(argv[i]) + " ";
do {
if (argc == 1 && !getline(cin, cmd)) // Wait for an input or an end-of-file (EOF) indication
cmd = "quit";
istringstream is(cmd);
token.clear(); // Avoid a stale if getline() returns nothing or a blank line
is >> skipws >> token;
if ( token == "quit"
|| token == "stop")
Threads.stop = true;
// The GUI sends 'ponderhit' to tell that the user has played the expected move.
// So, 'ponderhit' is sent if pondering was done on the same move that the user
// has played. The search should continue, but should also switch from pondering
// to the normal search.
else if (token == "ponderhit")
Threads.main()->ponder = false; // Switch to the normal search
else if (token == "uci")
sync_cout << "id name " << engine_info(true)
<< "\n" << Options
<< "\nuciok" << sync_endl;
else if (token == "setoption") setoption(is);
else if (token == "go") go(pos, is, states);
else if (token == "position") position(pos, is, states);
else if (token == "ucinewgame") Search::clear();
else if (token == "isready") sync_cout << "readyok" << sync_endl;
// Add custom non-UCI commands, mainly for debugging purposes.
// These commands must not be used during a search!
else if (token == "flip") pos.flip();
else if (token == "bench") bench(pos, is, states);
else if (token == "d") sync_cout << pos << sync_endl;
else if (token == "eval") trace_eval(pos);
else if (token == "compiler") sync_cout << compiler_info() << sync_endl;
else if (token == "export_net")
{
std::optional<std::string> filename;
std::string f;
if (is >> skipws >> f)
filename = f;
Eval::NNUE::save_eval(filename);
} }
else if (token == "--help" || token == "help" || token == "--license" || token == "license")
sync_cout << "\nStockfish is a powerful chess engine for playing and analyzing."
"\nIt is released as free software licensed under the GNU GPLv3 License."
"\nStockfish is normally used with a graphical user interface (GUI) and implements"
"\nthe Universal Chess Interface (UCI) protocol to communicate with a GUI, an API, etc."
"\nFor any further information, visit https://github.com/official-stockfish/Stockfish#readme"
"\nor read the corresponding README.md and Copying.txt files distributed along with this program.\n" << sync_endl;
else if (!token.empty() && token[0] != '#')
sync_cout << "Unknown command: '" << cmd << "'. Type help for more information." << sync_endl;
} while (token != "quit" && argc == 1); // The command-line arguments are one-shot
} }
std::string UCI::to_score(Value v, const Position& pos) {
/// UCI::value() converts a Value to a string by adhering to the UCI protocol specification:
///
/// cp <x> The score from the engine's point of view in centipawns.
/// mate <y> Mate in 'y' moves (not plies). If the engine is getting mated,
/// uses negative values for 'y'.
string UCI::value(Value v) {
assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); assert(-VALUE_INFINITE < v && v < VALUE_INFINITE);
stringstream ss; std::stringstream ss;
if (abs(v) < VALUE_TB_WIN_IN_MAX_PLY) if (std::abs(v) < VALUE_TB_WIN_IN_MAX_PLY)
ss << "cp " << v * 100 / NormalizeToPawnValue; ss << "cp " << to_cp(v, pos);
else if (abs(v) < VALUE_MATE_IN_MAX_PLY) else if (std::abs(v) <= VALUE_TB)
{ {
const int ply = VALUE_MATE_IN_MAX_PLY - 1 - std::abs(v); // recompute ss->ply const int ply = VALUE_TB - std::abs(v); // recompute ss->ply
ss << "cp " << (v > 0 ? 20000 - ply : -20000 + ply); ss << "cp " << (v > 0 ? 20000 - ply : -20000 + ply);
} }
else else
@@ -328,71 +403,65 @@ string UCI::value(Value v) {
return ss.str(); return ss.str();
} }
// Turns a Value to an integer centipawn number,
// without treatment of mate and similar special scores.
int UCI::to_cp(Value v, const Position& pos) {
/// UCI::wdl() reports the win-draw-loss (WDL) statistics given an evaluation // In general, the score can be defined via the the WDL as
/// and a game ply based on the data gathered for fishtest LTC games. // (log(1/L - 1) - log(1/W - 1)) / ((log(1/L - 1) + log(1/W - 1))
// Based on our win_rate_model, this simply yields v / a.
string UCI::wdl(Value v, int ply) { auto [a, b] = win_rate_params(pos);
stringstream ss; return std::round(100 * int(v) / a);
}
int wdl_w = win_rate_model( v, ply); std::string UCI::wdl(Value v, const Position& pos) {
int wdl_l = win_rate_model(-v, ply); std::stringstream ss;
int wdl_w = win_rate_model(v, pos);
int wdl_l = win_rate_model(-v, pos);
int wdl_d = 1000 - wdl_w - wdl_l; int wdl_d = 1000 - wdl_w - wdl_l;
ss << " wdl " << wdl_w << " " << wdl_d << " " << wdl_l; ss << " wdl " << wdl_w << " " << wdl_d << " " << wdl_l;
return ss.str(); return ss.str();
} }
/// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.)
std::string UCI::square(Square s) { std::string UCI::square(Square s) {
return std::string{char('a' + file_of(s)), char('1' + rank_of(s))}; return std::string{char('a' + file_of(s)), char('1' + rank_of(s))};
} }
std::string UCI::move(Move m, bool chess960) {
/// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q). if (m == Move::none())
/// The only special case is castling where the e1g1 notation is printed in
/// standard chess mode and in e1h1 notation it is printed in Chess960 mode.
/// Internally, all castling moves are always encoded as 'king captures rook'.
string UCI::move(Move m, bool chess960) {
if (m == MOVE_NONE)
return "(none)"; return "(none)";
if (m == MOVE_NULL) if (m == Move::null())
return "0000"; return "0000";
Square from = from_sq(m); Square from = m.from_sq();
Square to = to_sq(m); Square to = m.to_sq();
if (type_of(m) == CASTLING && !chess960) if (m.type_of() == CASTLING && !chess960)
to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); to = make_square(to > from ? FILE_G : FILE_C, rank_of(from));
string move = UCI::square(from) + UCI::square(to); std::string move = square(from) + square(to);
if (type_of(m) == PROMOTION) if (m.type_of() == PROMOTION)
move += " pnbrqk"[promotion_type(m)]; move += " pnbrqk"[m.promotion_type()];
return move; return move;
} }
/// UCI::to_move() converts a string representing a move in coordinate notation Move UCI::to_move(const Position& pos, std::string& str) {
/// (g1f3, a7a8q) to the corresponding legal Move, if any.
Move UCI::to_move(const Position& pos, string& str) {
if (str.length() == 5) if (str.length() == 5)
str[4] = char(tolower(str[4])); // The promotion piece character must be lowercased str[4] = char(tolower(str[4])); // The promotion piece character must be lowercased
for (const auto& m : MoveList<LEGAL>(pos)) for (const auto& m : MoveList<LEGAL>(pos))
if (str == UCI::move(m, pos.is_chess960())) if (str == move(m, pos.is_chess960()))
return m; return m;
return MOVE_NONE; return Move::none();
} }
} // namespace Stockfish } // namespace Stockfish
+38 -56
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,74 +19,56 @@
#ifndef UCI_H_INCLUDED #ifndef UCI_H_INCLUDED
#define UCI_H_INCLUDED #define UCI_H_INCLUDED
#include <map> #include <iostream>
#include <string> #include <string>
#include "types.h" #include "misc.h"
#include "nnue/network.h"
#include "position.h"
#include "search.h"
#include "thread.h"
#include "tt.h"
#include "ucioption.h"
namespace Stockfish { namespace Stockfish {
class Position; class Move;
enum Square : int;
namespace UCI { using Value = int;
// Normalizes the internal value as reported by evaluate or search
// to the UCI centipawn result used in output. This value is derived from
// the win_rate_model() such that Stockfish outputs an advantage of
// "100 centipawns" for a position if the engine has a 50% probability to win
// from this position in selfplay at fishtest LTC time control.
const int NormalizeToPawnValue = 328;
class Option;
/// Define a custom comparator, because the UCI options should be case-insensitive
struct CaseInsensitiveLess {
bool operator() (const std::string&, const std::string&) const;
};
/// The options container is defined as a std::map
using OptionsMap = std::map<std::string, Option, CaseInsensitiveLess>;
/// The Option class implements each option as specified by the UCI protocol
class Option {
using OnChange = void (*)(const Option&);
class UCI {
public: public:
Option(OnChange = nullptr); UCI(int argc, char** argv);
Option(bool v, OnChange = nullptr);
Option(const char* v, OnChange = nullptr);
Option(double v, int minv, int maxv, OnChange = nullptr);
Option(const char* v, const char* cur, OnChange = nullptr);
Option& operator=(const std::string&); void loop();
void operator<<(const Option&);
operator int() const; static int to_cp(Value v, const Position& pos);
operator std::string() const; static std::string to_score(Value v, const Position& pos);
bool operator==(const char*) const; static std::string square(Square s);
static std::string move(Move m, bool chess960);
static std::string wdl(Value v, const Position& pos);
static Move to_move(const Position& pos, std::string& str);
static Search::LimitsType parse_limits(const Position& pos, std::istream& is);
const std::string& working_directory() const { return cli.workingDirectory; }
OptionsMap options;
Eval::NNUE::Networks networks;
private: private:
friend std::ostream& operator<<(std::ostream&, const OptionsMap&); TranspositionTable tt;
ThreadPool threads;
CommandLine cli;
std::string defaultValue, currentValue, type; void go(Position& pos, std::istringstream& is, StateListPtr& states);
int min, max; void bench(Position& pos, std::istream& args, StateListPtr& states);
size_t idx; void position(Position& pos, std::istringstream& is, StateListPtr& states);
OnChange on_change; void trace_eval(Position& pos);
void search_clear();
void setoption(std::istringstream& is);
}; };
void init(OptionsMap&);
void loop(int argc, char* argv[]);
std::string value(Value v);
std::string square(Square s);
std::string move(Move m, bool chess960);
std::string pv(const Position& pos, Depth depth);
std::string wdl(Value v, int ply);
Move to_move(const Position& pos, std::string& str);
} // namespace UCI
extern UCI::OptionsMap Options;
} // namespace Stockfish } // namespace Stockfish
#endif // #ifndef UCI_H_INCLUDED #endif // #ifndef UCI_H_INCLUDED
+92 -100
View File
@@ -1,6 +1,6 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -16,117 +16,93 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "ucioption.h"
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <ostream> #include <cctype>
#include <iostream>
#include <sstream> #include <sstream>
#include <utility>
#include "evaluate.h" #include "cluster.h"
#include "misc.h" #include "misc.h"
#include "search.h"
#include "thread.h"
#include "tt.h"
#include "uci.h"
#include "syzygy/tbprobe.h"
using std::string;
namespace Stockfish { namespace Stockfish {
UCI::OptionsMap Options; // Global object bool CaseInsensitiveLess::operator()(const std::string& s1, const std::string& s2) const {
namespace UCI { return std::lexicographical_compare(
s1.begin(), s1.end(), s2.begin(), s2.end(),
/// 'On change' actions, triggered by an option's value change [](char c1, char c2) { return std::tolower(c1) < std::tolower(c2); });
static void on_clear_hash(const Option&) { Search::clear(); }
static void on_hash_size(const Option& o) { TT.resize(size_t(o)); }
static void on_logger(const Option& o) { start_logger(o); }
static void on_threads(const Option& o) { Threads.set(size_t(o)); }
static void on_tb_path(const Option& o) { Tablebases::init(o); }
static void on_use_NNUE(const Option&) { Eval::NNUE::init(); }
static void on_eval_file(const Option&) { Eval::NNUE::init(); }
/// Our case insensitive less() function as required by UCI protocol
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(),
[](char c1, char c2) { return tolower(c1) < tolower(c2); });
} }
void OptionsMap::setoption(std::istringstream& is) {
std::string token, name, value;
/// UCI::init() initializes the UCI options to their hard-coded default values is >> token; // Consume the "name" token
void init(OptionsMap& o) { // Read the option name (can contain spaces)
while (is >> token && token != "value")
name += (name.empty() ? "" : " ") + token;
constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; // Read the option value (can contain spaces)
while (is >> token)
value += (value.empty() ? "" : " ") + token;
o["Debug Log File"] << Option("", on_logger); if (options_map.count(name))
o["Threads"] << Option(1, 1, 1024, on_threads); options_map[name] = value;
o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); else if (Cluster::is_root())
o["Clear Hash"] << Option(on_clear_hash); sync_cout << "No such option: " << name << sync_endl;
o["Ponder"] << Option(false);
o["MultiPV"] << Option(1, 1, 500);
o["Skill Level"] << Option(20, 0, 20);
o["Move Overhead"] << Option(10, 0, 5000);
o["Slow Mover"] << Option(100, 10, 1000);
o["nodestime"] << Option(0, 0, 10000);
o["UCI_Chess960"] << Option(false);
o["UCI_AnalyseMode"] << Option(false);
o["UCI_LimitStrength"] << Option(false);
o["UCI_Elo"] << Option(1320, 1320, 3190);
o["UCI_ShowWDL"] << Option(false);
o["SyzygyPath"] << Option("<empty>", on_tb_path);
o["SyzygyProbeDepth"] << Option(1, 1, 100);
o["Syzygy50MoveRule"] << Option(true);
o["SyzygyProbeLimit"] << Option(7, 0, 7);
o["Use NNUE"] << Option(true, on_use_NNUE);
o["EvalFile"] << Option(EvalFileDefaultName, on_eval_file);
} }
Option OptionsMap::operator[](const std::string& name) const {
/// operator<<() is used to print all the options default values in chronological auto it = options_map.find(name);
/// insertion order (the idx field) and in the format defined by the UCI protocol. return it != options_map.end() ? it->second : Option();
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
for (size_t idx = 0; idx < om.size(); ++idx)
for (const auto& it : om)
if (it.second.idx == idx)
{
const Option& o = it.second;
os << "\noption name " << it.first << " type " << o.type;
if (o.type == "string" || o.type == "check" || o.type == "combo")
os << " default " << o.defaultValue;
if (o.type == "spin")
os << " default " << int(stof(o.defaultValue))
<< " min " << o.min
<< " max " << o.max;
break;
} }
return os; Option& OptionsMap::operator[](const std::string& name) { return options_map[name]; }
std::size_t OptionsMap::count(const std::string& name) const { return options_map.count(name); }
Option::Option(const char* v, OnChange f) :
type("string"),
min(0),
max(0),
on_change(std::move(f)) {
defaultValue = currentValue = v;
} }
Option::Option(bool v, OnChange f) :
type("check"),
min(0),
max(0),
on_change(std::move(f)) {
defaultValue = currentValue = (v ? "true" : "false");
}
/// Option class constructors and conversion operators Option::Option(OnChange f) :
type("button"),
min(0),
max(0),
on_change(std::move(f)) {}
Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f) Option::Option(double v, int minv, int maxv, OnChange f) :
{ defaultValue = currentValue = v; } type("spin"),
min(minv),
max(maxv),
on_change(std::move(f)) {
defaultValue = currentValue = std::to_string(v);
}
Option::Option(bool v, OnChange f) : type("check"), min(0), max(0), on_change(f) Option::Option(const char* v, const char* cur, OnChange f) :
{ defaultValue = currentValue = (v ? "true" : "false"); } type("combo"),
min(0),
Option::Option(OnChange f) : type("button"), min(0), max(0), on_change(f) max(0),
{} on_change(std::move(f)) {
defaultValue = v;
Option::Option(double v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), on_change(f) currentValue = cur;
{ defaultValue = currentValue = std::to_string(v); } }
Option::Option(const char* v, const char* cur, OnChange f) : type("combo"), min(0), max(0), on_change(f)
{ defaultValue = v; currentValue = cur; }
Option::operator int() const { Option::operator int() const {
assert(type == "check" || type == "spin"); assert(type == "check" || type == "spin");
@@ -140,12 +116,11 @@ Option::operator std::string() const {
bool Option::operator==(const char* s) const { bool Option::operator==(const char* s) const {
assert(type == "combo"); assert(type == "combo");
return !CaseInsensitiveLess()(currentValue, s) return !CaseInsensitiveLess()(currentValue, s) && !CaseInsensitiveLess()(s, currentValue);
&& !CaseInsensitiveLess()(s, currentValue);
} }
/// operator<<() inits options and assigns idx in the correct printing order // Inits options and assigns idx in the correct printing order
void Option::operator<<(const Option& o) { void Option::operator<<(const Option& o) {
@@ -156,23 +131,22 @@ void Option::operator<<(const Option& o) {
} }
/// operator=() updates currentValue and triggers on_change() action. It's up to // Updates currentValue and triggers on_change() action. It's up to
/// the GUI to check for option's limits, but we could receive the new value // the GUI to check for option's limits, but we could receive the new value
/// from the user by console window, so let's check the bounds anyway. // from the user by console window, so let's check the bounds anyway.
Option& Option::operator=(const std::string& v) {
Option& Option::operator=(const string& v) {
assert(!type.empty()); assert(!type.empty());
if ((type != "button" && type != "string" && v.empty()) if ((type != "button" && type != "string" && v.empty())
|| (type == "check" && v != "true" && v != "false") || (type == "check" && v != "true" && v != "false")
|| (type == "spin" && (stof(v) < min || stof(v) > max))) || (type == "spin" && (std::stof(v) < min || std::stof(v) > max)))
return *this; return *this;
if (type == "combo") if (type == "combo")
{ {
OptionsMap comboMap; // To have case insensitive compare OptionsMap comboMap; // To have case insensitive compare
string token; std::string token;
std::istringstream ss(defaultValue); std::istringstream ss(defaultValue);
while (ss >> token) while (ss >> token)
comboMap[token] << Option(); comboMap[token] << Option();
@@ -189,6 +163,24 @@ Option& Option::operator=(const string& v) {
return *this; return *this;
} }
} // namespace UCI std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
for (size_t idx = 0; idx < om.options_map.size(); ++idx)
for (const auto& it : om.options_map)
if (it.second.idx == idx)
{
const Option& o = it.second;
os << "\noption name " << it.first << " type " << o.type;
} // namespace Stockfish if (o.type == "string" || o.type == "check" || o.type == "combo")
os << " default " << o.defaultValue;
if (o.type == "spin")
os << " default " << int(stof(o.defaultValue)) << " min " << o.min << " max "
<< o.max;
break;
}
return os;
}
}
+81
View File
@@ -0,0 +1,81 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UCIOPTION_H_INCLUDED
#define UCIOPTION_H_INCLUDED
#include <cstddef>
#include <functional>
#include <iosfwd>
#include <map>
#include <string>
namespace Stockfish {
// Define a custom comparator, because the UCI options should be case-insensitive
struct CaseInsensitiveLess {
bool operator()(const std::string&, const std::string&) const;
};
class Option;
class OptionsMap {
public:
void setoption(std::istringstream&);
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
Option operator[](const std::string&) const;
Option& operator[](const std::string&);
std::size_t count(const std::string&) const;
private:
// The options container is defined as a std::map
using OptionsStore = std::map<std::string, Option, CaseInsensitiveLess>;
OptionsStore options_map;
};
// The Option class implements each option as specified by the UCI protocol
class Option {
public:
using OnChange = std::function<void(const Option&)>;
Option(OnChange = nullptr);
Option(bool v, OnChange = nullptr);
Option(const char* v, OnChange = nullptr);
Option(double v, int minv, int maxv, OnChange = nullptr);
Option(const char* v, const char* cur, OnChange = nullptr);
Option& operator=(const std::string&);
void operator<<(const Option&);
operator int() const;
operator std::string() const;
bool operator==(const char*) const;
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
private:
std::string defaultValue, currentValue, type;
int min, max;
size_t idx;
OnChange on_change;
};
}
#endif // #ifndef UCIOPTION_H_INCLUDED
+60 -5
View File
@@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# check for errors under valgrind or sanitizers. # check for errors under Valgrind or sanitizers.
error() error()
{ {
@@ -8,6 +8,13 @@ error()
} }
trap 'error ${LINENO}' ERR trap 'error ${LINENO}' ERR
# Since Linux Kernel 6.5 we are getting false positives from the ci,
# lower the ALSR entropy to disable ALSR, which works as a temporary workaround.
# https://github.com/google/sanitizers/issues/1716
# https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2056762
sudo sysctl -w vm.mmap_rnd_bits=28
# define suitable post and prefixes for testing options # define suitable post and prefixes for testing options
case $1 in case $1 in
--valgrind) --valgrind)
@@ -64,14 +71,32 @@ EOF
;; ;;
esac esac
cat << EOF > bench_tmp.epd
Rn6/1rbq1bk1/2p2n1p/2Bp1p2/3Pp1pP/1N2P1P1/2Q1NPB1/6K1 w - - 2 26
rnbqkb1r/ppp1pp2/5n1p/3p2p1/P2PP3/5P2/1PP3PP/RNBQKBNR w KQkq - 0 3
3qnrk1/4bp1p/1p2p1pP/p2bN3/1P1P1B2/P2BQ3/5PP1/4R1K1 w - - 9 28
r4rk1/1b2ppbp/pq4pn/2pp1PB1/1p2P3/1P1P1NN1/1PP3PP/R2Q1RK1 w - - 0 13
EOF
# simple command line testing # simple command line testing
for args in "eval" \ for args in "eval" \
"go nodes 1000" \ "go nodes 1000" \
"go depth 10" \ "go depth 10" \
"go perft 4" \
"go movetime 1000" \ "go movetime 1000" \
"go wtime 8000 btime 8000 winc 500 binc 500" \ "go wtime 8000 btime 8000 winc 500 binc 500" \
"go wtime 1000 btime 1000 winc 0 binc 0" \
"go wtime 1000 btime 1000 winc 0 binc 0" \
"go wtime 1000 btime 1000 winc 0 binc 0 movestogo 5" \
"go movetime 200" \
"go nodes 20000 searchmoves e2e4 d2d4" \
"bench 128 $threads 8 default depth" \ "bench 128 $threads 8 default depth" \
"export_net verify.nnue" "bench 128 $threads 3 bench_tmp.epd depth" \
"export_net verify.nnue" \
"d" \
"compiler" \
"license" \
"uci"
do do
echo "$prefix $exeprefix ./stockfish $args $postfix" echo "$prefix $exeprefix ./stockfish $args $postfix"
@@ -92,6 +117,7 @@ cat << EOF > game.exp
send "uci\n" send "uci\n"
expect "uciok" expect "uciok"
# send "setoption name Debug Log File value debug.log\n"
send "setoption name Threads value $threads\n" send "setoption name Threads value $threads\n"
send "ucinewgame\n" send "ucinewgame\n"
@@ -107,10 +133,32 @@ cat << EOF > game.exp
send "go depth 10\n" send "go depth 10\n"
expect "bestmove" expect "bestmove"
send "setoption name UCI_ShowWDL value true\n"
send "position startpos\n"
send "flip\n"
send "go depth 5\n"
expect "bestmove"
send "setoption name Skill Level value 10\n"
send "position startpos\n"
send "go depth 5\n"
expect "bestmove"
send "setoption name Clear Hash\n"
send "setoption name EvalFile value verify.nnue\n"
send "position startpos\n"
send "go depth 5\n"
expect "bestmove"
send "setoption name MultiPV value 4\n"
send "position startpos\n"
send "go depth 5\n"
send "quit\n" send "quit\n"
expect eof expect eof
# return error code of the spawned program, useful for valgrind # return error code of the spawned program, useful for Valgrind
lassign [wait] pid spawnid os_error_flag value lassign [wait] pid spawnid os_error_flag value
exit \$value exit \$value
EOF EOF
@@ -128,10 +176,17 @@ cat << EOF > syzygy.exp
send "setoption name SyzygyPath value ../tests/syzygy/\n" send "setoption name SyzygyPath value ../tests/syzygy/\n"
expect "info string Found 35 tablebases" {} timeout {exit 1} expect "info string Found 35 tablebases" {} timeout {exit 1}
send "bench 128 1 8 default depth\n" send "bench 128 1 8 default depth\n"
send "ucinewgame\n"
send "position fen 4k3/PP6/8/8/8/8/8/4K3 w - - 0 1\n"
send "go depth 5\n"
expect "bestmove"
send "position fen 8/1P6/2B5/8/4K3/8/6k1/8 w - - 0 1\n"
send "go depth 5\n"
expect "bestmove"
send "quit\n" send "quit\n"
expect eof expect eof
# return error code of the spawned program, useful for valgrind # return error code of the spawned program, useful for Valgrind
lassign [wait] pid spawnid os_error_flag value lassign [wait] pid spawnid os_error_flag value
exit \$value exit \$value
EOF EOF
@@ -146,6 +201,6 @@ do
done done
rm -f tsan.supp rm -f tsan.supp bench_tmp.epd
echo "instrumented testing OK" echo "instrumented testing OK"
+1 -1
View File
@@ -11,7 +11,7 @@ trap 'error ${LINENO}' ERR
# obtain # obtain
signature=`./stockfish bench 2>&1 | grep "Nodes searched : " | awk '{print $4}'` signature=`eval "$WINE_PATH ./stockfish bench 2>&1" | grep "Nodes searched : " | awk '{print $4}'`
if [ $# -gt 0 ]; then if [ $# -gt 0 ]; then
# compare to given reference # compare to given reference