mirror of
https://github.com/opelly27/Stockfish.git
synced 2026-05-20 06:17:49 +00:00
Compare commits
188 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa6c30af81 | |||
| d54240c50a | |||
| a4edacb87a | |||
| 9cc15b3049 | |||
| 7258567804 | |||
| 3a0418c0d0 | |||
| e089f723d8 | |||
| d66e603070 | |||
| 3dfbc5de25 | |||
| 4c6d2bf921 | |||
| 2a5b41fd12 | |||
| e852d9880a | |||
| 67573218e1 | |||
| ec7f1d6229 | |||
| 3b8bfeb38a | |||
| 09623abbe8 | |||
| 9ed1725e78 | |||
| fccc6f624e | |||
| c12dbdedd9 | |||
| 8c73472ac8 | |||
| 2a1ab11ab0 | |||
| d46c0b6f49 | |||
| 9f0844c101 | |||
| 65a9a391e9 | |||
| dabffbceff | |||
| 344e89275a | |||
| c83ddd9e4b | |||
| 7690fac5cf | |||
| 40e0486d02 | |||
| 5ef1f2b132 | |||
| 7684b6e4d8 | |||
| 4a77fb213f | |||
| f50d52aa7f | |||
| c180163540 | |||
| ebdc7ba2da | |||
| a016abd698 | |||
| 831cb01cea | |||
| 69be04d38e | |||
| 27e747d1d7 | |||
| 1b31e266b0 | |||
| 889fed448c | |||
| 435ba3dbb5 | |||
| 6c7c5c7e47 | |||
| 75b75bc16a | |||
| d606311e55 | |||
| aa894c0f93 | |||
| 4975b2bc6f | |||
| 59c578ad28 | |||
| e7367cef0f | |||
| 18b3465a86 | |||
| 62ecdfe82c | |||
| 8e3e22b3d4 | |||
| f00d91f8ac | |||
| 738ac2a100 | |||
| c94bcf62e4 | |||
| 329c267e25 | |||
| 7701d0b3c4 | |||
| b392ac76db | |||
| 165ace194f | |||
| 4423c8eefa | |||
| ccbd060b01 | |||
| 132b90df04 | |||
| a944f08225 | |||
| 69ec5dcbfc | |||
| 56000827af | |||
| 4c2241089d | |||
| 675319b45d | |||
| 5868b4cb58 | |||
| 3104cd72d5 | |||
| aaafaaecf2 | |||
| c085670b84 | |||
| e2612f9a29 | |||
| 93edf7a74c | |||
| 8b32e4825f | |||
| b84c8807a3 | |||
| 921361829a | |||
| 8d517bddff | |||
| 28c07fb456 | |||
| ea71a08843 | |||
| d49fd9090b | |||
| c88a5b3950 | |||
| 403a5e100b | |||
| 5370c3035e | |||
| c47e6fcf84 | |||
| d1a1ff4f17 | |||
| 7858f9dfdc | |||
| c76c179361 | |||
| 1611b9c940 | |||
| 5488dd2f91 | |||
| 00da3ff463 | |||
| 5cf6f99177 | |||
| 78b5733939 | |||
| 03e4cde729 | |||
| f656fdfa9a | |||
| 79261bec59 | |||
| 4bc2a24245 | |||
| e7e78aa09e | |||
| a04b07265f | |||
| 6075e787d0 | |||
| 2dc47e4345 | |||
| 77ec878ffa | |||
| ba145332c9 | |||
| e770b55f7f | |||
| 1776448917 | |||
| f414d490bc | |||
| b822fdf2f2 | |||
| cf10644d6e | |||
| a8b6bf1b1a | |||
| afaf3a0f2a | |||
| e8d2ba194a | |||
| 6a8478c6ad | |||
| 1f9404434d | |||
| 713000c517 | |||
| d5a36a3c92 | |||
| da82942b54 | |||
| 57e06be71f | |||
| 70bb317afe | |||
| 55905e562a | |||
| b7f17346e5 | |||
| fb6be17ad4 | |||
| 4fcd78ceb4 | |||
| cd3c13a883 | |||
| d29c8bd5d4 | |||
| 0282edc0b0 | |||
| f129bf0de9 | |||
| 82b092ca48 | |||
| 49138b8c33 | |||
| ce2d9e27ea | |||
| 070db8b3a1 | |||
| 43e100ae06 | |||
| 3d084e9164 | |||
| cc5c67c564 | |||
| f77bac3dca | |||
| 54cf226604 | |||
| ecf5646f6e | |||
| c2611efe5c | |||
| 16fee2a7da | |||
| 24c57793e1 | |||
| 8681d3c2b3 | |||
| 4a9c980f3b | |||
| 8ef403c786 | |||
| c15113554f | |||
| 2ce47573b4 | |||
| b325b2c348 | |||
| 7f386d109e | |||
| bf2a0d5392 | |||
| 2f3e6198e8 | |||
| 656b2cb645 | |||
| 9766db8139 | |||
| b261df970d | |||
| aaadbe0572 | |||
| d4358ddba7 | |||
| 76923bb6fe | |||
| 9a21e3e996 | |||
| 3348603770 | |||
| dce72913fe | |||
| e046c4ef0d | |||
| 6592b13d56 | |||
| 81c1d31084 | |||
| 7ac745a736 | |||
| 0186904f53 | |||
| 2b9154882a | |||
| c85f802185 | |||
| d6043970bd | |||
| 56444ce1f7 | |||
| 3ac75cd27d | |||
| aff1f67997 | |||
| ae420e735f | |||
| 5d0bb5976e | |||
| 93869d5d0a | |||
| 60351b9df9 | |||
| 240a5b1c72 | |||
| 5ce7f866a5 | |||
| 224c147bd6 | |||
| a06e7004c1 | |||
| f677aee28b | |||
| d8e49cdbdd | |||
| 6de2587236 | |||
| 2680c9c799 | |||
| effa246071 | |||
| a8cb002038 | |||
| d7e3a708d4 | |||
| 1b310cc87e | |||
| 66a7965b0f | |||
| e74452ae44 | |||
| ddc9f48bc3 | |||
| 4fb04eb3df | |||
| f4ba7ce67a |
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"config": [
|
||||
{
|
||||
"name": "Ubuntu 20.04 GCC",
|
||||
"os": "ubuntu-20.04",
|
||||
"name": "Ubuntu 22.04 GCC",
|
||||
"os": "ubuntu-22.04",
|
||||
"simple_name": "ubuntu",
|
||||
"compiler": "g++",
|
||||
"comp": "gcc",
|
||||
@@ -111,7 +111,7 @@
|
||||
{
|
||||
"binaries": "x86-64-avxvnni",
|
||||
"config": {
|
||||
"ubuntu-20.04": null
|
||||
"ubuntu-22.04": null
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -153,7 +153,7 @@
|
||||
{
|
||||
"binaries": "apple-silicon",
|
||||
"config": {
|
||||
"os": "ubuntu-20.04"
|
||||
"os": "ubuntu-22.04"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -18,7 +18,7 @@ permissions:
|
||||
jobs:
|
||||
Clang-Format:
|
||||
name: Clang-Format
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
@@ -19,22 +19,22 @@ jobs:
|
||||
working-directory: Stockfish/src
|
||||
run: make -j build debug=yes
|
||||
|
||||
- name: Checkout fast-chess repo
|
||||
- name: Checkout fastchess repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: Disservin/fast-chess
|
||||
path: fast-chess
|
||||
ref: d54af1910d5479c669dc731f1f54f9108a251951
|
||||
repository: Disservin/fastchess
|
||||
path: fastchess
|
||||
ref: 894616028492ae6114835195f14a899f6fa237d3
|
||||
persist-credentials: false
|
||||
|
||||
- name: fast-chess build
|
||||
working-directory: fast-chess
|
||||
- name: fastchess build
|
||||
working-directory: fastchess
|
||||
run: make -j
|
||||
|
||||
- name: Run games
|
||||
working-directory: fast-chess
|
||||
working-directory: fastchess
|
||||
run: |
|
||||
./fast-chess -rounds 4 -games 2 -repeat -concurrency 4 -openings file=app/tests/data/openings.epd format=epd order=random -srand $RANDOM\
|
||||
./fastchess -rounds 4 -games 2 -repeat -concurrency 4 -openings file=app/tests/data/openings.epd format=epd order=random -srand $RANDOM\
|
||||
-engine name=sf1 cmd=/home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish\
|
||||
-engine name=sf2 cmd=/home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish\
|
||||
-ratinginterval 1 -report penta=true -each proto=uci tc=4+0.04 -log file=fast.log | tee fast.out
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
with:
|
||||
repository: vondele/matetrack
|
||||
path: matetrack
|
||||
ref: 814160f82e6428ed2f6522dc06c2a6fa539cd413
|
||||
ref: 4f8a80860ed8f3607f05a9195df8b40203bdc360
|
||||
persist-credentials: false
|
||||
|
||||
- name: matetrack install deps
|
||||
@@ -50,5 +50,22 @@ jobs:
|
||||
- name: Run matetrack
|
||||
working-directory: matetrack
|
||||
run: |
|
||||
python matecheck.py --syzygyPath 3-4-5-wdl/:3-4-5-dtz/ --engine /home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish --epdFile mates2000.epd --nodes 100000 | tee matecheckout.out
|
||||
python matecheck.py --syzygyPath 3-4-5-wdl/:3-4-5-dtz/ --engine /home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish --epdFile mates2000.epd --nodes 100000 | tee matecheckout.out
|
||||
! grep "issues were detected" matecheckout.out > /dev/null
|
||||
|
||||
- name: Run matetrack with --syzygy50MoveRule false
|
||||
working-directory: matetrack
|
||||
run: |
|
||||
grep 5men cursed.epd > cursed5.epd
|
||||
python matecheck.py --syzygyPath 3-4-5-wdl/:3-4-5-dtz/ --engine /home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish --epdFile cursed5.epd --nodes 100000 --syzygy50MoveRule false | tee matecheckcursed.out
|
||||
! grep "issues were detected" matecheckcursed.out > /dev/null
|
||||
|
||||
- name: Verify mate and TB win count for matecheckcursed.out
|
||||
working-directory: matetrack
|
||||
run: |
|
||||
mates=$(grep "Found mates:" matecheckcursed.out | awk '{print $3}')
|
||||
tbwins=$(grep "Found TB wins:" matecheckcursed.out | awk '{print $4}')
|
||||
if [ $(($mates + $tbwins)) -ne 32 ]; then
|
||||
echo "Sum of mates and TB wins is not 32 in matecheckcursed.out" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -21,19 +21,28 @@ jobs:
|
||||
sanitizers:
|
||||
- name: Run with thread sanitizer
|
||||
make_option: sanitize=thread
|
||||
cxx_extra_flags: ""
|
||||
instrumented_option: sanitizer-thread
|
||||
- name: Run with UB sanitizer
|
||||
make_option: sanitize=undefined
|
||||
cxx_extra_flags: ""
|
||||
instrumented_option: sanitizer-undefined
|
||||
- name: Run under valgrind
|
||||
make_option: ""
|
||||
cxx_extra_flags: ""
|
||||
instrumented_option: valgrind
|
||||
- name: Run under valgrind-thread
|
||||
make_option: ""
|
||||
cxx_extra_flags: ""
|
||||
instrumented_option: valgrind-thread
|
||||
- name: Run non-instrumented
|
||||
make_option: ""
|
||||
cxx_extra_flags: ""
|
||||
instrumented_option: none
|
||||
- name: Run with glibcxx assertions
|
||||
make_option: ""
|
||||
cxx_extra_flags: -D_GLIBCXX_ASSERTIONS
|
||||
instrumented_option: non
|
||||
defaults:
|
||||
run:
|
||||
working-directory: src
|
||||
@@ -72,7 +81,7 @@ jobs:
|
||||
|
||||
- name: ${{ matrix.sanitizers.name }}
|
||||
run: |
|
||||
export CXXFLAGS="-O1 -fno-inline"
|
||||
export CXXFLAGS="-O1 -fno-inline ${{ matrix.sanitizers.cxx_extra_flags }}"
|
||||
make clean
|
||||
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 }}
|
||||
python3 ../tests/instrumented.py --${{ matrix.sanitizers.instrumented_option }} ./stockfish
|
||||
|
||||
+11
-11
@@ -13,15 +13,15 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- name: Ubuntu 20.04 GCC
|
||||
os: ubuntu-20.04
|
||||
- name: Ubuntu 22.04 GCC
|
||||
os: ubuntu-22.04
|
||||
compiler: g++
|
||||
comp: gcc
|
||||
run_32bit_tests: true
|
||||
run_64bit_tests: true
|
||||
shell: bash
|
||||
- name: Ubuntu 20.04 Clang
|
||||
os: ubuntu-20.04
|
||||
- name: Ubuntu 22.04 Clang
|
||||
os: ubuntu-22.04
|
||||
compiler: clang++
|
||||
comp: clang
|
||||
run_32bit_tests: true
|
||||
@@ -139,11 +139,11 @@ jobs:
|
||||
- name: Build Docker container
|
||||
if: matrix.config.base_image
|
||||
run: |
|
||||
docker buildx build --load -t sf_builder - << EOF
|
||||
docker buildx build --platform ${{ matrix.config.platform }} --load -t sf_builder - << EOF
|
||||
FROM ${{ matrix.config.base_image }}
|
||||
WORKDIR /app
|
||||
RUN apk update && apk add make g++
|
||||
CMD ["sh", "script.sh"]
|
||||
CMD ["sh", "src/script.sh"]
|
||||
EOF
|
||||
|
||||
- name: Download required macOS packages
|
||||
@@ -176,7 +176,7 @@ jobs:
|
||||
$COMPCXX -v
|
||||
else
|
||||
echo "$COMPCXX -v" > script.sh
|
||||
docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}/src:/app sf_builder
|
||||
docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}:/app sf_builder
|
||||
fi
|
||||
|
||||
- name: Test help target
|
||||
@@ -342,8 +342,8 @@ jobs:
|
||||
- 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
|
||||
echo "cd src && export LDFLAGS='-static' && make clean && make -j4 ARCH=riscv64 build" > script.sh
|
||||
docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}:/app sf_builder
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
# ppc64 tests
|
||||
@@ -351,8 +351,8 @@ jobs:
|
||||
- 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
|
||||
echo "cd src && export LDFLAGS='-static' && make clean && make -j4 ARCH=ppc-64 build" > script.sh
|
||||
docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}:/app sf_builder
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
# Other tests
|
||||
|
||||
@@ -59,6 +59,7 @@ jobs:
|
||||
mv "${{ matrix.config.simple_name }} ${{ matrix.binaries }}" stockfish-workflow
|
||||
cd stockfish-workflow
|
||||
cp -r src ../stockfish/
|
||||
cp -r scripts ../stockfish/
|
||||
cp stockfish-$NAME-$BINARY$EXT ../stockfish/
|
||||
cp "Top CPU Contributors.txt" ../stockfish/
|
||||
cp Copying.txt ../stockfish/
|
||||
|
||||
@@ -10,3 +10,8 @@ src/-lstdc++.res
|
||||
# Neural network for the NNUE evaluation
|
||||
**/*.nnue
|
||||
|
||||
# Files generated by the instrumented tests
|
||||
tsan.supp
|
||||
__pycache__/
|
||||
tests/syzygy
|
||||
tests/bench_tmp.epd
|
||||
@@ -45,7 +45,9 @@ Bruno de Melo Costa (BM123499)
|
||||
Bruno Pellanda (pellanda)
|
||||
Bryan Cross (crossbr)
|
||||
candirufish
|
||||
Carlos Esparza Sánchez (ces42)
|
||||
Chess13234
|
||||
Chris Bao (sscg13)
|
||||
Chris Cain (ceebo)
|
||||
Ciekce
|
||||
clefrks
|
||||
@@ -123,10 +125,12 @@ jundery
|
||||
Justin Blanchard (UncombedCoconut)
|
||||
Kelly Wilson
|
||||
Ken Takusagawa
|
||||
Kenneth Lee (kennethlee33)
|
||||
Kian E (KJE-98)
|
||||
kinderchocolate
|
||||
Kiran Panditrao (Krgp)
|
||||
Kojirion
|
||||
Krisztián Peőcz
|
||||
Krystian Kuzniarek (kuzkry)
|
||||
Leonardo Ljubičić (ICCF World Champion)
|
||||
Leonid Pechenik (lp--)
|
||||
@@ -143,6 +147,7 @@ Maciej Żenczykowski (zenczykowski)
|
||||
Malcolm Campbell (xoto10)
|
||||
Mark Tenzer (31m059)
|
||||
marotear
|
||||
Mathias Parnaudeau (mparnaudeau)
|
||||
Matt Ginsberg (mattginsberg)
|
||||
Matthew Lai (matthewlai)
|
||||
Matthew Sullivan (Matt14916)
|
||||
@@ -176,6 +181,7 @@ Ofek Shochat (OfekShochat, ghostway)
|
||||
Ondrej Mosnáček (WOnder93)
|
||||
Ondřej Mišina (AndrovT)
|
||||
Oskar Werkelin Ahlin
|
||||
Ömer Faruk Tutkun (OmerFarukTutkun)
|
||||
Pablo Vazquez
|
||||
Panthee
|
||||
Pascal Romaret
|
||||
@@ -237,6 +243,7 @@ Unai Corzo (unaiic)
|
||||
Uri Blass (uriblass)
|
||||
Vince Negri (cuddlestmonkey)
|
||||
Viren
|
||||
Wencey Wang
|
||||
windfishballad
|
||||
xefoci7612
|
||||
Xiang Wang (KatyushaScarlet)
|
||||
|
||||
+1
-2
@@ -49,7 +49,7 @@ further discussion._
|
||||
- Provide a clear and concise description of the changes in the pull request
|
||||
description.
|
||||
|
||||
_First time contributors should add their name to [AUTHORS](../AUTHORS)._
|
||||
_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
|
||||
@@ -86,7 +86,6 @@ 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
|
||||
|
||||
@@ -97,7 +97,7 @@ descriptions. An example suitable for most Intel and AMD chips:
|
||||
|
||||
```
|
||||
cd src
|
||||
make -j profile-build ARCH=x86-64-avx2
|
||||
make -j profile-build
|
||||
```
|
||||
|
||||
Detailed compilation instructions for all platforms can be found in our
|
||||
@@ -120,6 +120,11 @@ where the source code can be found) to generate the exact binary you are
|
||||
distributing. If you make any changes to the source code, these changes must
|
||||
also be made available under GPL v3.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Stockfish uses neural networks trained on [data provided by the Leela Chess Zero
|
||||
project][lc0-data-link], which is made available under the [Open Database License][odbl-link] (ODbL).
|
||||
|
||||
|
||||
[authors-link]: https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS
|
||||
[build-link]: https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml
|
||||
@@ -144,6 +149,8 @@ also be made available under GPL v3.
|
||||
[wiki-uci-link]: https://github.com/official-stockfish/Stockfish/wiki/UCI-&-Commands
|
||||
[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
|
||||
[lc0-data-link]: https://storage.lczero.org/files/training_data
|
||||
[odbl-link]: https://opendatacommons.org/licenses/odbl/odbl-10.txt
|
||||
|
||||
[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
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
*.sh text eol=lf
|
||||
@@ -26,6 +26,17 @@ check_znver_1_2() {
|
||||
[ "$vendor_id" = "AuthenticAMD" ] && [ "$cpu_family" = "23" ] && znver_1_2=true
|
||||
}
|
||||
|
||||
# Set the file CPU loongarch64 architecture
|
||||
set_arch_loongarch64() {
|
||||
if check_flags 'lasx'; then
|
||||
true_arch='loongarch64-lasx'
|
||||
elif check_flags 'lsx'; then
|
||||
true_arch='lonngarch64-lsx'
|
||||
else
|
||||
true_arch='loongarch64'
|
||||
fi
|
||||
}
|
||||
|
||||
# Set the file CPU x86_64 architecture
|
||||
set_arch_x86_64() {
|
||||
if check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then
|
||||
@@ -43,6 +54,20 @@ set_arch_x86_64() {
|
||||
fi
|
||||
}
|
||||
|
||||
set_arch_ppc_64() {
|
||||
if $(grep -q -w "altivec" /proc/cpuinfo); then
|
||||
power=$(grep -oP -m 1 'cpu\t+: POWER\K\d+' /proc/cpuinfo)
|
||||
if [ "0$power" -gt 7 ]; then
|
||||
# VSX started with POWER8
|
||||
true_arch='ppc-64-vsx'
|
||||
else
|
||||
true_arch='ppc-64-altivec'
|
||||
fi
|
||||
else
|
||||
true_arch='ppc-64'
|
||||
fi
|
||||
}
|
||||
|
||||
# Check the system type
|
||||
uname_s=$(uname -s)
|
||||
uname_m=$(uname -m)
|
||||
@@ -51,7 +76,7 @@ case $uname_s in
|
||||
case $uname_m in
|
||||
'arm64')
|
||||
true_arch='apple-silicon'
|
||||
file_arch='x86-64-sse41-popcnt' # Supported by Rosetta 2
|
||||
file_arch='m1-apple-silicon'
|
||||
;;
|
||||
'x86_64')
|
||||
flags=$(sysctl -n machdep.cpu.features machdep.cpu.leaf7_features | tr '\n' ' ' | tr '[:upper:]' '[:lower:]' | tr -d '_.')
|
||||
@@ -76,6 +101,10 @@ case $uname_s in
|
||||
file_os='ubuntu'
|
||||
true_arch='x86-32'
|
||||
;;
|
||||
'ppc64'*)
|
||||
file_os='ubuntu'
|
||||
set_arch_ppc_64
|
||||
;;
|
||||
'aarch64')
|
||||
file_os='android'
|
||||
true_arch='armv8'
|
||||
@@ -90,6 +119,10 @@ case $uname_s in
|
||||
true_arch="$true_arch-neon"
|
||||
fi
|
||||
;;
|
||||
'loongarch64'*)
|
||||
file_os='linux'
|
||||
set_arch_loongarch64
|
||||
;;
|
||||
*) # Unsupported machine type, exit with error
|
||||
printf 'Unsupported machine type: %s\n' "$uname_m"
|
||||
exit 1
|
||||
|
||||
Executable
+75
@@ -0,0 +1,75 @@
|
||||
#!/bin/sh
|
||||
|
||||
wget_or_curl=$( (command -v wget > /dev/null 2>&1 && echo "wget -qO-") || \
|
||||
(command -v curl > /dev/null 2>&1 && echo "curl -skL"))
|
||||
|
||||
if [ -z "$wget_or_curl" ]; then
|
||||
>&2 printf "%s\n" "Neither wget or curl is installed." \
|
||||
"Install one of these tools to download NNUE files automatically."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sha256sum=$( (command -v shasum > /dev/null 2>&1 && echo "shasum -a 256") || \
|
||||
(command -v sha256sum > /dev/null 2>&1 && echo "sha256sum"))
|
||||
|
||||
if [ -z "$sha256sum" ]; then
|
||||
>&2 echo "sha256sum not found, NNUE files will be assumed valid."
|
||||
fi
|
||||
|
||||
get_nnue_filename() {
|
||||
grep "$1" evaluate.h | grep "#define" | sed "s/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/"
|
||||
}
|
||||
|
||||
validate_network() {
|
||||
# If no sha256sum command is available, assume the file is always valid.
|
||||
if [ -n "$sha256sum" ] && [ -f "$1" ]; then
|
||||
if [ "$1" != "nn-$($sha256sum "$1" | cut -c 1-12).nnue" ]; then
|
||||
rm -f "$1"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
fetch_network() {
|
||||
_filename="$(get_nnue_filename "$1")"
|
||||
|
||||
if [ -z "$_filename" ]; then
|
||||
>&2 echo "NNUE file name not found for: $1"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -f "$_filename" ]; then
|
||||
if validate_network "$_filename"; then
|
||||
echo "Existing $_filename validated, skipping download"
|
||||
return
|
||||
else
|
||||
echo "Removing invalid NNUE file: $_filename"
|
||||
fi
|
||||
fi
|
||||
|
||||
for url in \
|
||||
"https://tests.stockfishchess.org/api/nn/$_filename" \
|
||||
"https://github.com/official-stockfish/networks/raw/master/$_filename"; do
|
||||
echo "Downloading from $url ..."
|
||||
if $wget_or_curl "$url" > "$_filename"; then
|
||||
if validate_network "$_filename"; then
|
||||
echo "Successfully validated $_filename"
|
||||
else
|
||||
echo "Downloaded $_filename is invalid"
|
||||
continue
|
||||
fi
|
||||
else
|
||||
echo "Failed to download from $url"
|
||||
fi
|
||||
if [ -f "$_filename" ]; then
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
# Download was not successful in the loop, return false.
|
||||
>&2 echo "Failed to download $_filename"
|
||||
return 1
|
||||
}
|
||||
|
||||
fetch_network EvalFileDefaultNameBig && \
|
||||
fetch_network EvalFileDefaultNameSmall
|
||||
+202
-178
@@ -1,5 +1,5 @@
|
||||
# Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
# Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
# Copyright (C) 2004-2025 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
|
||||
@@ -57,7 +57,7 @@ SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \
|
||||
search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
|
||||
nnue/nnue_misc.cpp nnue/features/half_ka_v2_hm.cpp nnue/network.cpp engine.cpp score.cpp memory.cpp
|
||||
|
||||
HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \
|
||||
HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h history.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 \
|
||||
@@ -98,8 +98,12 @@ VPATH = syzygy:nnue:nnue/features
|
||||
# avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512
|
||||
# vnni256 = yes/no --- -mavx256vnni --- Use Intel Vector Neural Network Instructions 512 with 256bit operands
|
||||
# vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512
|
||||
# altivec = yes/no --- -maltivec --- Use PowerPC Altivec SIMD extension
|
||||
# vsx = yes/no --- -mvsx --- Use POWER VSX SIMD extension
|
||||
# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture
|
||||
# dotprod = yes/no --- -DUSE_NEON_DOTPROD --- Use ARM advanced SIMD Int8 dot product instructions
|
||||
# lsx = yes/no --- -mlsx --- Use Loongson SIMD eXtension
|
||||
# lasx = yes/no --- -mlasx --- use Loongson Advanced SIMD eXtension
|
||||
#
|
||||
# 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
|
||||
@@ -124,8 +128,9 @@ endif
|
||||
ifeq ($(ARCH), $(filter $(ARCH), \
|
||||
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 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 loongarch64))
|
||||
x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-64-altivec ppc-64-vsx ppc-32 e2k \
|
||||
armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64 \
|
||||
loongarch64 loongarch64-lsx loongarch64-lasx))
|
||||
SUPPORTED_ARCH=true
|
||||
else
|
||||
SUPPORTED_ARCH=false
|
||||
@@ -148,9 +153,13 @@ avxvnni = no
|
||||
avx512 = no
|
||||
vnni256 = no
|
||||
vnni512 = no
|
||||
altivec = no
|
||||
vsx = no
|
||||
neon = no
|
||||
dotprod = no
|
||||
arm_version = 0
|
||||
lsx = no
|
||||
lasx = no
|
||||
STRIP = strip
|
||||
|
||||
ifneq ($(shell which clang-format-18 2> /dev/null),)
|
||||
@@ -355,6 +364,20 @@ ifeq ($(ARCH),ppc-64)
|
||||
prefetch = yes
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ppc-64-altivec)
|
||||
arch = ppc64
|
||||
popcnt = yes
|
||||
prefetch = yes
|
||||
altivec = yes
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),ppc-64-vsx)
|
||||
arch = ppc64
|
||||
popcnt = yes
|
||||
prefetch = yes
|
||||
vsx = yes
|
||||
endif
|
||||
|
||||
ifeq ($(findstring e2k,$(ARCH)),e2k)
|
||||
arch = e2k
|
||||
mmx = yes
|
||||
@@ -370,8 +393,19 @@ ifeq ($(ARCH),riscv64)
|
||||
arch = riscv64
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),loongarch64)
|
||||
ifeq ($(findstring loongarch64,$(ARCH)),loongarch64)
|
||||
arch = loongarch64
|
||||
prefetch = yes
|
||||
|
||||
ifeq ($(findstring -lasx,$(ARCH)),-lasx)
|
||||
lsx = yes
|
||||
lasx = yes
|
||||
endif
|
||||
|
||||
ifeq ($(findstring -lsx,$(ARCH)),-lsx)
|
||||
lsx = yes
|
||||
endif
|
||||
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -408,7 +442,7 @@ ifeq ($(COMP),gcc)
|
||||
ifeq ($(ARCH),riscv64)
|
||||
CXXFLAGS += -latomic
|
||||
endif
|
||||
else ifeq ($(ARCH),loongarch64)
|
||||
else ifeq ($(arch),loongarch64)
|
||||
CXXFLAGS += -latomic
|
||||
else
|
||||
CXXFLAGS += -m$(bits)
|
||||
@@ -480,7 +514,7 @@ ifeq ($(COMP),clang)
|
||||
ifeq ($(ARCH),riscv64)
|
||||
CXXFLAGS += -latomic
|
||||
endif
|
||||
else ifeq ($(ARCH),loongarch64)
|
||||
else ifeq ($(arch),loongarch64)
|
||||
CXXFLAGS += -latomic
|
||||
else
|
||||
CXXFLAGS += -m$(bits)
|
||||
@@ -634,7 +668,7 @@ else
|
||||
endif
|
||||
|
||||
ifeq ($(popcnt),yes)
|
||||
ifeq ($(arch),$(filter $(arch),ppc64 armv7 armv8 arm64))
|
||||
ifeq ($(arch),$(filter $(arch),ppc64 ppc64-altivec ppc64-vsx armv7 armv8 arm64))
|
||||
CXXFLAGS += -DUSE_POPCNT
|
||||
else
|
||||
CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT
|
||||
@@ -704,6 +738,20 @@ ifeq ($(mmx),yes)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(altivec),yes)
|
||||
CXXFLAGS += -maltivec
|
||||
ifeq ($(COMP),gcc)
|
||||
CXXFLAGS += -mabi=altivec
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(vsx),yes)
|
||||
CXXFLAGS += -mvsx
|
||||
ifeq ($(COMP),gcc)
|
||||
CXXFLAGS += -DNO_WARN_X86_INTRINSICS -DUSE_SSE2
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(neon),yes)
|
||||
CXXFLAGS += -DUSE_NEON=$(arm_version)
|
||||
ifeq ($(KERNEL),Linux)
|
||||
@@ -719,6 +767,18 @@ ifeq ($(dotprod),yes)
|
||||
CXXFLAGS += -march=armv8.2-a+dotprod -DUSE_NEON_DOTPROD
|
||||
endif
|
||||
|
||||
ifeq ($(lasx),yes)
|
||||
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
|
||||
CXXFLAGS += -mlasx
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(lsx),yes)
|
||||
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
|
||||
CXXFLAGS += -mlsx
|
||||
endif
|
||||
endif
|
||||
|
||||
### 3.7 pext
|
||||
ifeq ($(pext),yes)
|
||||
CXXFLAGS += -DUSE_PEXT
|
||||
@@ -791,71 +851,75 @@ endif
|
||||
### ==========================================================================
|
||||
|
||||
help:
|
||||
@echo ""
|
||||
@echo "To compile stockfish, type: "
|
||||
@echo ""
|
||||
@echo "make -j target [ARCH=arch] [COMP=compiler] [COMPCXX=cxx]"
|
||||
@echo ""
|
||||
@echo "Supported targets:"
|
||||
@echo ""
|
||||
@echo "help > Display architecture details"
|
||||
@echo "profile-build > standard build with profile-guided optimization"
|
||||
@echo "build > skip profile-guided optimization"
|
||||
@echo "net > Download the default nnue nets"
|
||||
@echo "strip > Strip executable"
|
||||
@echo "install > Install executable"
|
||||
@echo "clean > Clean up"
|
||||
@echo ""
|
||||
@echo "Supported archs:"
|
||||
@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-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-avxvnni > x86 64-bit with vnni 256bit support"
|
||||
@echo "x86-64-bmi2 > x86 64-bit with bmi2 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-modern > deprecated, currently x86-64-sse41-popcnt"
|
||||
@echo "x86-64-ssse3 > x86 64-bit with ssse3 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-32-sse41-popcnt > x86 32-bit with sse41 and popcnt support"
|
||||
@echo "x86-32-sse2 > x86 32-bit with sse2 support"
|
||||
@echo "x86-32 > x86 32-bit generic (with mmx compile support)"
|
||||
@echo "ppc-64 > PPC 64-bit"
|
||||
@echo "ppc-32 > PPC 32-bit"
|
||||
@echo "armv7 > ARMv7 32-bit"
|
||||
@echo "armv7-neon > ARMv7 32-bit with popcnt and neon"
|
||||
@echo "armv8 > ARMv8 64-bit with popcnt and neon"
|
||||
@echo "armv8-dotprod > ARMv8 64-bit with popcnt, neon and dot product support"
|
||||
@echo "e2k > Elbrus 2000"
|
||||
@echo "apple-silicon > Apple silicon ARM64"
|
||||
@echo "general-64 > unspecified 64-bit"
|
||||
@echo "general-32 > unspecified 32-bit"
|
||||
@echo "riscv64 > RISC-V 64-bit"
|
||||
@echo "loongarch64 > LoongArch 64-bit"
|
||||
@echo ""
|
||||
@echo "Supported compilers:"
|
||||
@echo ""
|
||||
@echo "gcc > GNU compiler (default)"
|
||||
@echo "mingw > GNU compiler with MinGW under Windows"
|
||||
@echo "clang > LLVM Clang compiler"
|
||||
@echo "icx > Intel oneAPI DPC++/C++ Compiler"
|
||||
@echo "ndk > Google NDK to cross-compile for Android"
|
||||
@echo ""
|
||||
@echo "Simple examples. If you don't know what to do, you likely want to run one of: "
|
||||
@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-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 ""
|
||||
@echo "Advanced examples, for experienced users: "
|
||||
@echo ""
|
||||
@echo "make -j profile-build ARCH=x86-64-avxvnni"
|
||||
@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 ""
|
||||
@echo "" && \
|
||||
echo "To compile stockfish, type: " && \
|
||||
echo "" && \
|
||||
echo "make -j target [ARCH=arch] [COMP=compiler] [COMPCXX=cxx]" && \
|
||||
echo "" && \
|
||||
echo "Supported targets:" && \
|
||||
echo "" && \
|
||||
echo "help > Display architecture details" && \
|
||||
echo "profile-build > standard build with profile-guided optimization" && \
|
||||
echo "build > skip profile-guided optimization" && \
|
||||
echo "net > Download the default nnue nets" && \
|
||||
echo "strip > Strip executable" && \
|
||||
echo "install > Install executable" && \
|
||||
echo "clean > Clean up" && \
|
||||
echo "" && \
|
||||
echo "Supported archs:" && \
|
||||
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-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-avxvnni > x86 64-bit with vnni 256bit support" && \
|
||||
echo "x86-64-bmi2 > x86 64-bit with bmi2 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-modern > deprecated, currently x86-64-sse41-popcnt" && \
|
||||
echo "x86-64-ssse3 > x86 64-bit with ssse3 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-32-sse41-popcnt > x86 32-bit with sse41 and popcnt support" && \
|
||||
echo "x86-32-sse2 > x86 32-bit with sse2 support" && \
|
||||
echo "x86-32 > x86 32-bit generic (with mmx compile support)" && \
|
||||
echo "ppc-64 > PPC 64-bit" && \
|
||||
echo "ppc-64-altivec > PPC 64-bit with altivec support" && \
|
||||
echo "ppc-64-vsx > PPC 64-bit with vsx support" && \
|
||||
echo "ppc-32 > PPC 32-bit" && \
|
||||
echo "armv7 > ARMv7 32-bit" && \
|
||||
echo "armv7-neon > ARMv7 32-bit with popcnt and neon" && \
|
||||
echo "armv8 > ARMv8 64-bit with popcnt and neon" && \
|
||||
echo "armv8-dotprod > ARMv8 64-bit with popcnt, neon and dot product support" && \
|
||||
echo "e2k > Elbrus 2000" && \
|
||||
echo "apple-silicon > Apple silicon ARM64" && \
|
||||
echo "general-64 > unspecified 64-bit" && \
|
||||
echo "general-32 > unspecified 32-bit" && \
|
||||
echo "riscv64 > RISC-V 64-bit" && \
|
||||
echo "loongarch64 > LoongArch 64-bit" && \
|
||||
echo "loongarch64-lsx > LoongArch 64-bit with SIMD eXtension" && \
|
||||
echo "loongarch64-lasx > LoongArch 64-bit with Advanced SIMD eXtension" && \
|
||||
echo "" && \
|
||||
echo "Supported compilers:" && \
|
||||
echo "" && \
|
||||
echo "gcc > GNU compiler (default)" && \
|
||||
echo "mingw > GNU compiler with MinGW under Windows" && \
|
||||
echo "clang > LLVM Clang compiler" && \
|
||||
echo "icx > Intel oneAPI DPC++/C++ Compiler" && \
|
||||
echo "ndk > Google NDK to cross-compile for Android" && \
|
||||
echo "" && \
|
||||
echo "Simple examples. If you don't know what to do, you likely want to run one of: " && \
|
||||
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-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 "" && \
|
||||
echo "Advanced examples, for experienced users: " && \
|
||||
echo "" && \
|
||||
echo "make -j profile-build ARCH=x86-64-avxvnni" && \
|
||||
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 ""
|
||||
ifneq ($(SUPPORTED_ARCH), true)
|
||||
@echo "Specify a supported architecture with the ARCH option for more details"
|
||||
@echo ""
|
||||
@@ -917,59 +981,9 @@ profileclean:
|
||||
@rm -f stockfish.res
|
||||
@rm -f ./-lstdc++.res
|
||||
|
||||
define fetch_network
|
||||
@echo "Default net: $(nnuenet)"
|
||||
@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"; \
|
||||
fi
|
||||
@if [ "x$(shasum_command)" = "x" ]; then \
|
||||
echo "shasum / sha256sum not found, skipping net validation"; \
|
||||
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 \
|
||||
if test -f "$(nnuenet)"; then \
|
||||
echo "$(nnuenet) available : OK"; break; \
|
||||
else \
|
||||
if [ "x$(curl_or_wget)" != "x" ]; then \
|
||||
echo "Downloading $${nnuedownloadurl}"; $(curl_or_wget) $${nnuedownloadurl} > $(nnuenet);\
|
||||
else \
|
||||
echo "No net found and download not possible"; exit 1;\
|
||||
fi; \
|
||||
fi; \
|
||||
if [ "x$(shasum_command)" != "x" ]; then \
|
||||
if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \
|
||||
echo "Removing failed download"; rm -f $(nnuenet); \
|
||||
fi; \
|
||||
fi; \
|
||||
done
|
||||
@if ! test -f "$(nnuenet)"; then \
|
||||
echo "Failed to download $(nnuenet)."; \
|
||||
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
|
||||
|
||||
# set up shell variables for the net stuff
|
||||
define netvariables
|
||||
$(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
|
||||
|
||||
# evaluation network (nnue)
|
||||
net:
|
||||
$(call netvariables, EvalFileDefaultNameBig)
|
||||
$(call fetch_network)
|
||||
$(call netvariables, EvalFileDefaultNameSmall)
|
||||
$(call fetch_network)
|
||||
@$(SHELL) ../scripts/net.sh
|
||||
|
||||
format:
|
||||
$(CLANG-FORMAT) -i $(SRCS) $(HEADERS) -style=file
|
||||
@@ -986,61 +1000,71 @@ all: $(EXE) .depend
|
||||
|
||||
config-sanity: net
|
||||
@echo ""
|
||||
@echo "Config:"
|
||||
@echo "debug: '$(debug)'"
|
||||
@echo "sanitize: '$(sanitize)'"
|
||||
@echo "optimize: '$(optimize)'"
|
||||
@echo "arch: '$(arch)'"
|
||||
@echo "bits: '$(bits)'"
|
||||
@echo "kernel: '$(KERNEL)'"
|
||||
@echo "os: '$(OS)'"
|
||||
@echo "prefetch: '$(prefetch)'"
|
||||
@echo "popcnt: '$(popcnt)'"
|
||||
@echo "pext: '$(pext)'"
|
||||
@echo "sse: '$(sse)'"
|
||||
@echo "mmx: '$(mmx)'"
|
||||
@echo "sse2: '$(sse2)'"
|
||||
@echo "ssse3: '$(ssse3)'"
|
||||
@echo "sse41: '$(sse41)'"
|
||||
@echo "avx2: '$(avx2)'"
|
||||
@echo "avxvnni: '$(avxvnni)'"
|
||||
@echo "avx512: '$(avx512)'"
|
||||
@echo "vnni256: '$(vnni256)'"
|
||||
@echo "vnni512: '$(vnni512)'"
|
||||
@echo "neon: '$(neon)'"
|
||||
@echo "dotprod: '$(dotprod)'"
|
||||
@echo "arm_version: '$(arm_version)'"
|
||||
@echo "target_windows: '$(target_windows)'"
|
||||
@echo ""
|
||||
@echo "Flags:"
|
||||
@echo "CXX: $(CXX)"
|
||||
@echo "CXXFLAGS: $(CXXFLAGS)"
|
||||
@echo "LDFLAGS: $(LDFLAGS)"
|
||||
@echo ""
|
||||
@echo "Testing config sanity. If this fails, try 'make help' ..."
|
||||
@echo ""
|
||||
@test "$(debug)" = "yes" || test "$(debug)" = "no"
|
||||
@test "$(optimize)" = "yes" || test "$(optimize)" = "no"
|
||||
@test "$(SUPPORTED_ARCH)" = "true"
|
||||
@test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
|
||||
@echo "Config:" && \
|
||||
echo "debug: '$(debug)'" && \
|
||||
echo "sanitize: '$(sanitize)'" && \
|
||||
echo "optimize: '$(optimize)'" && \
|
||||
echo "arch: '$(arch)'" && \
|
||||
echo "bits: '$(bits)'" && \
|
||||
echo "kernel: '$(KERNEL)'" && \
|
||||
echo "os: '$(OS)'" && \
|
||||
echo "prefetch: '$(prefetch)'" && \
|
||||
echo "popcnt: '$(popcnt)'" && \
|
||||
echo "pext: '$(pext)'" && \
|
||||
echo "sse: '$(sse)'" && \
|
||||
echo "mmx: '$(mmx)'" && \
|
||||
echo "sse2: '$(sse2)'" && \
|
||||
echo "ssse3: '$(ssse3)'" && \
|
||||
echo "sse41: '$(sse41)'" && \
|
||||
echo "avx2: '$(avx2)'" && \
|
||||
echo "avxvnni: '$(avxvnni)'" && \
|
||||
echo "avx512: '$(avx512)'" && \
|
||||
echo "vnni256: '$(vnni256)'" && \
|
||||
echo "vnni512: '$(vnni512)'" && \
|
||||
echo "altivec: '$(altivec)'" && \
|
||||
echo "vsx: '$(vsx)'" && \
|
||||
echo "neon: '$(neon)'" && \
|
||||
echo "dotprod: '$(dotprod)'" && \
|
||||
echo "arm_version: '$(arm_version)'" && \
|
||||
echo "lsx: '$(lsx)'" && \
|
||||
echo "lasx: '$(lasx)'" && \
|
||||
echo "target_windows: '$(target_windows)'" && \
|
||||
echo "" && \
|
||||
echo "Flags:" && \
|
||||
echo "CXX: $(CXX)" && \
|
||||
echo "CXXFLAGS: $(CXXFLAGS)" && \
|
||||
echo "LDFLAGS: $(LDFLAGS)" && \
|
||||
echo "" && \
|
||||
echo "Testing config sanity. If this fails, try 'make help' ..." && \
|
||||
echo "" && \
|
||||
(test "$(debug)" = "yes" || test "$(debug)" = "no") && \
|
||||
(test "$(optimize)" = "yes" || test "$(optimize)" = "no") && \
|
||||
(test "$(SUPPORTED_ARCH)" = "true") && \
|
||||
(test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
|
||||
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "e2k" || \
|
||||
test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" || test "$(arch)" = "riscv64" || test "$(arch)" = "loongarch64"
|
||||
@test "$(bits)" = "32" || test "$(bits)" = "64"
|
||||
@test "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
|
||||
@test "$(popcnt)" = "yes" || test "$(popcnt)" = "no"
|
||||
@test "$(pext)" = "yes" || test "$(pext)" = "no"
|
||||
@test "$(sse)" = "yes" || test "$(sse)" = "no"
|
||||
@test "$(mmx)" = "yes" || test "$(mmx)" = "no"
|
||||
@test "$(sse2)" = "yes" || test "$(sse2)" = "no"
|
||||
@test "$(ssse3)" = "yes" || test "$(ssse3)" = "no"
|
||||
@test "$(sse41)" = "yes" || test "$(sse41)" = "no"
|
||||
@test "$(avx2)" = "yes" || test "$(avx2)" = "no"
|
||||
@test "$(avx512)" = "yes" || test "$(avx512)" = "no"
|
||||
@test "$(vnni256)" = "yes" || test "$(vnni256)" = "no"
|
||||
@test "$(vnni512)" = "yes" || test "$(vnni512)" = "no"
|
||||
@test "$(neon)" = "yes" || test "$(neon)" = "no"
|
||||
@test "$(comp)" = "gcc" || test "$(comp)" = "icx" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" \
|
||||
|| test "$(comp)" = "armv7a-linux-androideabi16-clang" || test "$(comp)" = "aarch64-linux-android21-clang"
|
||||
test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" || \
|
||||
test "$(arch)" = "riscv64" || test "$(arch)" = "loongarch64") && \
|
||||
(test "$(bits)" = "32" || test "$(bits)" = "64") && \
|
||||
(test "$(prefetch)" = "yes" || test "$(prefetch)" = "no") && \
|
||||
(test "$(popcnt)" = "yes" || test "$(popcnt)" = "no") && \
|
||||
(test "$(pext)" = "yes" || test "$(pext)" = "no") && \
|
||||
(test "$(sse)" = "yes" || test "$(sse)" = "no") && \
|
||||
(test "$(mmx)" = "yes" || test "$(mmx)" = "no") && \
|
||||
(test "$(sse2)" = "yes" || test "$(sse2)" = "no") && \
|
||||
(test "$(ssse3)" = "yes" || test "$(ssse3)" = "no") && \
|
||||
(test "$(sse41)" = "yes" || test "$(sse41)" = "no") && \
|
||||
(test "$(avx2)" = "yes" || test "$(avx2)" = "no") && \
|
||||
(test "$(avx512)" = "yes" || test "$(avx512)" = "no") && \
|
||||
(test "$(vnni256)" = "yes" || test "$(vnni256)" = "no") && \
|
||||
(test "$(vnni512)" = "yes" || test "$(vnni512)" = "no") && \
|
||||
(test "$(altivec)" = "yes" || test "$(altivec)" = "no") && \
|
||||
(test "$(vsx)" = "yes" || test "$(vsx)" = "no") && \
|
||||
(test "$(neon)" = "yes" || test "$(neon)" = "no") && \
|
||||
(test "$(lsx)" = "yes" || test "$(lsx)" = "no") && \
|
||||
(test "$(lasx)" = "yes" || test "$(lasx)" = "no") && \
|
||||
(test "$(comp)" = "gcc" || test "$(comp)" = "icx" || test "$(comp)" = "mingw" || \
|
||||
test "$(comp)" = "clang" || test "$(comp)" = "armv7a-linux-androideabi16-clang" || \
|
||||
test "$(comp)" = "aarch64-linux-android21-clang")
|
||||
|
||||
$(EXE): $(OBJS)
|
||||
+$(CXX) -o $@ $(OBJS) $(LDFLAGS)
|
||||
|
||||
+350
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
#include "benchmark.h"
|
||||
#include "numa.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
@@ -91,6 +92,282 @@ const std::vector<std::string> Defaults = {
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// clang-format off
|
||||
// human-randomly picked 5 games with <60 moves from
|
||||
// https://tests.stockfishchess.org/tests/view/665c71f9fd45fb0f907c21e0
|
||||
// only moves for one side
|
||||
const std::vector<std::vector<std::string>> BenchmarkPositions = {
|
||||
{
|
||||
"rnbq1k1r/ppp1bppp/4pn2/8/2B5/2NP1N2/PPP2PPP/R1BQR1K1 b - - 2 8",
|
||||
"rnbq1k1r/pp2bppp/4pn2/2p5/2B2B2/2NP1N2/PPP2PPP/R2QR1K1 b - - 1 9",
|
||||
"r1bq1k1r/pp2bppp/2n1pn2/2p5/2B1NB2/3P1N2/PPP2PPP/R2QR1K1 b - - 3 10",
|
||||
"r1bq1k1r/pp2bppp/2n1p3/2p5/2B1PB2/5N2/PPP2PPP/R2QR1K1 b - - 0 11",
|
||||
"r1b2k1r/pp2bppp/2n1p3/2p5/2B1PB2/5N2/PPP2PPP/3RR1K1 b - - 0 12",
|
||||
"r1b1k2r/pp2bppp/2n1p3/2p5/2B1PB2/2P2N2/PP3PPP/3RR1K1 b - - 0 13",
|
||||
"r1b1k2r/1p2bppp/p1n1p3/2p5/4PB2/2P2N2/PP2BPPP/3RR1K1 b - - 1 14",
|
||||
"r1b1k2r/4bppp/p1n1p3/1pp5/P3PB2/2P2N2/1P2BPPP/3RR1K1 b - - 0 15",
|
||||
"r1b1k2r/4bppp/p1n1p3/1P6/2p1PB2/2P2N2/1P2BPPP/3RR1K1 b - - 0 16",
|
||||
"r1b1k2r/4bppp/2n1p3/1p6/2p1PB2/1PP2N2/4BPPP/3RR1K1 b - - 0 17",
|
||||
"r3k2r/3bbppp/2n1p3/1p6/2P1PB2/2P2N2/4BPPP/3RR1K1 b - - 0 18",
|
||||
"r3k2r/3bbppp/2n1p3/8/1pP1P3/2P2N2/3BBPPP/3RR1K1 b - - 1 19",
|
||||
"1r2k2r/3bbppp/2n1p3/8/1pPNP3/2P5/3BBPPP/3RR1K1 b - - 3 20",
|
||||
"1r2k2r/3bbppp/2n1p3/8/2PNP3/2B5/4BPPP/3RR1K1 b - - 0 21",
|
||||
"1r2k2r/3bb1pp/2n1pp2/1N6/2P1P3/2B5/4BPPP/3RR1K1 b - - 1 22",
|
||||
"1r2k2r/3b2pp/2n1pp2/1N6/1BP1P3/8/4BPPP/3RR1K1 b - - 0 23",
|
||||
"1r2k2r/3b2pp/4pp2/1N6/1nP1P3/8/3RBPPP/4R1K1 b - - 1 24",
|
||||
"1r5r/3bk1pp/4pp2/1N6/1nP1PP2/8/3RB1PP/4R1K1 b - - 0 25",
|
||||
"1r5r/3bk1pp/2n1pp2/1N6/2P1PP2/8/3RBKPP/4R3 b - - 2 26",
|
||||
"1r5r/3bk1pp/2n2p2/1N2p3/2P1PP2/6P1/3RBK1P/4R3 b - - 0 27",
|
||||
"1r1r4/3bk1pp/2n2p2/1N2p3/2P1PP2/6P1/3RBK1P/R7 b - - 2 28",
|
||||
"1r1r4/N3k1pp/2n1bp2/4p3/2P1PP2/6P1/3RBK1P/R7 b - - 4 29",
|
||||
"1r1r4/3bk1pp/2N2p2/4p3/2P1PP2/6P1/3RBK1P/R7 b - - 0 30",
|
||||
"1r1R4/4k1pp/2b2p2/4p3/2P1PP2/6P1/4BK1P/R7 b - - 0 31",
|
||||
"3r4/4k1pp/2b2p2/4P3/2P1P3/6P1/4BK1P/R7 b - - 0 32",
|
||||
"3r4/R3k1pp/2b5/4p3/2P1P3/6P1/4BK1P/8 b - - 1 33",
|
||||
"8/3rk1pp/2b5/R3p3/2P1P3/6P1/4BK1P/8 b - - 3 34",
|
||||
"8/3r2pp/2bk4/R1P1p3/4P3/6P1/4BK1P/8 b - - 0 35",
|
||||
"8/2kr2pp/2b5/R1P1p3/4P3/4K1P1/4B2P/8 b - - 2 36",
|
||||
"1k6/3r2pp/2b5/RBP1p3/4P3/4K1P1/7P/8 b - - 4 37",
|
||||
"8/1k1r2pp/2b5/R1P1p3/4P3/3BK1P1/7P/8 b - - 6 38",
|
||||
"1k6/3r2pp/2b5/2P1p3/4P3/3BK1P1/7P/R7 b - - 8 39",
|
||||
"1k6/r5pp/2b5/2P1p3/4P3/3BK1P1/7P/5R2 b - - 10 40",
|
||||
"1k3R2/6pp/2b5/2P1p3/4P3/r2BK1P1/7P/8 b - - 12 41",
|
||||
"5R2/2k3pp/2b5/2P1p3/4P3/r2B2P1/3K3P/8 b - - 14 42",
|
||||
"5R2/2k3pp/2b5/2P1p3/4P3/3BK1P1/r6P/8 b - - 16 43",
|
||||
"5R2/2k3pp/2b5/2P1p3/4P3/r2B2P1/4K2P/8 b - - 18 44",
|
||||
"5R2/2k3pp/2b5/2P1p3/4P3/3B1KP1/r6P/8 b - - 20 45",
|
||||
"8/2k2Rpp/2b5/2P1p3/4P3/r2B1KP1/7P/8 b - - 22 46",
|
||||
"3k4/5Rpp/2b5/2P1p3/4P3/r2B2P1/4K2P/8 b - - 24 47",
|
||||
"3k4/5Rpp/2b5/2P1p3/4P3/3B1KP1/r6P/8 b - - 26 48",
|
||||
"3k4/5Rpp/2b5/2P1p3/4P3/r2B2P1/4K2P/8 b - - 28 49",
|
||||
"3k4/5Rpp/2b5/2P1p3/4P3/3BK1P1/r6P/8 b - - 30 50",
|
||||
"3k4/5Rpp/2b5/2P1p3/4P3/r2B2P1/3K3P/8 b - - 32 51",
|
||||
"3k4/5Rpp/2b5/2P1p3/4P3/2KB2P1/r6P/8 b - - 34 52",
|
||||
"3k4/5Rpp/2b5/2P1p3/4P3/r2B2P1/2K4P/8 b - - 36 53",
|
||||
"3k4/5Rpp/2b5/2P1p3/4P3/1K1B2P1/r6P/8 b - - 38 54",
|
||||
"3k4/6Rp/2b5/2P1p3/4P3/1K1B2P1/7r/8 b - - 0 55",
|
||||
"3k4/8/2b3Rp/2P1p3/4P3/1K1B2P1/7r/8 b - - 1 56",
|
||||
"8/2k3R1/2b4p/2P1p3/4P3/1K1B2P1/7r/8 b - - 3 57",
|
||||
"3k4/8/2b3Rp/2P1p3/4P3/1K1B2P1/7r/8 b - - 5 58",
|
||||
"8/2k5/2b3Rp/2P1p3/1K2P3/3B2P1/7r/8 b - - 7 59",
|
||||
"8/2k5/2b3Rp/2P1p3/4P3/2KB2P1/3r4/8 b - - 9 60",
|
||||
"8/2k5/2b3Rp/2P1p3/1K2P3/3B2P1/6r1/8 b - - 11 61",
|
||||
"8/2k5/2b3Rp/2P1p3/4P3/2KB2P1/3r4/8 b - - 13 62",
|
||||
"8/2k5/2b3Rp/2P1p3/2K1P3/3B2P1/6r1/8 b - - 15 63",
|
||||
"4b3/2k3R1/7p/2P1p3/2K1P3/3B2P1/6r1/8 b - - 17 64",
|
||||
},
|
||||
{
|
||||
"r1bqkbnr/npp1pppp/p7/3P4/4pB2/2N5/PPP2PPP/R2QKBNR w KQkq - 1 6",
|
||||
"r1bqkb1r/npp1pppp/p4n2/3P4/4pB2/2N5/PPP1QPPP/R3KBNR w KQkq - 3 7",
|
||||
"r2qkb1r/npp1pppp/p4n2/3P1b2/4pB2/2N5/PPP1QPPP/2KR1BNR w kq - 5 8",
|
||||
"r2qkb1r/1pp1pppp/p4n2/1n1P1b2/4pB2/2N4P/PPP1QPP1/2KR1BNR w kq - 1 9",
|
||||
"r2qkb1r/1pp1pppp/5n2/1p1P1b2/4pB2/7P/PPP1QPP1/2KR1BNR w kq - 0 10",
|
||||
"r2qkb1r/1ppbpppp/5n2/1Q1P4/4pB2/7P/PPP2PP1/2KR1BNR w kq - 1 11",
|
||||
"3qkb1r/1Qpbpppp/5n2/3P4/4pB2/7P/rPP2PP1/2KR1BNR w k - 0 12",
|
||||
"q3kb1r/1Qpbpppp/5n2/3P4/4pB2/7P/rPP2PP1/1K1R1BNR w k - 2 13",
|
||||
"r3kb1r/2pbpppp/5n2/3P4/4pB2/7P/1PP2PP1/1K1R1BNR w k - 0 14",
|
||||
"r3kb1r/2Bb1ppp/4pn2/3P4/4p3/7P/1PP2PP1/1K1R1BNR w k - 0 15",
|
||||
"r3kb1r/2Bb2pp/4pn2/8/4p3/7P/1PP2PP1/1K1R1BNR w k - 0 16",
|
||||
"r3k2r/2Bb2pp/4pn2/2b5/4p3/7P/1PP1NPP1/1K1R1B1R w k - 2 17",
|
||||
"r6r/2Bbk1pp/4pn2/2b5/3Np3/7P/1PP2PP1/1K1R1B1R w - - 4 18",
|
||||
"r6r/b2bk1pp/4pn2/4B3/3Np3/7P/1PP2PP1/1K1R1B1R w - - 6 19",
|
||||
"r1r5/b2bk1pp/4pn2/4B3/2BNp3/7P/1PP2PP1/1K1R3R w - - 8 20",
|
||||
"r7/b2bk1pp/4pn2/2r1B3/2BNp3/1P5P/2P2PP1/1K1R3R w - - 1 21",
|
||||
"rb6/3bk1pp/4pn2/2r1B3/2BNpP2/1P5P/2P3P1/1K1R3R w - - 1 22",
|
||||
"1r6/3bk1pp/4pn2/2r5/2BNpP2/1P5P/2P3P1/1K1R3R w - - 0 23",
|
||||
"1r6/3bk1p1/4pn1p/2r5/2BNpP2/1P5P/2P3P1/2KR3R w - - 0 24",
|
||||
"8/3bk1p1/1r2pn1p/2r5/2BNpP1P/1P6/2P3P1/2KR3R w - - 1 25",
|
||||
"8/3bk3/1r2pnpp/2r5/2BNpP1P/1P6/2P3P1/2K1R2R w - - 0 26",
|
||||
"2b5/4k3/1r2pnpp/2r5/2BNpP1P/1P4P1/2P5/2K1R2R w - - 1 27",
|
||||
"8/1b2k3/1r2pnpp/2r5/2BNpP1P/1P4P1/2P5/2K1R1R1 w - - 3 28",
|
||||
"8/1b1nk3/1r2p1pp/2r5/2BNpPPP/1P6/2P5/2K1R1R1 w - - 1 29",
|
||||
"8/1b2k3/1r2p1pp/2r1nP2/2BNp1PP/1P6/2P5/2K1R1R1 w - - 1 30",
|
||||
"8/1b2k3/1r2p1p1/2r1nPp1/2BNp2P/1P6/2P5/2K1R1R1 w - - 0 31",
|
||||
"8/1b2k3/1r2p1n1/2r3p1/2BNp2P/1P6/2P5/2K1R1R1 w - - 0 32",
|
||||
"8/1b2k3/1r2p1n1/6r1/2BNp2P/1P6/2P5/2K1R3 w - - 0 33",
|
||||
"8/1b2k3/1r2p3/4n1P1/2BNp3/1P6/2P5/2K1R3 w - - 1 34",
|
||||
"8/1b2k3/1r2p3/4n1P1/2BN4/1P2p3/2P5/2K4R w - - 0 35",
|
||||
"8/1b2k3/1r2p2R/6P1/2nN4/1P2p3/2P5/2K5 w - - 0 36",
|
||||
"8/1b2k3/3rp2R/6P1/2PN4/4p3/2P5/2K5 w - - 1 37",
|
||||
"8/4k3/3rp2R/6P1/2PN4/2P1p3/6b1/2K5 w - - 1 38",
|
||||
"8/4k3/r3p2R/2P3P1/3N4/2P1p3/6b1/2K5 w - - 1 39",
|
||||
"8/3k4/r3p2R/2P2NP1/8/2P1p3/6b1/2K5 w - - 3 40",
|
||||
"8/3k4/4p2R/2P3P1/8/2P1N3/6b1/r1K5 w - - 1 41",
|
||||
"8/3k4/4p2R/2P3P1/8/2P1N3/3K2b1/6r1 w - - 3 42",
|
||||
"8/3k4/4p2R/2P3P1/8/2PKNb2/8/6r1 w - - 5 43",
|
||||
"8/4k3/4p1R1/2P3P1/8/2PKNb2/8/6r1 w - - 7 44",
|
||||
"8/4k3/4p1R1/2P3P1/3K4/2P1N3/8/6rb w - - 9 45",
|
||||
"8/3k4/4p1R1/2P1K1P1/8/2P1N3/8/6rb w - - 11 46",
|
||||
"8/3k4/4p1R1/2P3P1/5K2/2P1N3/8/4r2b w - - 13 47",
|
||||
"8/3k4/2b1p2R/2P3P1/5K2/2P1N3/8/4r3 w - - 15 48",
|
||||
"8/3k4/2b1p3/2P3P1/5K2/2P1N2R/8/6r1 w - - 17 49",
|
||||
"2k5/7R/2b1p3/2P3P1/5K2/2P1N3/8/6r1 w - - 19 50",
|
||||
"2k5/7R/4p3/2P3P1/b1P2K2/4N3/8/6r1 w - - 1 51",
|
||||
"2k5/3bR3/4p3/2P3P1/2P2K2/4N3/8/6r1 w - - 3 52",
|
||||
"3k4/3b2R1/4p3/2P3P1/2P2K2/4N3/8/6r1 w - - 5 53",
|
||||
"3kb3/6R1/4p1P1/2P5/2P2K2/4N3/8/6r1 w - - 1 54",
|
||||
"3kb3/6R1/4p1P1/2P5/2P2KN1/8/8/2r5 w - - 3 55",
|
||||
"3kb3/6R1/4p1P1/2P1N3/2P2K2/8/8/5r2 w - - 5 56",
|
||||
"3kb3/6R1/4p1P1/2P1N3/2P5/4K3/8/4r3 w - - 7 57",
|
||||
},
|
||||
{
|
||||
"rnbq1rk1/ppp1npb1/4p1p1/3P3p/3PP3/2N2N2/PP2BPPP/R1BQ1RK1 b - - 0 8",
|
||||
"rnbq1rk1/ppp1npb1/6p1/3pP2p/3P4/2N2N2/PP2BPPP/R1BQ1RK1 b - - 0 9",
|
||||
"rn1q1rk1/ppp1npb1/6p1/3pP2p/3P2b1/2N2N2/PP2BPPP/R1BQR1K1 b - - 2 10",
|
||||
"r2q1rk1/ppp1npb1/2n3p1/3pP2p/3P2bN/2N5/PP2BPPP/R1BQR1K1 b - - 4 11",
|
||||
"r4rk1/pppqnpb1/2n3p1/3pP2p/3P2bN/2N4P/PP2BPP1/R1BQR1K1 b - - 0 12",
|
||||
"r4rk1/pppqnpb1/2n3p1/3pP2p/3P3N/7P/PP2NPP1/R1BQR1K1 b - - 0 13",
|
||||
"r4rk1/pppq1pb1/2n3p1/3pPN1p/3P4/7P/PP2NPP1/R1BQR1K1 b - - 0 14",
|
||||
"r4rk1/ppp2pb1/2n3p1/3pPq1p/3P1N2/7P/PP3PP1/R1BQR1K1 b - - 1 15",
|
||||
"r4rk1/pppq1pb1/2n3p1/3pP2p/P2P1N2/7P/1P3PP1/R1BQR1K1 b - - 0 16",
|
||||
"r2n1rk1/pppq1pb1/6p1/3pP2p/P2P1N2/R6P/1P3PP1/2BQR1K1 b - - 2 17",
|
||||
"r4rk1/pppq1pb1/4N1p1/3pP2p/P2P4/R6P/1P3PP1/2BQR1K1 b - - 0 18",
|
||||
"r4rk1/ppp2pb1/4q1p1/3pP1Bp/P2P4/R6P/1P3PP1/3QR1K1 b - - 1 19",
|
||||
"r3r1k1/ppp2pb1/4q1p1/3pP1Bp/P2P1P2/R6P/1P4P1/3QR1K1 b - - 0 20",
|
||||
"r3r1k1/ppp3b1/4qpp1/3pP2p/P2P1P1B/R6P/1P4P1/3QR1K1 b - - 1 21",
|
||||
"r3r1k1/ppp3b1/4q1p1/3pP2p/P4P1B/R6P/1P4P1/3QR1K1 b - - 0 22",
|
||||
"r4rk1/ppp3b1/4q1p1/3pP1Bp/P4P2/R6P/1P4P1/3QR1K1 b - - 2 23",
|
||||
"r4rk1/pp4b1/4q1p1/2ppP1Bp/P4P2/3R3P/1P4P1/3QR1K1 b - - 1 24",
|
||||
"r4rk1/pp4b1/4q1p1/2p1P1Bp/P2p1PP1/3R3P/1P6/3QR1K1 b - - 0 25",
|
||||
"r4rk1/pp4b1/4q1p1/2p1P1B1/P2p1PP1/3R4/1P6/3QR1K1 b - - 0 26",
|
||||
"r5k1/pp3rb1/4q1p1/2p1P1B1/P2p1PP1/6R1/1P6/3QR1K1 b - - 2 27",
|
||||
"5rk1/pp3rb1/4q1p1/2p1P1B1/P2pRPP1/6R1/1P6/3Q2K1 b - - 4 28",
|
||||
"5rk1/1p3rb1/p3q1p1/P1p1P1B1/3pRPP1/6R1/1P6/3Q2K1 b - - 0 29",
|
||||
"4r1k1/1p3rb1/p3q1p1/P1p1P1B1/3pRPP1/1P4R1/8/3Q2K1 b - - 0 30",
|
||||
"4r1k1/5rb1/pP2q1p1/2p1P1B1/3pRPP1/1P4R1/8/3Q2K1 b - - 0 31",
|
||||
"4r1k1/5rb1/pq4p1/2p1P1B1/3pRPP1/1P4R1/4Q3/6K1 b - - 1 32",
|
||||
"4r1k1/1r4b1/pq4p1/2p1P1B1/3pRPP1/1P4R1/2Q5/6K1 b - - 3 33",
|
||||
"4r1k1/1r4b1/1q4p1/p1p1P1B1/3p1PP1/1P4R1/2Q5/4R1K1 b - - 1 34",
|
||||
"4r1k1/3r2b1/1q4p1/p1p1P1B1/2Qp1PP1/1P4R1/8/4R1K1 b - - 3 35",
|
||||
"4r1k1/3r2b1/4q1p1/p1p1P1B1/2Qp1PP1/1P4R1/5K2/4R3 b - - 5 36",
|
||||
"4r1k1/3r2b1/6p1/p1p1P1B1/2Pp1PP1/6R1/5K2/4R3 b - - 0 37",
|
||||
"4r1k1/3r2b1/6p1/p1p1P1B1/2P2PP1/3p2R1/5K2/3R4 b - - 1 38",
|
||||
"5rk1/3r2b1/6p1/p1p1P1B1/2P2PP1/3p2R1/8/3RK3 b - - 3 39",
|
||||
"5rk1/6b1/6p1/p1p1P1B1/2Pr1PP1/3R4/8/3RK3 b - - 0 40",
|
||||
"5rk1/3R2b1/6p1/p1p1P1B1/2r2PP1/8/8/3RK3 b - - 1 41",
|
||||
"5rk1/3R2b1/6p1/p1p1P1B1/4rPP1/8/3K4/3R4 b - - 3 42",
|
||||
"1r4k1/3R2b1/6p1/p1p1P1B1/4rPP1/2K5/8/3R4 b - - 5 43",
|
||||
"1r4k1/3R2b1/6p1/p1p1P1B1/2K2PP1/4r3/8/3R4 b - - 7 44",
|
||||
"1r3bk1/8/3R2p1/p1p1P1B1/2K2PP1/4r3/8/3R4 b - - 9 45",
|
||||
"1r3bk1/8/6R1/2p1P1B1/p1K2PP1/4r3/8/3R4 b - - 0 46",
|
||||
"1r3b2/5k2/R7/2p1P1B1/p1K2PP1/4r3/8/3R4 b - - 2 47",
|
||||
"5b2/1r3k2/R7/2p1P1B1/p1K2PP1/4r3/8/7R b - - 4 48",
|
||||
"5b2/5k2/R7/2pKP1B1/pr3PP1/4r3/8/7R b - - 6 49",
|
||||
"5b2/5k2/R1K5/2p1P1B1/p2r1PP1/4r3/8/7R b - - 8 50",
|
||||
"8/R4kb1/2K5/2p1P1B1/p2r1PP1/4r3/8/7R b - - 10 51",
|
||||
"8/R5b1/2K3k1/2p1PPB1/p2r2P1/4r3/8/7R b - - 0 52",
|
||||
"8/6R1/2K5/2p1PPk1/p2r2P1/4r3/8/7R b - - 0 53",
|
||||
"8/6R1/2K5/2p1PP2/p2r1kP1/4r3/8/5R2 b - - 2 54",
|
||||
"8/6R1/2K2P2/2p1P3/p2r2P1/4r1k1/8/5R2 b - - 0 55",
|
||||
"8/5PR1/2K5/2p1P3/p2r2P1/4r3/6k1/5R2 b - - 0 56",
|
||||
},
|
||||
{
|
||||
"rn1qkb1r/p1pbpppp/5n2/8/2pP4/2N5/1PQ1PPPP/R1B1KBNR w KQkq - 0 7",
|
||||
"r2qkb1r/p1pbpppp/2n2n2/8/2pP4/2N2N2/1PQ1PPPP/R1B1KB1R w KQkq - 2 8",
|
||||
"r2qkb1r/p1pbpppp/5n2/8/1npPP3/2N2N2/1PQ2PPP/R1B1KB1R w KQkq - 1 9",
|
||||
"r2qkb1r/p1pb1ppp/4pn2/8/1npPP3/2N2N2/1P3PPP/R1BQKB1R w KQkq - 0 10",
|
||||
"r2qk2r/p1pbbppp/4pn2/8/1nBPP3/2N2N2/1P3PPP/R1BQK2R w KQkq - 1 11",
|
||||
"r2q1rk1/p1pbbppp/4pn2/8/1nBPP3/2N2N2/1P3PPP/R1BQ1RK1 w - - 3 12",
|
||||
"r2q1rk1/2pbbppp/p3pn2/8/1nBPPB2/2N2N2/1P3PPP/R2Q1RK1 w - - 0 13",
|
||||
"r2q1rk1/2p1bppp/p3pn2/1b6/1nBPPB2/2N2N2/1P3PPP/R2QR1K1 w - - 2 14",
|
||||
"r2q1rk1/4bppp/p1p1pn2/1b6/1nBPPB2/1PN2N2/5PPP/R2QR1K1 w - - 0 15",
|
||||
"r4rk1/3qbppp/p1p1pn2/1b6/1nBPPB2/1PN2N2/3Q1PPP/R3R1K1 w - - 2 16",
|
||||
"r4rk1/1q2bppp/p1p1pn2/1b6/1nBPPB2/1PN2N1P/3Q1PP1/R3R1K1 w - - 1 17",
|
||||
"r3r1k1/1q2bppp/p1p1pn2/1b6/1nBPPB2/1PN2N1P/4QPP1/R3R1K1 w - - 3 18",
|
||||
"r3r1k1/1q1nbppp/p1p1p3/1b6/1nBPPB2/1PN2N1P/4QPP1/3RR1K1 w - - 5 19",
|
||||
"r3rbk1/1q1n1ppp/p1p1p3/1b6/1nBPPB2/1PN2N1P/3RQPP1/4R1K1 w - - 7 20",
|
||||
"r3rbk1/1q3ppp/pnp1p3/1b6/1nBPPB2/1PN2N1P/3RQPP1/4R2K w - - 9 21",
|
||||
"2r1rbk1/1q3ppp/pnp1p3/1b6/1nBPPB2/1PN2N1P/3RQPP1/1R5K w - - 11 22",
|
||||
"2r1rbk1/1q4pp/pnp1pp2/1b6/1nBPPB2/1PN2N1P/4QPP1/1R1R3K w - - 0 23",
|
||||
"2r1rbk1/5qpp/pnp1pp2/1b6/1nBPP3/1PN1BN1P/4QPP1/1R1R3K w - - 2 24",
|
||||
"2r1rbk1/5qp1/pnp1pp1p/1b6/1nBPP3/1PN1BN1P/4QPP1/1R1R2K1 w - - 0 25",
|
||||
"2r1rbk1/5qp1/pnp1pp1p/1b6/2BPP3/1P2BN1P/n3QPP1/1R1R2K1 w - - 0 26",
|
||||
"r3rbk1/5qp1/pnp1pp1p/1b6/2BPP3/1P2BN1P/Q4PP1/1R1R2K1 w - - 1 27",
|
||||
"rr3bk1/5qp1/pnp1pp1p/1b6/2BPP3/1P2BN1P/Q4PP1/R2R2K1 w - - 3 28",
|
||||
"rr2qbk1/6p1/pnp1pp1p/1b6/2BPP3/1P2BN1P/4QPP1/R2R2K1 w - - 5 29",
|
||||
"rr2qbk1/6p1/1np1pp1p/pb6/2BPP3/1P1QBN1P/5PP1/R2R2K1 w - - 0 30",
|
||||
"rr2qbk1/6p1/1n2pp1p/pp6/3PP3/1P1QBN1P/5PP1/R2R2K1 w - - 0 31",
|
||||
"rr2qbk1/6p1/1n2pp1p/1p1P4/p3P3/1P1QBN1P/5PP1/R2R2K1 w - - 0 32",
|
||||
"rr2qbk1/3n2p1/3Ppp1p/1p6/p3P3/1P1QBN1P/5PP1/R2R2K1 w - - 1 33",
|
||||
"rr3bk1/3n2p1/3Ppp1p/1p5q/pP2P3/3QBN1P/5PP1/R2R2K1 w - - 1 34",
|
||||
"rr3bk1/3n2p1/3Ppp1p/1p5q/1P2P3/p2QBN1P/5PP1/2RR2K1 w - - 0 35",
|
||||
"1r3bk1/3n2p1/r2Ppp1p/1p5q/1P2P3/pQ2BN1P/5PP1/2RR2K1 w - - 2 36",
|
||||
"1r2qbk1/2Rn2p1/r2Ppp1p/1p6/1P2P3/pQ2BN1P/5PP1/3R2K1 w - - 4 37",
|
||||
"1r2qbk1/2Rn2p1/r2Ppp1p/1pB5/1P2P3/1Q3N1P/p4PP1/3R2K1 w - - 0 38",
|
||||
"1r2q1k1/2Rn2p1/r2bpp1p/1pB5/1P2P3/1Q3N1P/p4PP1/R5K1 w - - 0 39",
|
||||
"1r2q1k1/2Rn2p1/3rpp1p/1p6/1P2P3/1Q3N1P/p4PP1/R5K1 w - - 0 40",
|
||||
"2r1q1k1/2Rn2p1/3rpp1p/1p6/1P2P3/5N1P/Q4PP1/R5K1 w - - 1 41",
|
||||
"1r2q1k1/1R1n2p1/3rpp1p/1p6/1P2P3/5N1P/Q4PP1/R5K1 w - - 3 42",
|
||||
"2r1q1k1/2Rn2p1/3rpp1p/1p6/1P2P3/5N1P/Q4PP1/R5K1 w - - 5 43",
|
||||
"1r2q1k1/1R1n2p1/3rpp1p/1p6/1P2P3/5N1P/Q4PP1/R5K1 w - - 7 44",
|
||||
"1rq3k1/R2n2p1/3rpp1p/1p6/1P2P3/5N1P/Q4PP1/R5K1 w - - 9 45",
|
||||
"2q3k1/Rr1n2p1/3rpp1p/1p6/1P2P3/5N1P/4QPP1/R5K1 w - - 11 46",
|
||||
"Rrq3k1/3n2p1/3rpp1p/1p6/1P2P3/5N1P/4QPP1/R5K1 w - - 13 47",
|
||||
},
|
||||
{
|
||||
"rn1qkb1r/1pp2ppp/p4p2/3p1b2/5P2/1P2PN2/P1PP2PP/RN1QKB1R b KQkq - 1 6",
|
||||
"r2qkb1r/1pp2ppp/p1n2p2/3p1b2/3P1P2/1P2PN2/P1P3PP/RN1QKB1R b KQkq - 0 7",
|
||||
"r2qkb1r/1pp2ppp/p4p2/3p1b2/1n1P1P2/1P1BPN2/P1P3PP/RN1QK2R b KQkq - 2 8",
|
||||
"r2qkb1r/1pp2ppp/p4p2/3p1b2/3P1P2/1P1PPN2/P5PP/RN1QK2R b KQkq - 0 9",
|
||||
"r2qk2r/1pp2ppp/p2b1p2/3p1b2/3P1P2/1PNPPN2/P5PP/R2QK2R b KQkq - 2 10",
|
||||
"r2qk2r/1p3ppp/p1pb1p2/3p1b2/3P1P2/1PNPPN2/P5PP/R2Q1RK1 b kq - 1 11",
|
||||
"r2q1rk1/1p3ppp/p1pb1p2/3p1b2/3P1P2/1PNPPN2/P2Q2PP/R4RK1 b - - 3 12",
|
||||
"r2qr1k1/1p3ppp/p1pb1p2/3p1b2/3P1P2/1P1PPN2/P2QN1PP/R4RK1 b - - 5 13",
|
||||
"r3r1k1/1p3ppp/pqpb1p2/3p1b2/3P1P2/1P1PPNN1/P2Q2PP/R4RK1 b - - 7 14",
|
||||
"r3r1k1/1p3ppp/pqp2p2/3p1b2/1b1P1P2/1P1PPNN1/P1Q3PP/R4RK1 b - - 9 15",
|
||||
"r3r1k1/1p1b1ppp/pqp2p2/3p4/1b1P1P2/1P1PPNN1/P4QPP/R4RK1 b - - 11 16",
|
||||
"2r1r1k1/1p1b1ppp/pqp2p2/3p4/1b1PPP2/1P1P1NN1/P4QPP/R4RK1 b - - 0 17",
|
||||
"2r1r1k1/1p1b1ppp/pq3p2/2pp4/1b1PPP2/PP1P1NN1/5QPP/R4RK1 b - - 0 18",
|
||||
"2r1r1k1/1p1b1ppp/pq3p2/2Pp4/4PP2/PPbP1NN1/5QPP/R4RK1 b - - 0 19",
|
||||
"2r1r1k1/1p1b1ppp/p4p2/2Pp4/4PP2/PqbP1NN1/5QPP/RR4K1 b - - 1 20",
|
||||
"2r1r1k1/1p1b1ppp/p4p2/2Pp4/q3PP2/P1bP1NN1/R4QPP/1R4K1 b - - 3 21",
|
||||
"2r1r1k1/1p3ppp/p4p2/1bPP4/q4P2/P1bP1NN1/R4QPP/1R4K1 b - - 0 22",
|
||||
"2r1r1k1/1p3ppp/p4p2/2PP4/q4P2/P1bb1NN1/R4QPP/2R3K1 b - - 1 23",
|
||||
"2r1r1k1/1p3ppp/p2P1p2/2P5/2q2P2/P1bb1NN1/R4QPP/2R3K1 b - - 0 24",
|
||||
"2rr2k1/1p3ppp/p2P1p2/2P5/2q2P2/P1bb1NN1/R4QPP/2R4K b - - 2 25",
|
||||
"2rr2k1/1p3ppp/p2P1p2/2Q5/5P2/P1bb1NN1/R5PP/2R4K b - - 0 26",
|
||||
"3r2k1/1p3ppp/p2P1p2/2r5/5P2/P1bb1N2/R3N1PP/2R4K b - - 1 27",
|
||||
"3r2k1/1p3ppp/p2P1p2/2r5/5P2/P1b2N2/4R1PP/2R4K b - - 0 28",
|
||||
"3r2k1/1p3ppp/p2P1p2/2r5/1b3P2/P4N2/4R1PP/3R3K b - - 2 29",
|
||||
"3r2k1/1p2Rppp/p2P1p2/b1r5/5P2/P4N2/6PP/3R3K b - - 4 30",
|
||||
"3r2k1/1R3ppp/p1rP1p2/b7/5P2/P4N2/6PP/3R3K b - - 0 31",
|
||||
"3r2k1/1R3ppp/p2R1p2/b7/5P2/P4N2/6PP/7K b - - 0 32",
|
||||
"6k1/1R3ppp/p2r1p2/b7/5P2/P4NP1/7P/7K b - - 0 33",
|
||||
"6k1/1R3p1p/p2r1pp1/b7/5P1P/P4NP1/8/7K b - - 0 34",
|
||||
"6k1/3R1p1p/pr3pp1/b7/5P1P/P4NP1/8/7K b - - 2 35",
|
||||
"6k1/5p2/pr3pp1/b2R3p/5P1P/P4NP1/8/7K b - - 1 36",
|
||||
"6k1/5p2/pr3pp1/7p/5P1P/P1bR1NP1/8/7K b - - 3 37",
|
||||
"6k1/5p2/p1r2pp1/7p/5P1P/P1bR1NP1/6K1/8 b - - 5 38",
|
||||
"6k1/5p2/p1r2pp1/b2R3p/5P1P/P4NP1/6K1/8 b - - 7 39",
|
||||
"6k1/5p2/p4pp1/b2R3p/5P1P/P4NPK/2r5/8 b - - 9 40",
|
||||
"6k1/2b2p2/p4pp1/7p/5P1P/P2R1NPK/2r5/8 b - - 11 41",
|
||||
"6k1/2b2p2/5pp1/p6p/3N1P1P/P2R2PK/2r5/8 b - - 1 42",
|
||||
"6k1/2b2p2/5pp1/p6p/3N1P1P/P1R3PK/r7/8 b - - 3 43",
|
||||
"6k1/5p2/1b3pp1/p6p/5P1P/P1R3PK/r1N5/8 b - - 5 44",
|
||||
"8/5pk1/1bR2pp1/p6p/5P1P/P5PK/r1N5/8 b - - 7 45",
|
||||
"3b4/5pk1/2R2pp1/p4P1p/7P/P5PK/r1N5/8 b - - 0 46",
|
||||
"8/4bpk1/2R2pp1/p4P1p/6PP/P6K/r1N5/8 b - - 0 47",
|
||||
"8/5pk1/2R2pP1/p6p/6PP/b6K/r1N5/8 b - - 0 48",
|
||||
"8/6k1/2R2pp1/p6P/7P/b6K/r1N5/8 b - - 0 49",
|
||||
"8/6k1/2R2p2/p6p/7P/b5K1/r1N5/8 b - - 1 50",
|
||||
"8/8/2R2pk1/p6p/7P/b4K2/r1N5/8 b - - 3 51",
|
||||
"8/8/2R2pk1/p6p/7P/4NK2/rb6/8 b - - 5 52",
|
||||
"2R5/8/5pk1/7p/p6P/4NK2/rb6/8 b - - 1 53",
|
||||
"6R1/8/5pk1/7p/p6P/4NK2/1b6/r7 b - - 3 54",
|
||||
"R7/5k2/5p2/7p/p6P/4NK2/1b6/r7 b - - 5 55",
|
||||
"R7/5k2/5p2/7p/7P/p3N3/1b2K3/r7 b - - 1 56",
|
||||
"8/R4k2/5p2/7p/7P/p3N3/1b2K3/7r b - - 3 57",
|
||||
"8/8/5pk1/7p/R6P/p3N3/1b2K3/7r b - - 5 58",
|
||||
"8/8/5pk1/7p/R6P/p7/4K3/2bN3r b - - 7 59",
|
||||
"8/8/5pk1/7p/R6P/p7/4KN1r/2b5 b - - 9 60",
|
||||
"8/8/5pk1/7p/R6P/p3K3/1b3N1r/8 b - - 11 61",
|
||||
"8/8/R4pk1/7p/7P/p1b1K3/5N1r/8 b - - 13 62",
|
||||
"8/8/5pk1/7p/7P/2b1K3/R4N1r/8 b - - 0 63",
|
||||
"8/8/5pk1/7p/3K3P/8/R4N1r/4b3 b - - 2 64",
|
||||
}
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Stockfish::Benchmark {
|
||||
@@ -160,4 +437,76 @@ std::vector<std::string> setup_bench(const std::string& currentFen, std::istream
|
||||
return list;
|
||||
}
|
||||
|
||||
BenchmarkSetup setup_benchmark(std::istream& is) {
|
||||
// TT_SIZE_PER_THREAD is chosen such that roughly half of the hash is used all positions
|
||||
// for the current sequence have been searched.
|
||||
static constexpr int TT_SIZE_PER_THREAD = 128;
|
||||
|
||||
static constexpr int DEFAULT_DURATION_S = 150;
|
||||
|
||||
BenchmarkSetup setup{};
|
||||
|
||||
// Assign default values to missing arguments
|
||||
int desiredTimeS;
|
||||
|
||||
if (!(is >> setup.threads))
|
||||
setup.threads = get_hardware_concurrency();
|
||||
else
|
||||
setup.originalInvocation += std::to_string(setup.threads);
|
||||
|
||||
if (!(is >> setup.ttSize))
|
||||
setup.ttSize = TT_SIZE_PER_THREAD * setup.threads;
|
||||
else
|
||||
setup.originalInvocation += " " + std::to_string(setup.ttSize);
|
||||
|
||||
if (!(is >> desiredTimeS))
|
||||
desiredTimeS = DEFAULT_DURATION_S;
|
||||
else
|
||||
setup.originalInvocation += " " + std::to_string(desiredTimeS);
|
||||
|
||||
setup.filledInvocation += std::to_string(setup.threads) + " " + std::to_string(setup.ttSize)
|
||||
+ " " + std::to_string(desiredTimeS);
|
||||
|
||||
auto getCorrectedTime = [&](int ply) {
|
||||
// time per move is fit roughly based on LTC games
|
||||
// seconds = 50/{ply+15}
|
||||
// ms = 50000/{ply+15}
|
||||
// with this fit 10th move gets 2000ms
|
||||
// adjust for desired 10th move time
|
||||
return 50000.0 / (static_cast<double>(ply) + 15.0);
|
||||
};
|
||||
|
||||
float totalTime = 0;
|
||||
for (const auto& game : BenchmarkPositions)
|
||||
{
|
||||
setup.commands.emplace_back("ucinewgame");
|
||||
int ply = 1;
|
||||
for (int i = 0; i < static_cast<int>(game.size()); ++i)
|
||||
{
|
||||
const float correctedTime = getCorrectedTime(ply);
|
||||
totalTime += correctedTime;
|
||||
ply += 1;
|
||||
}
|
||||
}
|
||||
|
||||
float timeScaleFactor = static_cast<float>(desiredTimeS * 1000) / totalTime;
|
||||
|
||||
for (const auto& game : BenchmarkPositions)
|
||||
{
|
||||
setup.commands.emplace_back("ucinewgame");
|
||||
int ply = 1;
|
||||
for (const std::string& fen : game)
|
||||
{
|
||||
setup.commands.emplace_back("position fen " + fen);
|
||||
|
||||
const int correctedTime = static_cast<int>(getCorrectedTime(ply) * timeScaleFactor);
|
||||
setup.commands.emplace_back("go movetime " + std::to_string(correctedTime));
|
||||
|
||||
ply += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return setup;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
+11
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -27,6 +27,16 @@ namespace Stockfish::Benchmark {
|
||||
|
||||
std::vector<std::string> setup_bench(const std::string&, std::istream&);
|
||||
|
||||
struct BenchmarkSetup {
|
||||
int ttSize;
|
||||
int threads;
|
||||
std::vector<std::string> commands;
|
||||
std::string originalInvocation;
|
||||
std::string filledInvocation;
|
||||
};
|
||||
|
||||
BenchmarkSetup setup_benchmark(std::istream&);
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef BENCHMARK_H_INCLUDED
|
||||
|
||||
+26
-20
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -34,15 +34,14 @@ Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
||||
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||
|
||||
Magic RookMagics[SQUARE_NB];
|
||||
Magic BishopMagics[SQUARE_NB];
|
||||
alignas(64) Magic Magics[SQUARE_NB][2];
|
||||
|
||||
namespace {
|
||||
|
||||
Bitboard RookTable[0x19000]; // To store rook attacks
|
||||
Bitboard BishopTable[0x1480]; // To store bishop attacks
|
||||
|
||||
void init_magics(PieceType pt, Bitboard table[], Magic magics[]);
|
||||
void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]);
|
||||
|
||||
// Returns the bitboard of target square for the given step
|
||||
// from the given square. If the step is off the board, returns empty bitboard.
|
||||
@@ -82,8 +81,8 @@ void Bitboards::init() {
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
|
||||
|
||||
init_magics(ROOK, RookTable, RookMagics);
|
||||
init_magics(BISHOP, BishopTable, BishopMagics);
|
||||
init_magics(ROOK, RookTable, Magics);
|
||||
init_magics(BISHOP, BishopTable, Magics);
|
||||
|
||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||
{
|
||||
@@ -140,41 +139,49 @@ Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
|
||||
|
||||
// Computes all rook and bishop attacks at startup. Magic
|
||||
// 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
|
||||
// called "fancy" approach.
|
||||
void init_magics(PieceType pt, Bitboard table[], Magic magics[]) {
|
||||
// https://www.chessprogramming.org/Magic_Bitboards. In particular, here we use
|
||||
// the so called "fancy" approach.
|
||||
void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]) {
|
||||
|
||||
#ifndef USE_PEXT
|
||||
// Optimal PRNG seeds to pick the correct magics in the shortest time
|
||||
int seeds[][RANK_NB] = {{8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020},
|
||||
{728, 10316, 55013, 32803, 12281, 15100, 16645, 255}};
|
||||
|
||||
Bitboard occupancy[4096], reference[4096], edges, b;
|
||||
int epoch[4096] = {}, cnt = 0, size = 0;
|
||||
Bitboard occupancy[4096];
|
||||
int epoch[4096] = {}, cnt = 0;
|
||||
#endif
|
||||
Bitboard reference[4096];
|
||||
int size = 0;
|
||||
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
{
|
||||
// Board edges are not considered in the relevant occupancies
|
||||
edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
|
||||
Bitboard edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
|
||||
|
||||
// Given a square 's', the mask is the bitboard of sliding attacks from
|
||||
// 's' computed on an empty board. The index must be big enough to contain
|
||||
// all the attacks for each possible subset of the mask and so is 2 power
|
||||
// the number of 1s of the mask. Hence we deduce the size of the shift to
|
||||
// apply to the 64 or 32 bits word to get the index.
|
||||
Magic& m = magics[s];
|
||||
Magic& m = magics[s][pt - BISHOP];
|
||||
m.mask = sliding_attack(pt, s, 0) & ~edges;
|
||||
m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
|
||||
|
||||
#ifndef USE_PEXT
|
||||
m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
|
||||
#endif
|
||||
// Set the offset for the attacks table of the square. We have individual
|
||||
// table sizes for each square with "Fancy Magic Bitboards".
|
||||
m.attacks = s == SQ_A1 ? table : magics[s - 1].attacks + size;
|
||||
m.attacks = s == SQ_A1 ? table : magics[s - 1][pt - BISHOP].attacks + size;
|
||||
size = 0;
|
||||
|
||||
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
|
||||
// store the corresponding sliding attack bitboard in reference[].
|
||||
b = size = 0;
|
||||
Bitboard b = 0;
|
||||
do
|
||||
{
|
||||
#ifndef USE_PEXT
|
||||
occupancy[size] = b;
|
||||
#endif
|
||||
reference[size] = sliding_attack(pt, s, b);
|
||||
|
||||
if (HasPext)
|
||||
@@ -184,9 +191,7 @@ void init_magics(PieceType pt, Bitboard table[], Magic magics[]) {
|
||||
b = (b - m.mask) & m.mask;
|
||||
} while (b);
|
||||
|
||||
if (HasPext)
|
||||
continue;
|
||||
|
||||
#ifndef USE_PEXT
|
||||
PRNG rng(seeds[Is64Bit][rank_of(s)]);
|
||||
|
||||
// Find a magic for square 's' picking up an (almost) random number
|
||||
@@ -215,6 +220,7 @@ void init_magics(PieceType pt, Bitboard table[], Magic magics[]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
-15
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
@@ -67,27 +68,31 @@ extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||
// Magic holds all magic bitboards relevant data for a single square
|
||||
struct Magic {
|
||||
Bitboard mask;
|
||||
Bitboard magic;
|
||||
Bitboard* attacks;
|
||||
unsigned shift;
|
||||
#ifndef USE_PEXT
|
||||
Bitboard magic;
|
||||
unsigned shift;
|
||||
#endif
|
||||
|
||||
// Compute the attack's index using the 'magic bitboards' approach
|
||||
unsigned index(Bitboard occupied) const {
|
||||
|
||||
if (HasPext)
|
||||
return unsigned(pext(occupied, mask));
|
||||
|
||||
#ifdef USE_PEXT
|
||||
return unsigned(pext(occupied, mask));
|
||||
#else
|
||||
if (Is64Bit)
|
||||
return unsigned(((occupied & mask) * magic) >> shift);
|
||||
|
||||
unsigned lo = unsigned(occupied) & unsigned(mask);
|
||||
unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32);
|
||||
return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift;
|
||||
#endif
|
||||
}
|
||||
|
||||
Bitboard attacks_bb(Bitboard occupied) const { return attacks[index(occupied)]; }
|
||||
};
|
||||
|
||||
extern Magic RookMagics[SQUARE_NB];
|
||||
extern Magic BishopMagics[SQUARE_NB];
|
||||
extern Magic Magics[SQUARE_NB][2];
|
||||
|
||||
constexpr Bitboard square_bb(Square s) {
|
||||
assert(is_ok(s));
|
||||
@@ -229,9 +234,8 @@ inline Bitboard attacks_bb(Square s, Bitboard occupied) {
|
||||
switch (Pt)
|
||||
{
|
||||
case BISHOP :
|
||||
return BishopMagics[s].attacks[BishopMagics[s].index(occupied)];
|
||||
case ROOK :
|
||||
return RookMagics[s].attacks[RookMagics[s].index(occupied)];
|
||||
return Magics[s][Pt - BISHOP].attacks_bb(occupied);
|
||||
case QUEEN :
|
||||
return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
|
||||
default :
|
||||
@@ -265,11 +269,10 @@ inline int popcount(Bitboard b) {
|
||||
|
||||
#ifndef USE_POPCNT
|
||||
|
||||
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]];
|
||||
std::uint16_t indices[4];
|
||||
std::memcpy(indices, &b, sizeof(b));
|
||||
return PopCnt16[indices[0]] + PopCnt16[indices[1]] + PopCnt16[indices[2]]
|
||||
+ PopCnt16[indices[3]];
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
|
||||
+99
-65
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -47,8 +47,8 @@ namespace NN = Eval::NNUE;
|
||||
constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048;
|
||||
|
||||
Engine::Engine(std::string path) :
|
||||
binaryDirectory(CommandLine::get_binary_directory(path)),
|
||||
Engine::Engine(std::optional<std::string> path) :
|
||||
binaryDirectory(path ? CommandLine::get_binary_directory(*path) : ""),
|
||||
numaContext(NumaConfig::from_system()),
|
||||
states(new std::deque<StateInfo>(1)),
|
||||
threads(),
|
||||
@@ -58,58 +58,84 @@ Engine::Engine(std::string path) :
|
||||
NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG),
|
||||
NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) {
|
||||
pos.set(StartFEN, false, &states->back());
|
||||
capSq = SQ_NONE;
|
||||
|
||||
options["Debug Log File"] << Option("", [](const Option& o) {
|
||||
start_logger(o);
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
options["NumaPolicy"] << Option("auto", [this](const Option& o) {
|
||||
set_numa_config_from_option(o);
|
||||
return numa_config_information_as_string() + "\n" + thread_binding_information_as_string();
|
||||
});
|
||||
options.add( //
|
||||
"Debug Log File", Option("", [](const Option& o) {
|
||||
start_logger(o);
|
||||
return std::nullopt;
|
||||
}));
|
||||
|
||||
options["Threads"] << Option(1, 1, 1024, [this](const Option&) {
|
||||
resize_threads();
|
||||
return thread_binding_information_as_string();
|
||||
});
|
||||
options.add( //
|
||||
"NumaPolicy", Option("auto", [this](const Option& o) {
|
||||
set_numa_config_from_option(o);
|
||||
return numa_config_information_as_string() + "\n"
|
||||
+ thread_allocation_information_as_string();
|
||||
}));
|
||||
|
||||
options["Hash"] << Option(16, 1, MaxHashMB, [this](const Option& o) {
|
||||
set_tt_size(o);
|
||||
return std::nullopt;
|
||||
});
|
||||
options.add( //
|
||||
"Threads", Option(1, 1, 1024, [this](const Option&) {
|
||||
resize_threads();
|
||||
return thread_allocation_information_as_string();
|
||||
}));
|
||||
|
||||
options["Clear Hash"] << Option([this](const Option&) {
|
||||
search_clear();
|
||||
return std::nullopt;
|
||||
});
|
||||
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(Stockfish::Search::Skill::LowestElo,
|
||||
Stockfish::Search::Skill::LowestElo,
|
||||
Stockfish::Search::Skill::HighestElo);
|
||||
options["UCI_ShowWDL"] << Option(false);
|
||||
options["SyzygyPath"] << Option("", [](const Option& o) {
|
||||
Tablebases::init(o);
|
||||
return std::nullopt;
|
||||
});
|
||||
options["SyzygyProbeDepth"] << Option(1, 1, 100);
|
||||
options["Syzygy50MoveRule"] << Option(true);
|
||||
options["SyzygyProbeLimit"] << Option(7, 0, 7);
|
||||
options["EvalFile"] << Option(EvalFileDefaultNameBig, [this](const Option& o) {
|
||||
load_big_network(o);
|
||||
return std::nullopt;
|
||||
});
|
||||
options["EvalFileSmall"] << Option(EvalFileDefaultNameSmall, [this](const Option& o) {
|
||||
load_small_network(o);
|
||||
return std::nullopt;
|
||||
});
|
||||
options.add( //
|
||||
"Hash", Option(16, 1, MaxHashMB, [this](const Option& o) {
|
||||
set_tt_size(o);
|
||||
return std::nullopt;
|
||||
}));
|
||||
|
||||
options.add( //
|
||||
"Clear Hash", Option([this](const Option&) {
|
||||
search_clear();
|
||||
return std::nullopt;
|
||||
}));
|
||||
|
||||
options.add( //
|
||||
"Ponder", Option(false));
|
||||
|
||||
options.add( //
|
||||
"MultiPV", Option(1, 1, MAX_MOVES));
|
||||
|
||||
options.add("Skill Level", Option(20, 0, 20));
|
||||
|
||||
options.add("Move Overhead", Option(10, 0, 5000));
|
||||
|
||||
options.add("nodestime", Option(0, 0, 10000));
|
||||
|
||||
options.add("UCI_Chess960", Option(false));
|
||||
|
||||
options.add("UCI_LimitStrength", Option(false));
|
||||
|
||||
options.add("UCI_Elo",
|
||||
Option(Stockfish::Search::Skill::LowestElo, Stockfish::Search::Skill::LowestElo,
|
||||
Stockfish::Search::Skill::HighestElo));
|
||||
|
||||
options.add("UCI_ShowWDL", Option(false));
|
||||
|
||||
options.add( //
|
||||
"SyzygyPath", Option("", [](const Option& o) {
|
||||
Tablebases::init(o);
|
||||
return std::nullopt;
|
||||
}));
|
||||
|
||||
options.add("SyzygyProbeDepth", Option(1, 1, 100));
|
||||
|
||||
options.add("Syzygy50MoveRule", Option(true));
|
||||
|
||||
options.add("SyzygyProbeLimit", Option(7, 0, 7));
|
||||
|
||||
options.add( //
|
||||
"EvalFile", Option(EvalFileDefaultNameBig, [this](const Option& o) {
|
||||
load_big_network(o);
|
||||
return std::nullopt;
|
||||
}));
|
||||
|
||||
options.add( //
|
||||
"EvalFileSmall", Option(EvalFileDefaultNameSmall, [this](const Option& o) {
|
||||
load_small_network(o);
|
||||
return std::nullopt;
|
||||
}));
|
||||
|
||||
load_networks();
|
||||
resize_threads();
|
||||
@@ -124,7 +150,6 @@ std::uint64_t Engine::perft(const std::string& fen, Depth depth, bool isChess960
|
||||
void Engine::go(Search::LimitsType& limits) {
|
||||
assert(limits.perft == 0);
|
||||
verify_networks();
|
||||
limits.capSq = capSq;
|
||||
|
||||
threads.start_thinking(options, pos, states, limits);
|
||||
}
|
||||
@@ -156,6 +181,10 @@ void Engine::set_on_bestmove(std::function<void(std::string_view, std::string_vi
|
||||
updateContext.onBestmove = std::move(f);
|
||||
}
|
||||
|
||||
void Engine::set_on_verify_networks(std::function<void(std::string_view)>&& f) {
|
||||
onVerifyNetworks = std::move(f);
|
||||
}
|
||||
|
||||
void Engine::wait_for_search_finished() { threads.main_thread()->wait_for_search_finished(); }
|
||||
|
||||
void Engine::set_position(const std::string& fen, const std::vector<std::string>& moves) {
|
||||
@@ -163,7 +192,6 @@ void Engine::set_position(const std::string& fen, const std::vector<std::string>
|
||||
states = StateListPtr(new std::deque<StateInfo>(1));
|
||||
pos.set(fen, options["UCI_Chess960"], &states->back());
|
||||
|
||||
capSq = SQ_NONE;
|
||||
for (const auto& move : moves)
|
||||
{
|
||||
auto m = UCIEngine::to_move(pos, move);
|
||||
@@ -173,11 +201,6 @@ void Engine::set_position(const std::string& fen, const std::vector<std::string>
|
||||
|
||||
states->emplace_back();
|
||||
pos.do_move(m, states->back());
|
||||
|
||||
capSq = SQ_NONE;
|
||||
DirtyPiece& dp = states->back().dirtyPiece;
|
||||
if (dp.dirty_num > 1 && dp.to[1] == SQ_NONE)
|
||||
capSq = m.to_sq();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,8 +249,8 @@ void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; }
|
||||
// network related
|
||||
|
||||
void Engine::verify_networks() const {
|
||||
networks->big.verify(options["EvalFile"]);
|
||||
networks->small.verify(options["EvalFileSmall"]);
|
||||
networks->big.verify(options["EvalFile"], onVerifyNetworks);
|
||||
networks->small.verify(options["EvalFileSmall"], onVerifyNetworks);
|
||||
}
|
||||
|
||||
void Engine::load_networks() {
|
||||
@@ -285,6 +308,8 @@ std::string Engine::visualize() const {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
int Engine::get_hashfull(int maxAge) const { return tt.hashfull(maxAge); }
|
||||
|
||||
std::vector<std::pair<size_t, size_t>> Engine::get_bound_thread_count_by_numa_node() const {
|
||||
auto counts = threads.get_bound_thread_count_by_numa_node();
|
||||
const NumaConfig& cfg = numaContext.get_numa_config();
|
||||
@@ -310,15 +335,9 @@ std::string Engine::numa_config_information_as_string() const {
|
||||
std::string Engine::thread_binding_information_as_string() const {
|
||||
auto boundThreadsByNode = get_bound_thread_count_by_numa_node();
|
||||
std::stringstream ss;
|
||||
|
||||
size_t threadsSize = threads.size();
|
||||
ss << "Using " << threadsSize << (threadsSize > 1 ? " threads" : " thread");
|
||||
|
||||
if (boundThreadsByNode.empty())
|
||||
return ss.str();
|
||||
|
||||
ss << " with NUMA node thread binding: ";
|
||||
|
||||
bool isFirst = true;
|
||||
|
||||
for (auto&& [current, total] : boundThreadsByNode)
|
||||
@@ -332,4 +351,19 @@ std::string Engine::thread_binding_information_as_string() const {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string Engine::thread_allocation_information_as_string() const {
|
||||
std::stringstream ss;
|
||||
|
||||
size_t threadsSize = threads.size();
|
||||
ss << "Using " << threadsSize << (threadsSize > 1 ? " threads" : " thread");
|
||||
|
||||
auto boundThreadsByNodeStr = thread_binding_information_as_string();
|
||||
if (boundThreadsByNodeStr.empty())
|
||||
return ss.str();
|
||||
|
||||
ss << " with NUMA node thread binding: ";
|
||||
ss << boundThreadsByNodeStr;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
+8
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -39,15 +39,13 @@
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
enum Square : int;
|
||||
|
||||
class Engine {
|
||||
public:
|
||||
using InfoShort = Search::InfoShort;
|
||||
using InfoFull = Search::InfoFull;
|
||||
using InfoIter = Search::InfoIteration;
|
||||
|
||||
Engine(std::string path = "");
|
||||
Engine(std::optional<std::string> path = std::nullopt);
|
||||
|
||||
// Cannot be movable due to components holding backreferences to fields
|
||||
Engine(const Engine&) = delete;
|
||||
@@ -81,6 +79,7 @@ class Engine {
|
||||
void set_on_update_full(std::function<void(const InfoFull&)>&&);
|
||||
void set_on_iter(std::function<void(const InfoIter&)>&&);
|
||||
void set_on_bestmove(std::function<void(std::string_view, std::string_view)>&&);
|
||||
void set_on_verify_networks(std::function<void(std::string_view)>&&);
|
||||
|
||||
// network related
|
||||
|
||||
@@ -97,12 +96,15 @@ class Engine {
|
||||
const OptionsMap& get_options() const;
|
||||
OptionsMap& get_options();
|
||||
|
||||
int get_hashfull(int maxAge = 0) const;
|
||||
|
||||
std::string fen() const;
|
||||
void flip();
|
||||
std::string visualize() const;
|
||||
std::vector<std::pair<size_t, size_t>> get_bound_thread_count_by_numa_node() const;
|
||||
std::string get_numa_config_as_string() const;
|
||||
std::string numa_config_information_as_string() const;
|
||||
std::string thread_allocation_information_as_string() const;
|
||||
std::string thread_binding_information_as_string() const;
|
||||
|
||||
private:
|
||||
@@ -112,14 +114,14 @@ class Engine {
|
||||
|
||||
Position pos;
|
||||
StateListPtr states;
|
||||
Square capSq;
|
||||
|
||||
OptionsMap options;
|
||||
ThreadPool threads;
|
||||
TranspositionTable tt;
|
||||
LazyNumaReplicated<Eval::NNUE::Networks> networks;
|
||||
|
||||
Search::SearchManager::UpdateContext updateContext;
|
||||
Search::SearchManager::UpdateContext updateContext;
|
||||
std::function<void(std::string_view)> onVerifyNetworks;
|
||||
};
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+7
-12
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -59,16 +59,14 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks,
|
||||
|
||||
assert(!pos.checkers());
|
||||
|
||||
bool smallNet = use_smallnet(pos);
|
||||
int v;
|
||||
|
||||
bool smallNet = use_smallnet(pos);
|
||||
auto [psqt, positional] = smallNet ? networks.small.evaluate(pos, &caches.small)
|
||||
: networks.big.evaluate(pos, &caches.big);
|
||||
|
||||
Value nnue = (125 * psqt + 131 * positional) / 128;
|
||||
|
||||
// Re-evaluate the position when higher eval accuracy is worth the time spent
|
||||
if (smallNet && (nnue * psqt < 0 || std::abs(nnue) < 227))
|
||||
if (smallNet && (std::abs(nnue) < 236))
|
||||
{
|
||||
std::tie(psqt, positional) = networks.big.evaluate(pos, &caches.big);
|
||||
nnue = (125 * psqt + 131 * positional) / 128;
|
||||
@@ -77,14 +75,11 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks,
|
||||
|
||||
// Blend optimism and eval with nnue complexity
|
||||
int nnueComplexity = std::abs(psqt - positional);
|
||||
optimism += optimism * nnueComplexity / (smallNet ? 433 : 453);
|
||||
nnue -= nnue * nnueComplexity / (smallNet ? 18815 : 17864);
|
||||
optimism += optimism * nnueComplexity / 468;
|
||||
nnue -= nnue * nnueComplexity / (smallNet ? 20233 : 17879);
|
||||
|
||||
int material = (smallNet ? 553 : 532) * pos.count<PAWN>() + pos.non_pawn_material();
|
||||
v = (nnue * (73921 + material) + optimism * (8112 + material)) / (smallNet ? 68104 : 74715);
|
||||
|
||||
// Evaluation grain (to get more alpha-beta cuts) with randomization (for robustness)
|
||||
v = (v / 16) * 16 - 1 + (pos.key() & 0x2);
|
||||
int material = 535 * pos.count<PAWN>() + pos.non_pawn_material();
|
||||
int v = (nnue * (77777 + material) + optimism * (7777 + material)) / 77777;
|
||||
|
||||
// Damp down the evaluation linearly when shuffling
|
||||
v -= v * pos.rule50_count() / 212;
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -33,7 +33,7 @@ namespace Eval {
|
||||
// for the build process (profile-build and fishtest) to work. Do not change the
|
||||
// name of the macro or the location where this macro is defined, as it is used
|
||||
// in the Makefile/Fishtest.
|
||||
#define EvalFileDefaultNameBig "nn-1111cefa1111.nnue"
|
||||
#define EvalFileDefaultNameBig "nn-1c0000000000.nnue"
|
||||
#define EvalFileDefaultNameSmall "nn-37f18f62d772.nnue"
|
||||
|
||||
namespace NNUE {
|
||||
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2025 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 HISTORY_H_INCLUDED
|
||||
#define HISTORY_H_INCLUDED
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <type_traits> // IWYU pragma: keep
|
||||
|
||||
#include "misc.h"
|
||||
#include "position.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2
|
||||
constexpr int CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2
|
||||
constexpr int CORRECTION_HISTORY_LIMIT = 1024;
|
||||
constexpr int LOW_PLY_HISTORY_SIZE = 4;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
inline int minor_piece_index(const Position& pos) {
|
||||
return pos.minor_piece_key() & (CORRECTION_HISTORY_SIZE - 1);
|
||||
}
|
||||
|
||||
template<Color c>
|
||||
inline int non_pawn_index(const Position& pos) {
|
||||
return pos.non_pawn_key(c) & (CORRECTION_HISTORY_SIZE - 1);
|
||||
}
|
||||
|
||||
// StatsEntry is the container of various numerical statistics. We use a class
|
||||
// instead of a naked value to directly call history update operator<<() on
|
||||
// the entry. 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 values with the << operator
|
||||
template<typename T, int D>
|
||||
class StatsEntry {
|
||||
|
||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||
static_assert(D <= std::numeric_limits<T>::max(), "D overflows T");
|
||||
|
||||
T entry;
|
||||
|
||||
public:
|
||||
StatsEntry& operator=(const T& v) {
|
||||
entry = v;
|
||||
return *this;
|
||||
}
|
||||
operator const T&() const { return entry; }
|
||||
|
||||
void operator<<(int bonus) {
|
||||
// Make sure that bonus is in range [-D, D]
|
||||
int clampedBonus = std::clamp(bonus, -D, D);
|
||||
entry += clampedBonus - entry * std::abs(clampedBonus) / D;
|
||||
|
||||
assert(std::abs(entry) <= D);
|
||||
}
|
||||
};
|
||||
|
||||
enum StatsType {
|
||||
NoCaptures,
|
||||
Captures
|
||||
};
|
||||
|
||||
template<typename T, int D, std::size_t... Sizes>
|
||||
using Stats = MultiArray<StatsEntry<T, D>, Sizes...>;
|
||||
|
||||
// ButterflyHistory records how often quiet moves have been successful or unsuccessful
|
||||
// during the current search, and is used for reduction and move ordering decisions.
|
||||
// It uses 2 tables (one for each color) indexed by the move's from and to squares,
|
||||
// see https://www.chessprogramming.org/Butterfly_Boards (~11 elo)
|
||||
using ButterflyHistory = Stats<std::int16_t, 7183, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)>;
|
||||
|
||||
// LowPlyHistory is adressed by play and move's from and to squares, used
|
||||
// to improve move ordering near the root
|
||||
using LowPlyHistory =
|
||||
Stats<std::int16_t, 7183, LOW_PLY_HISTORY_SIZE, int(SQUARE_NB) * int(SQUARE_NB)>;
|
||||
|
||||
// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type]
|
||||
using CapturePieceToHistory = Stats<std::int16_t, 10692, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB>;
|
||||
|
||||
// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to]
|
||||
using PieceToHistory = Stats<std::int16_t, 30000, PIECE_NB, SQUARE_NB>;
|
||||
|
||||
// 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
|
||||
// PieceToHistory instead of ButterflyBoards.
|
||||
// (~63 elo)
|
||||
using ContinuationHistory = MultiArray<PieceToHistory, PIECE_NB, SQUARE_NB>;
|
||||
|
||||
// PawnHistory is addressed by the pawn structure and a move's [piece][to]
|
||||
using PawnHistory = Stats<std::int16_t, 8192, PAWN_HISTORY_SIZE, PIECE_NB, SQUARE_NB>;
|
||||
|
||||
// Correction histories record differences between the static evaluation of
|
||||
// positions and their search score. It is used to improve the static evaluation
|
||||
// used by some search heuristics.
|
||||
// see https://www.chessprogramming.org/Static_Evaluation_Correction_History
|
||||
enum CorrHistType {
|
||||
Pawn, // By color and pawn structure
|
||||
Minor, // By color and positions of minor pieces (Knight, Bishop)
|
||||
NonPawn, // By non-pawn material positions and color
|
||||
PieceTo, // By [piece][to] move
|
||||
Continuation, // Combined history of move pairs
|
||||
};
|
||||
|
||||
namespace Detail {
|
||||
|
||||
template<CorrHistType>
|
||||
struct CorrHistTypedef {
|
||||
using type = Stats<std::int16_t, CORRECTION_HISTORY_LIMIT, CORRECTION_HISTORY_SIZE, COLOR_NB>;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct CorrHistTypedef<PieceTo> {
|
||||
using type = Stats<std::int16_t, CORRECTION_HISTORY_LIMIT, PIECE_NB, SQUARE_NB>;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct CorrHistTypedef<Continuation> {
|
||||
using type = MultiArray<CorrHistTypedef<PieceTo>::type, PIECE_NB, SQUARE_NB>;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<CorrHistType T>
|
||||
using CorrectionHistory = typename Detail::CorrHistTypedef<T>::type;
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef HISTORY_H_INCLUDED
|
||||
+167
-59
@@ -3,8 +3,8 @@
|
||||
* @author Dale Weiler
|
||||
* @brief Utility for including binary files
|
||||
*
|
||||
* Facilities for including binary files into the current translation unit
|
||||
* and making use of them externally in other translation units.
|
||||
* Facilities for including binary files into the current translation unit and
|
||||
* making use from them externally in other translation units.
|
||||
*/
|
||||
#ifndef INCBIN_HDR
|
||||
#define INCBIN_HDR
|
||||
@@ -26,7 +26,9 @@
|
||||
defined(__SSSE3__) || \
|
||||
defined(__SSE4_1__) || \
|
||||
defined(__SSE4_2__) || \
|
||||
defined(__neon__)
|
||||
defined(__neon__) || \
|
||||
defined(__ARM_NEON) || \
|
||||
defined(__ALTIVEC__)
|
||||
# define INCBIN_ALIGNMENT_INDEX 4
|
||||
#elif ULONG_MAX != 0xffffffffu
|
||||
# define INCBIN_ALIGNMENT_INDEX 3
|
||||
@@ -64,6 +66,9 @@
|
||||
X
|
||||
#define INCBIN_INVOKE(N, ...) \
|
||||
INCBIN_EVAL(N(__VA_ARGS__))
|
||||
/* Variable argument count for overloading by arity */
|
||||
#define INCBIN_VA_ARG_COUNTER(_1, _2, _3, N, ...) N
|
||||
#define INCBIN_VA_ARGC(...) INCBIN_VA_ARG_COUNTER(__VA_ARGS__, 3, 2, 1, 0)
|
||||
|
||||
/* Green Hills uses a different directive for including binary data */
|
||||
#if defined(__ghs__)
|
||||
@@ -117,29 +122,50 @@
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Optionally override the linker section into which data is emitted.
|
||||
*
|
||||
* @warning If you use this facility, you'll have to deal with platform-specific linker output
|
||||
* section naming on your own
|
||||
*
|
||||
* Overriding the default linker output section, e.g for esp8266/Arduino:
|
||||
* @code
|
||||
* #define INCBIN_OUTPUT_SECTION ".irom.text"
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
* // Data is emitted into program memory that never gets copied to RAM
|
||||
* @endcode
|
||||
* @brief Optionally override the linker section into which size and data is
|
||||
* emitted.
|
||||
*
|
||||
* @warning If you use this facility, you might have to deal with
|
||||
* platform-specific linker output section naming on your own.
|
||||
*/
|
||||
#if !defined(INCBIN_OUTPUT_SECTION)
|
||||
# if defined(__APPLE__)
|
||||
# define INCBIN_OUTPUT_SECTION ".const_data"
|
||||
# define INCBIN_OUTPUT_SECTION ".const_data"
|
||||
# else
|
||||
# define INCBIN_OUTPUT_SECTION ".rodata"
|
||||
# define INCBIN_OUTPUT_SECTION ".rodata"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Optionally override the linker section into which data is emitted.
|
||||
*
|
||||
* @warning If you use this facility, you might have to deal with
|
||||
* platform-specific linker output section naming on your own.
|
||||
*/
|
||||
#if !defined(INCBIN_OUTPUT_DATA_SECTION)
|
||||
# define INCBIN_OUTPUT_DATA_SECTION INCBIN_OUTPUT_SECTION
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Optionally override the linker section into which size is emitted.
|
||||
*
|
||||
* @warning If you use this facility, you might have to deal with
|
||||
* platform-specific linker output section naming on your own.
|
||||
*
|
||||
* @note This is useful for Harvard architectures where program memory cannot
|
||||
* be directly read from the program without special instructions. With this you
|
||||
* can chose to put the size variable in RAM rather than ROM.
|
||||
*/
|
||||
#if !defined(INCBIN_OUTPUT_SIZE_SECTION)
|
||||
# define INCBIN_OUTPUT_SIZE_SECTION INCBIN_OUTPUT_SECTION
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
/* The directives are different for Apple-branded compilers */
|
||||
# include "TargetConditionals.h"
|
||||
# if defined(TARGET_OS_IPHONE) && !defined(INCBIN_SILENCE_BITCODE_WARNING)
|
||||
# warning "incbin is incompatible with bitcode. Using the library will break upload to App Store if you have bitcode enabled. Add `#define INCBIN_SILENCE_BITCODE_WARNING` before including this header to silence this warning."
|
||||
# endif
|
||||
/* The directives are different for Apple branded compilers */
|
||||
# define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n"
|
||||
# define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
|
||||
# define INCBIN_INT ".long "
|
||||
@@ -179,27 +205,17 @@
|
||||
/**
|
||||
* @brief Specify the prefix to use for symbol names.
|
||||
*
|
||||
* By default this is `g', producing symbols of the form:
|
||||
* @code
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
* @note By default this is "g".
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char gFooData[];
|
||||
* // const unsigned char *const gFooEnd;
|
||||
* // const unsigned int gFooSize;
|
||||
* @endcode
|
||||
*
|
||||
* If however you specify a prefix before including: e.g:
|
||||
* @code
|
||||
* #define INCBIN_PREFIX incbin
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols instead:
|
||||
* // const unsigned char incbinFooData[];
|
||||
* // const unsigned char *const incbinFooEnd;
|
||||
* // const unsigned int incbinFooSize;
|
||||
* // const unsigned char incbinFoo<data>[];
|
||||
* // const unsigned char *const incbinFoo<end>;
|
||||
* // const unsigned int incbinFoo<size>;
|
||||
* @endcode
|
||||
*/
|
||||
#if !defined(INCBIN_PREFIX)
|
||||
@@ -213,18 +229,8 @@
|
||||
* - INCBIN_STYLE_CAMEL "CamelCase"
|
||||
* - INCBIN_STYLE_SNAKE "snake_case"
|
||||
*
|
||||
* Default option is *INCBIN_STYLE_CAMEL* producing symbols of the form:
|
||||
* @code
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
* @note By default this is INCBIN_STYLE_CAMEL
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>FooData[];
|
||||
* // const unsigned char *const <prefix>FooEnd;
|
||||
* // const unsigned int <prefix>FooSize;
|
||||
* @endcode
|
||||
*
|
||||
* If however you specify a style before including: e.g:
|
||||
* @code
|
||||
* #define INCBIN_STYLE INCBIN_STYLE_SNAKE
|
||||
* #include "incbin.h"
|
||||
@@ -261,8 +267,8 @@
|
||||
INCBIN_STRINGIZE( \
|
||||
INCBIN_STYLE_IDENT(TYPE)) \
|
||||
|
||||
/* Generate the global labels by indirectly invoking the macro
|
||||
* with our style type and concatenate the name against them. */
|
||||
/* Generate the global labels by indirectly invoking the macro with our style
|
||||
* type and concatenating the name against them. */
|
||||
#define INCBIN_GLOBAL_LABELS(NAME, TYPE) \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_GLOBAL, \
|
||||
@@ -288,23 +294,38 @@
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param TYPE Optional array type. Omitting this picks a default of `unsigned char`.
|
||||
* @param NAME The name given for the binary data
|
||||
*
|
||||
* @code
|
||||
* INCBIN_EXTERN(Foo);
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // extern const unsigned char <prefix>FooData[];
|
||||
* // extern const unsigned char *const <prefix>FooEnd;
|
||||
* // extern const unsigned int <prefix>FooSize;
|
||||
* // extern const unsigned char <prefix>Foo<data>[];
|
||||
* // extern const unsigned char *const <prefix>Foo<end>;
|
||||
* // extern const unsigned int <prefix>Foo<size>;
|
||||
* @endcode
|
||||
*
|
||||
* You may specify a custom optional data type as well as the first argument.
|
||||
* @code
|
||||
* INCBIN_EXTERN(custom_type, Foo);
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // extern const custom_type <prefix>Foo<data>[];
|
||||
* // extern const custom_type *const <prefix>Foo<end>;
|
||||
* // extern const unsigned int <prefix>Foo<size>;
|
||||
* @endcode
|
||||
*/
|
||||
#define INCBIN_EXTERN(NAME) \
|
||||
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char \
|
||||
#define INCBIN_EXTERN(...) \
|
||||
INCBIN_CONCATENATE(INCBIN_EXTERN_, INCBIN_VA_ARGC(__VA_ARGS__))(__VA_ARGS__)
|
||||
#define INCBIN_EXTERN_1(NAME, ...) \
|
||||
INCBIN_EXTERN_2(unsigned char, NAME)
|
||||
#define INCBIN_EXTERN_2(TYPE, NAME) \
|
||||
INCBIN_EXTERNAL const INCBIN_ALIGN TYPE \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(DATA))[]; \
|
||||
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char *const \
|
||||
INCBIN_EXTERNAL const INCBIN_ALIGN TYPE *const \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(END)); \
|
||||
@@ -313,6 +334,29 @@
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(SIZE))
|
||||
|
||||
/**
|
||||
* @brief Externally reference textual data included in another translation unit.
|
||||
*
|
||||
* Produces three external symbols that reference the textual data included in
|
||||
* another translation unit.
|
||||
*
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param NAME The name given for the textual data
|
||||
*
|
||||
* @code
|
||||
* INCBIN_EXTERN(Foo);
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // extern const char <prefix>Foo<data>[];
|
||||
* // extern const char *const <prefix>Foo<end>;
|
||||
* // extern const unsigned int <prefix>Foo<size>;
|
||||
* @endcode
|
||||
*/
|
||||
#define INCTXT_EXTERN(NAME) \
|
||||
INCBIN_EXTERN_2(char, NAME)
|
||||
|
||||
/**
|
||||
* @brief Include a binary file into the current translation unit.
|
||||
*
|
||||
@@ -322,6 +366,7 @@
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param TYPE Optional array type. Omitting this picks a default of `unsigned char`.
|
||||
* @param NAME The name to associate with this binary data (as an identifier.)
|
||||
* @param FILENAME The file to include (as a string literal.)
|
||||
*
|
||||
@@ -329,9 +374,20 @@
|
||||
* INCBIN(Icon, "icon.png");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>IconData[];
|
||||
* // const unsigned char *const <prefix>IconEnd;
|
||||
* // const unsigned int <prefix>IconSize;
|
||||
* // const unsigned char <prefix>Icon<data>[];
|
||||
* // const unsigned char *const <prefix>Icon<end>;
|
||||
* // const unsigned int <prefix>Icon<size>;
|
||||
* @endcode
|
||||
*
|
||||
* You may specify a custom optional data type as well as the first argument.
|
||||
* These macros are specialized by arity.
|
||||
* @code
|
||||
* INCBIN(custom_type, Icon, "icon.png");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const custom_type <prefix>Icon<data>[];
|
||||
* // const custom_type *const <prefix>Icon<end>;
|
||||
* // const unsigned int <prefix>Icon<size>;
|
||||
* @endcode
|
||||
*
|
||||
* @warning This must be used in global scope
|
||||
@@ -341,15 +397,28 @@
|
||||
* please @see INCBIN_EXTERN.
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
#define INCBIN(NAME, FILENAME) \
|
||||
INCBIN_EXTERN(NAME)
|
||||
# define INCBIN(NAME, FILENAME) \
|
||||
INCBIN_EXTERN(NAME)
|
||||
#else
|
||||
#define INCBIN(NAME, FILENAME) \
|
||||
# define INCBIN(...) \
|
||||
INCBIN_CONCATENATE(INCBIN_, INCBIN_VA_ARGC(__VA_ARGS__))(__VA_ARGS__)
|
||||
# if defined(__GNUC__)
|
||||
# define INCBIN_1(...) _Pragma("GCC error \"Single argument INCBIN not allowed\"")
|
||||
# elif defined(__clang__)
|
||||
# define INCBIN_1(...) _Pragma("clang error \"Single argument INCBIN not allowed\"")
|
||||
# else
|
||||
# define INCBIN_1(...) /* Cannot do anything here */
|
||||
# endif
|
||||
# define INCBIN_2(NAME, FILENAME) \
|
||||
INCBIN_3(unsigned char, NAME, FILENAME)
|
||||
# define INCBIN_3(TYPE, NAME, FILENAME) INCBIN_COMMON(TYPE, NAME, FILENAME, /* No terminator for binary data */)
|
||||
# define INCBIN_COMMON(TYPE, NAME, FILENAME, TERMINATOR) \
|
||||
__asm__(INCBIN_SECTION \
|
||||
INCBIN_GLOBAL_LABELS(NAME, DATA) \
|
||||
INCBIN_ALIGN_HOST \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) ":\n" \
|
||||
INCBIN_MACRO " \"" FILENAME "\"\n" \
|
||||
TERMINATOR \
|
||||
INCBIN_GLOBAL_LABELS(NAME, END) \
|
||||
INCBIN_ALIGN_BYTE \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) ":\n" \
|
||||
@@ -362,7 +431,46 @@
|
||||
INCBIN_ALIGN_HOST \
|
||||
".text\n" \
|
||||
); \
|
||||
INCBIN_EXTERN(NAME)
|
||||
INCBIN_EXTERN(TYPE, NAME)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Include a textual file into the current translation unit.
|
||||
*
|
||||
* This behaves the same as INCBIN except it produces char compatible arrays
|
||||
* and implicitly adds a null-terminator byte, thus the size of data included
|
||||
* by this is one byte larger than that of INCBIN.
|
||||
*
|
||||
* Includes a textual file into the current translation unit, producing three
|
||||
* symbols for objects that encode the data and size respectively.
|
||||
*
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param NAME The name to associate with this binary data (as an identifier.)
|
||||
* @param FILENAME The file to include (as a string literal.)
|
||||
*
|
||||
* @code
|
||||
* INCTXT(Readme, "readme.txt");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const char <prefix>Readme<data>[];
|
||||
* // const char *const <prefix>Readme<end>;
|
||||
* // const unsigned int <prefix>Readme<size>;
|
||||
* @endcode
|
||||
*
|
||||
* @warning This must be used in global scope
|
||||
* @warning The identifiers may be different if INCBIN_STYLE is not default
|
||||
*
|
||||
* To externally reference the data included by this in another translation unit
|
||||
* please @see INCBIN_EXTERN.
|
||||
*/
|
||||
#if defined(_MSC_VER)
|
||||
# define INCTXT(NAME, FILENAME) \
|
||||
INCBIN_EXTERN(NAME)
|
||||
#else
|
||||
# define INCTXT(NAME, FILENAME) \
|
||||
INCBIN_COMMON(char, NAME, FILENAME, INCBIN_BYTE "0\n")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+32
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -212,6 +212,37 @@ void* aligned_large_pages_alloc(size_t allocSize) {
|
||||
|
||||
#endif
|
||||
|
||||
bool has_large_pages() {
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
constexpr size_t page_size = 2 * 1024 * 1024; // 2MB page size assumed
|
||||
void* mem = aligned_large_pages_alloc_windows(page_size);
|
||||
if (mem == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
aligned_large_pages_free(mem);
|
||||
return true;
|
||||
}
|
||||
|
||||
#elif defined(__linux__)
|
||||
|
||||
#if defined(MADV_HUGEPAGE)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
return false;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// aligned_large_pages_free() will free the previously memory allocated
|
||||
// by aligned_large_pages_alloc(). The effect is a nop if mem == nullptr.
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -38,6 +38,8 @@ void std_aligned_free(void* ptr);
|
||||
void* aligned_large_pages_alloc(size_t size);
|
||||
void aligned_large_pages_free(void* mem);
|
||||
|
||||
bool has_large_pages();
|
||||
|
||||
// Frees memory which was placed there with placement new.
|
||||
// Works for both single objects and arrays of unknown bound.
|
||||
template<typename T, typename FREE_FUNC>
|
||||
|
||||
+38
-30
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -18,7 +18,9 @@
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
@@ -38,7 +40,7 @@ namespace Stockfish {
|
||||
namespace {
|
||||
|
||||
// Version number or dev.
|
||||
constexpr std::string_view version = "17";
|
||||
constexpr std::string_view version = "dev";
|
||||
|
||||
// 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
|
||||
@@ -122,7 +124,7 @@ class Logger {
|
||||
//
|
||||
// For releases (non-dev builds) we only include the version number:
|
||||
// Stockfish version
|
||||
std::string engine_info(bool to_uci) {
|
||||
std::string engine_version_info() {
|
||||
std::stringstream ss;
|
||||
ss << "Stockfish " << version << std::setfill('0');
|
||||
|
||||
@@ -151,11 +153,14 @@ std::string engine_info(bool to_uci) {
|
||||
#endif
|
||||
}
|
||||
|
||||
ss << (to_uci ? "\nid author " : " by ") << "the Stockfish developers (see AUTHORS file)";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string engine_info(bool to_uci) {
|
||||
return engine_version_info() + (to_uci ? "\nid author " : " by ")
|
||||
+ "the Stockfish developers (see AUTHORS file)";
|
||||
}
|
||||
|
||||
|
||||
// Returns a string trying to describe the compiler we use
|
||||
std::string compiler_info() {
|
||||
@@ -284,7 +289,10 @@ template<size_t N>
|
||||
struct DebugInfo {
|
||||
std::atomic<int64_t> data[N] = {0};
|
||||
|
||||
constexpr std::atomic<int64_t>& operator[](int index) { return data[index]; }
|
||||
[[nodiscard]] constexpr std::atomic<int64_t>& operator[](size_t index) {
|
||||
assert(index < N);
|
||||
return data[index];
|
||||
}
|
||||
};
|
||||
|
||||
struct DebugExtremes: public DebugInfo<3> {
|
||||
@@ -294,54 +302,54 @@ struct DebugExtremes: public DebugInfo<3> {
|
||||
}
|
||||
};
|
||||
|
||||
DebugInfo<2> hit[MaxDebugSlots];
|
||||
DebugInfo<2> mean[MaxDebugSlots];
|
||||
DebugInfo<3> stdev[MaxDebugSlots];
|
||||
DebugInfo<6> correl[MaxDebugSlots];
|
||||
DebugExtremes extremes[MaxDebugSlots];
|
||||
std::array<DebugInfo<2>, MaxDebugSlots> hit;
|
||||
std::array<DebugInfo<2>, MaxDebugSlots> mean;
|
||||
std::array<DebugInfo<3>, MaxDebugSlots> stdev;
|
||||
std::array<DebugInfo<6>, MaxDebugSlots> correl;
|
||||
std::array<DebugExtremes, MaxDebugSlots> extremes;
|
||||
|
||||
} // namespace
|
||||
|
||||
void dbg_hit_on(bool cond, int slot) {
|
||||
|
||||
++hit[slot][0];
|
||||
++hit.at(slot)[0];
|
||||
if (cond)
|
||||
++hit[slot][1];
|
||||
++hit.at(slot)[1];
|
||||
}
|
||||
|
||||
void dbg_mean_of(int64_t value, int slot) {
|
||||
|
||||
++mean[slot][0];
|
||||
mean[slot][1] += value;
|
||||
++mean.at(slot)[0];
|
||||
mean.at(slot)[1] += value;
|
||||
}
|
||||
|
||||
void dbg_stdev_of(int64_t value, int slot) {
|
||||
|
||||
++stdev[slot][0];
|
||||
stdev[slot][1] += value;
|
||||
stdev[slot][2] += value * value;
|
||||
++stdev.at(slot)[0];
|
||||
stdev.at(slot)[1] += value;
|
||||
stdev.at(slot)[2] += value * value;
|
||||
}
|
||||
|
||||
void dbg_extremes_of(int64_t value, int slot) {
|
||||
++extremes[slot][0];
|
||||
++extremes.at(slot)[0];
|
||||
|
||||
int64_t current_max = extremes[slot][1].load();
|
||||
while (current_max < value && !extremes[slot][1].compare_exchange_weak(current_max, value))
|
||||
int64_t current_max = extremes.at(slot)[1].load();
|
||||
while (current_max < value && !extremes.at(slot)[1].compare_exchange_weak(current_max, value))
|
||||
{}
|
||||
|
||||
int64_t current_min = extremes[slot][2].load();
|
||||
while (current_min > value && !extremes[slot][2].compare_exchange_weak(current_min, value))
|
||||
int64_t current_min = extremes.at(slot)[2].load();
|
||||
while (current_min > value && !extremes.at(slot)[2].compare_exchange_weak(current_min, value))
|
||||
{}
|
||||
}
|
||||
|
||||
void dbg_correl_of(int64_t value1, int64_t value2, int slot) {
|
||||
|
||||
++correl[slot][0];
|
||||
correl[slot][1] += value1;
|
||||
correl[slot][2] += value1 * value1;
|
||||
correl[slot][3] += value2;
|
||||
correl[slot][4] += value2 * value2;
|
||||
correl[slot][5] += value1 * value2;
|
||||
++correl.at(slot)[0];
|
||||
correl.at(slot)[1] += value1;
|
||||
correl.at(slot)[2] += value1 * value1;
|
||||
correl.at(slot)[3] += value2;
|
||||
correl.at(slot)[4] += value2 * value2;
|
||||
correl.at(slot)[5] += value1 * value2;
|
||||
}
|
||||
|
||||
void dbg_print() {
|
||||
@@ -451,7 +459,7 @@ void remove_whitespace(std::string& s) {
|
||||
s.erase(std::remove_if(s.begin(), s.end(), [](char c) { return std::isspace(c); }), s.end());
|
||||
}
|
||||
|
||||
bool is_whitespace(const std::string& s) {
|
||||
bool is_whitespace(std::string_view s) {
|
||||
return std::all_of(s.begin(), s.end(), [](char c) { return std::isspace(c); });
|
||||
}
|
||||
|
||||
|
||||
+95
-9
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -20,6 +20,7 @@
|
||||
#define MISC_H_INCLUDED
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
@@ -28,6 +29,7 @@
|
||||
#include <iosfwd>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#define stringify2(x) #x
|
||||
@@ -35,6 +37,7 @@
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
std::string engine_version_info();
|
||||
std::string engine_info(bool to_uci = false);
|
||||
std::string compiler_info();
|
||||
|
||||
@@ -79,8 +82,8 @@ inline TimePoint now() {
|
||||
.count();
|
||||
}
|
||||
|
||||
inline std::vector<std::string> split(const std::string& s, const std::string& delimiter) {
|
||||
std::vector<std::string> res;
|
||||
inline std::vector<std::string_view> split(std::string_view s, std::string_view delimiter) {
|
||||
std::vector<std::string_view> res;
|
||||
|
||||
if (s.empty())
|
||||
return res;
|
||||
@@ -102,7 +105,7 @@ inline std::vector<std::string> split(const std::string& s, const std::string& d
|
||||
}
|
||||
|
||||
void remove_whitespace(std::string& s);
|
||||
bool is_whitespace(const std::string& s);
|
||||
bool is_whitespace(std::string_view s);
|
||||
|
||||
enum SyncCout {
|
||||
IO_LOCK,
|
||||
@@ -117,11 +120,8 @@ void sync_cout_start();
|
||||
void sync_cout_end();
|
||||
|
||||
// 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 bool IsLittleEndian = (Le.c[0] == 4);
|
||||
static inline const std::uint16_t Le = 1;
|
||||
static inline const bool IsLittleEndian = *reinterpret_cast<const char*>(&Le) == 1;
|
||||
|
||||
|
||||
template<typename T, std::size_t MaxSize>
|
||||
@@ -140,6 +140,92 @@ class ValueList {
|
||||
};
|
||||
|
||||
|
||||
template<typename T, std::size_t Size, std::size_t... Sizes>
|
||||
class MultiArray;
|
||||
|
||||
namespace Detail {
|
||||
|
||||
template<typename T, std::size_t Size, std::size_t... Sizes>
|
||||
struct MultiArrayHelper {
|
||||
using ChildType = MultiArray<T, Sizes...>;
|
||||
};
|
||||
|
||||
template<typename T, std::size_t Size>
|
||||
struct MultiArrayHelper<T, Size> {
|
||||
using ChildType = T;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// MultiArray is a generic N-dimensional array.
|
||||
// The template parameters (Size and Sizes) encode the dimensions of the array.
|
||||
template<typename T, std::size_t Size, std::size_t... Sizes>
|
||||
class MultiArray {
|
||||
using ChildType = typename Detail::MultiArrayHelper<T, Size, Sizes...>::ChildType;
|
||||
using ArrayType = std::array<ChildType, Size>;
|
||||
ArrayType data_;
|
||||
|
||||
public:
|
||||
using value_type = typename ArrayType::value_type;
|
||||
using size_type = typename ArrayType::size_type;
|
||||
using difference_type = typename ArrayType::difference_type;
|
||||
using reference = typename ArrayType::reference;
|
||||
using const_reference = typename ArrayType::const_reference;
|
||||
using pointer = typename ArrayType::pointer;
|
||||
using const_pointer = typename ArrayType::const_pointer;
|
||||
using iterator = typename ArrayType::iterator;
|
||||
using const_iterator = typename ArrayType::const_iterator;
|
||||
using reverse_iterator = typename ArrayType::reverse_iterator;
|
||||
using const_reverse_iterator = typename ArrayType::const_reverse_iterator;
|
||||
|
||||
constexpr auto& at(size_type index) noexcept { return data_.at(index); }
|
||||
constexpr const auto& at(size_type index) const noexcept { return data_.at(index); }
|
||||
|
||||
constexpr auto& operator[](size_type index) noexcept { return data_[index]; }
|
||||
constexpr const auto& operator[](size_type index) const noexcept { return data_[index]; }
|
||||
|
||||
constexpr auto& front() noexcept { return data_.front(); }
|
||||
constexpr const auto& front() const noexcept { return data_.front(); }
|
||||
constexpr auto& back() noexcept { return data_.back(); }
|
||||
constexpr const auto& back() const noexcept { return data_.back(); }
|
||||
|
||||
auto* data() { return data_.data(); }
|
||||
const auto* data() const { return data_.data(); }
|
||||
|
||||
constexpr auto begin() noexcept { return data_.begin(); }
|
||||
constexpr auto end() noexcept { return data_.end(); }
|
||||
constexpr auto begin() const noexcept { return data_.begin(); }
|
||||
constexpr auto end() const noexcept { return data_.end(); }
|
||||
constexpr auto cbegin() const noexcept { return data_.cbegin(); }
|
||||
constexpr auto cend() const noexcept { return data_.cend(); }
|
||||
|
||||
constexpr auto rbegin() noexcept { return data_.rbegin(); }
|
||||
constexpr auto rend() noexcept { return data_.rend(); }
|
||||
constexpr auto rbegin() const noexcept { return data_.rbegin(); }
|
||||
constexpr auto rend() const noexcept { return data_.rend(); }
|
||||
constexpr auto crbegin() const noexcept { return data_.crbegin(); }
|
||||
constexpr auto crend() const noexcept { return data_.crend(); }
|
||||
|
||||
constexpr bool empty() const noexcept { return data_.empty(); }
|
||||
constexpr size_type size() const noexcept { return data_.size(); }
|
||||
constexpr size_type max_size() const noexcept { return data_.max_size(); }
|
||||
|
||||
template<typename U>
|
||||
void fill(const U& v) {
|
||||
static_assert(std::is_assignable_v<T, U>, "Cannot assign fill value to entry type");
|
||||
for (auto& ele : data_)
|
||||
{
|
||||
if constexpr (sizeof...(Sizes) == 0)
|
||||
ele = v;
|
||||
else
|
||||
ele.fill(v);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void swap(MultiArray<T, Size, Sizes...>& other) noexcept { data_.swap(other.data_); }
|
||||
};
|
||||
|
||||
|
||||
// xorshift64star Pseudo-Random Number Generator
|
||||
// This class is based on original code written and dedicated
|
||||
// to the public domain by Sebastiano Vigna (2014).
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+33
-31
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -18,11 +18,11 @@
|
||||
|
||||
#include "movepick.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
#include <limits>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "misc.h"
|
||||
#include "position.h"
|
||||
|
||||
namespace Stockfish {
|
||||
@@ -82,16 +82,20 @@ MovePicker::MovePicker(const Position& p,
|
||||
Move ttm,
|
||||
Depth d,
|
||||
const ButterflyHistory* mh,
|
||||
const LowPlyHistory* lph,
|
||||
const CapturePieceToHistory* cph,
|
||||
const PieceToHistory** ch,
|
||||
const PawnHistory* ph) :
|
||||
const PawnHistory* ph,
|
||||
int pl) :
|
||||
pos(p),
|
||||
mainHistory(mh),
|
||||
lowPlyHistory(lph),
|
||||
captureHistory(cph),
|
||||
continuationHistory(ch),
|
||||
pawnHistory(ph),
|
||||
ttMove(ttm),
|
||||
depth(d) {
|
||||
depth(d),
|
||||
ply(pl) {
|
||||
|
||||
if (pos.checkers())
|
||||
stage = EVASION_TT + !(ttm && pos.pseudo_legal(ttm));
|
||||
@@ -152,12 +156,13 @@ void MovePicker::score() {
|
||||
Square to = m.to_sq();
|
||||
|
||||
// histories
|
||||
m.value = (*mainHistory)[pos.side_to_move()][m.from_to()];
|
||||
m.value = 2 * (*mainHistory)[pos.side_to_move()][m.from_to()];
|
||||
m.value += 2 * (*pawnHistory)[pawn_structure_index(pos)][pc][to];
|
||||
m.value += 2 * (*continuationHistory[0])[pc][to];
|
||||
m.value += (*continuationHistory[0])[pc][to];
|
||||
m.value += (*continuationHistory[1])[pc][to];
|
||||
m.value += (*continuationHistory[2])[pc][to] / 3;
|
||||
m.value += (*continuationHistory[2])[pc][to];
|
||||
m.value += (*continuationHistory[3])[pc][to];
|
||||
m.value += (*continuationHistory[4])[pc][to] / 3;
|
||||
m.value += (*continuationHistory[5])[pc][to];
|
||||
|
||||
// bonus for checks
|
||||
@@ -171,16 +176,18 @@ void MovePicker::score() {
|
||||
: 0;
|
||||
|
||||
// malus for putting piece en prise
|
||||
m.value -= (pt == QUEEN ? bool(to & threatenedByRook) * 49000
|
||||
: pt == ROOK ? bool(to & threatenedByMinor) * 24335
|
||||
: bool(to & threatenedByPawn) * 14900);
|
||||
m.value -= (pt == QUEEN ? bool(to & threatenedByRook) * 49000
|
||||
: pt == ROOK && bool(to & threatenedByMinor) ? 24335
|
||||
: 0);
|
||||
|
||||
if (ply < LOW_PLY_HISTORY_SIZE)
|
||||
m.value += 8 * (*lowPlyHistory)[ply][m.from_to()] / (1 + 2 * ply);
|
||||
}
|
||||
|
||||
else // Type == EVASIONS
|
||||
{
|
||||
if (pos.capture_stage(m))
|
||||
m.value =
|
||||
PieceValue[pos.piece_on(m.to_sq())] - type_of(pos.moved_piece(m)) + (1 << 28);
|
||||
m.value = PieceValue[pos.piece_on(m.to_sq())] + (1 << 28);
|
||||
else
|
||||
m.value = (*mainHistory)[pos.side_to_move()][m.from_to()]
|
||||
+ (*continuationHistory[0])[pos.moved_piece(m)][m.to_sq()]
|
||||
@@ -190,26 +197,20 @@ void MovePicker::score() {
|
||||
|
||||
// Returns the next move satisfying a predicate function.
|
||||
// This never returns the TT move, as it was emitted before.
|
||||
template<MovePicker::PickType T, typename Pred>
|
||||
template<typename Pred>
|
||||
Move MovePicker::select(Pred filter) {
|
||||
|
||||
while (cur < endMoves)
|
||||
{
|
||||
if constexpr (T == Best)
|
||||
std::swap(*cur, *std::max_element(cur, endMoves));
|
||||
|
||||
for (; cur < endMoves; ++cur)
|
||||
if (*cur != ttMove && filter())
|
||||
return *cur++;
|
||||
|
||||
cur++;
|
||||
}
|
||||
return Move::none();
|
||||
}
|
||||
|
||||
// This is the most important method of the MovePicker class. We emit one
|
||||
// new pseudo-legal move on every call until there are no more moves left,
|
||||
// picking the move with the highest score from a list of generated moves.
|
||||
Move MovePicker::next_move(bool skipQuiets) {
|
||||
Move MovePicker::next_move() {
|
||||
|
||||
auto quiet_threshold = [](Depth d) { return -3560 * d; };
|
||||
|
||||
@@ -236,7 +237,7 @@ top:
|
||||
goto top;
|
||||
|
||||
case GOOD_CAPTURE :
|
||||
if (select<Next>([&]() {
|
||||
if (select([&]() {
|
||||
// Move losing capture to endBadCaptures to be tried later
|
||||
return pos.see_ge(*cur, -cur->value / 18) ? true
|
||||
: (*endBadCaptures++ = *cur, false);
|
||||
@@ -260,7 +261,7 @@ top:
|
||||
[[fallthrough]];
|
||||
|
||||
case GOOD_QUIET :
|
||||
if (!skipQuiets && select<Next>([]() { return true; }))
|
||||
if (!skipQuiets && select([]() { return true; }))
|
||||
{
|
||||
if ((cur - 1)->value > -7998 || (cur - 1)->value <= quiet_threshold(depth))
|
||||
return *(cur - 1);
|
||||
@@ -277,7 +278,7 @@ top:
|
||||
[[fallthrough]];
|
||||
|
||||
case BAD_CAPTURE :
|
||||
if (select<Next>([]() { return true; }))
|
||||
if (select([]() { return true; }))
|
||||
return *(cur - 1);
|
||||
|
||||
// Prepare the pointers to loop over the bad quiets
|
||||
@@ -289,7 +290,7 @@ top:
|
||||
|
||||
case BAD_QUIET :
|
||||
if (!skipQuiets)
|
||||
return select<Next>([]() { return true; });
|
||||
return select([]() { return true; });
|
||||
|
||||
return Move::none();
|
||||
|
||||
@@ -298,21 +299,22 @@ top:
|
||||
endMoves = generate<EVASIONS>(pos, cur);
|
||||
|
||||
score<EVASIONS>();
|
||||
partial_insertion_sort(cur, endMoves, std::numeric_limits<int>::min());
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
|
||||
case EVASION :
|
||||
return select<Best>([]() { return true; });
|
||||
case QCAPTURE :
|
||||
return select([]() { return true; });
|
||||
|
||||
case PROBCUT :
|
||||
return select<Next>([&]() { return pos.see_ge(*cur, threshold); });
|
||||
|
||||
case QCAPTURE :
|
||||
return select<Next>([]() { return true; });
|
||||
return select([&]() { return pos.see_ge(*cur, threshold); });
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return Move::none(); // Silence warning
|
||||
}
|
||||
|
||||
void MovePicker::skip_quiet_moves() { skipQuiets = true; }
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+12
-121
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -19,123 +19,13 @@
|
||||
#ifndef MOVEPICK_H_INCLUDED
|
||||
#define MOVEPICK_H_INCLUDED
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <type_traits> // IWYU pragma: keep
|
||||
|
||||
#include "history.h"
|
||||
#include "movegen.h"
|
||||
#include "position.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2
|
||||
constexpr int CORRECTION_HISTORY_SIZE = 16384; // has to be a power of 2
|
||||
constexpr int CORRECTION_HISTORY_LIMIT = 1024;
|
||||
|
||||
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>
|
||||
class StatsEntry {
|
||||
|
||||
T entry;
|
||||
|
||||
public:
|
||||
void operator=(const T& v) { entry = v; }
|
||||
T* operator&() { return &entry; }
|
||||
T* operator->() { return &entry; }
|
||||
operator const T&() const { return entry; }
|
||||
|
||||
void operator<<(int bonus) {
|
||||
static_assert(D <= std::numeric_limits<T>::max(), "D overflows T");
|
||||
|
||||
// Make sure that bonus is in range [-D, D]
|
||||
int clampedBonus = std::clamp(bonus, -D, D);
|
||||
entry += clampedBonus - entry * std::abs(clampedBonus) / D;
|
||||
|
||||
assert(std::abs(entry) <= D);
|
||||
}
|
||||
};
|
||||
|
||||
// Stats is a generic N-dimensional array used to store various statistics.
|
||||
// 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
|
||||
// values with the << operator, while the last parameters (Size and Sizes)
|
||||
// encode the dimensions of the array.
|
||||
template<typename T, int D, int Size, int... Sizes>
|
||||
struct Stats: public std::array<Stats<T, D, Sizes...>, Size> {
|
||||
using stats = Stats<T, D, Size, Sizes...>;
|
||||
|
||||
void fill(const T& v) {
|
||||
|
||||
// For standard-layout 'this' points to the first struct member
|
||||
assert(std::is_standard_layout_v<stats>);
|
||||
|
||||
using entry = StatsEntry<T, D>;
|
||||
entry* p = reinterpret_cast<entry*>(this);
|
||||
std::fill(p, p + sizeof(*this) / sizeof(entry), v);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, int D, int 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
|
||||
enum StatsParams {
|
||||
NOT_USED = 0
|
||||
};
|
||||
enum StatsType {
|
||||
NoCaptures,
|
||||
Captures
|
||||
};
|
||||
|
||||
// ButterflyHistory records how often quiet moves have been successful or unsuccessful
|
||||
// during the current search, and is used for reduction and move ordering decisions.
|
||||
// It uses 2 tables (one for each color) indexed by the move's from and to squares,
|
||||
// see www.chessprogramming.org/Butterfly_Boards (~11 elo)
|
||||
using ButterflyHistory = Stats<int16_t, 7183, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)>;
|
||||
|
||||
// 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>;
|
||||
|
||||
// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to]
|
||||
using PieceToHistory = Stats<int16_t, 29952, PIECE_NB, SQUARE_NB>;
|
||||
|
||||
// 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
|
||||
// PieceToHistory instead of ButterflyBoards.
|
||||
// (~63 elo)
|
||||
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>;
|
||||
|
||||
// CorrectionHistory is addressed by color and pawn structure
|
||||
using CorrectionHistory =
|
||||
Stats<int16_t, CORRECTION_HISTORY_LIMIT, COLOR_NB, CORRECTION_HISTORY_SIZE>;
|
||||
class Position;
|
||||
|
||||
// The MovePicker class is used to pick one pseudo-legal move at a time from the
|
||||
// current position. The most important method is next_move(), which emits one
|
||||
@@ -145,11 +35,6 @@ using CorrectionHistory =
|
||||
// a cut-off first.
|
||||
class MovePicker {
|
||||
|
||||
enum PickType {
|
||||
Next,
|
||||
Best
|
||||
};
|
||||
|
||||
public:
|
||||
MovePicker(const MovePicker&) = delete;
|
||||
MovePicker& operator=(const MovePicker&) = delete;
|
||||
@@ -157,14 +42,17 @@ class MovePicker {
|
||||
Move,
|
||||
Depth,
|
||||
const ButterflyHistory*,
|
||||
const LowPlyHistory*,
|
||||
const CapturePieceToHistory*,
|
||||
const PieceToHistory**,
|
||||
const PawnHistory*);
|
||||
const PawnHistory*,
|
||||
int);
|
||||
MovePicker(const Position&, Move, int, const CapturePieceToHistory*);
|
||||
Move next_move(bool skipQuiets = false);
|
||||
Move next_move();
|
||||
void skip_quiet_moves();
|
||||
|
||||
private:
|
||||
template<PickType T, typename Pred>
|
||||
template<typename Pred>
|
||||
Move select(Pred);
|
||||
template<GenType>
|
||||
void score();
|
||||
@@ -173,6 +61,7 @@ class MovePicker {
|
||||
|
||||
const Position& pos;
|
||||
const ButterflyHistory* mainHistory;
|
||||
const LowPlyHistory* lowPlyHistory;
|
||||
const CapturePieceToHistory* captureHistory;
|
||||
const PieceToHistory** continuationHistory;
|
||||
const PawnHistory* pawnHistory;
|
||||
@@ -181,6 +70,8 @@ class MovePicker {
|
||||
int stage;
|
||||
int threshold;
|
||||
Depth depth;
|
||||
int ply;
|
||||
bool skipQuiets = false;
|
||||
ExtMove moves[MAX_MOVES];
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -38,17 +38,41 @@
|
||||
namespace Stockfish::Eval::NNUE::Layers {
|
||||
|
||||
#if (USE_SSSE3 | (USE_NEON >= 8))
|
||||
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{};
|
||||
for (unsigned i = 0; i < 256; ++i)
|
||||
{
|
||||
std::uint64_t j = i, k = 0;
|
||||
while (j)
|
||||
v[i][k++] = pop_lsb(j);
|
||||
}
|
||||
return v;
|
||||
}();
|
||||
static constexpr int lsb_index64[64] = {
|
||||
0, 47, 1, 56, 48, 27, 2, 60, 57, 49, 41, 37, 28, 16, 3, 61, 54, 58, 35, 52, 50, 42,
|
||||
21, 44, 38, 32, 29, 23, 17, 11, 4, 62, 46, 55, 26, 59, 40, 36, 15, 53, 34, 51, 20, 43,
|
||||
31, 22, 10, 45, 25, 39, 14, 33, 19, 30, 9, 24, 13, 18, 8, 12, 7, 6, 5, 63};
|
||||
|
||||
constexpr int constexpr_lsb(uint64_t bb) {
|
||||
assert(bb != 0);
|
||||
constexpr uint64_t debruijn64 = 0x03F79D71B4CB0A89ULL;
|
||||
return lsb_index64[((bb ^ (bb - 1)) * debruijn64) >> 58];
|
||||
}
|
||||
|
||||
alignas(CacheLineSize) static constexpr struct OffsetIndices {
|
||||
|
||||
#if (USE_SSE41)
|
||||
std::uint8_t offset_indices[256][8];
|
||||
#else
|
||||
std::uint16_t offset_indices[256][8];
|
||||
#endif
|
||||
|
||||
constexpr OffsetIndices() :
|
||||
offset_indices() {
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
std::uint64_t j = i, k = 0;
|
||||
while (j)
|
||||
{
|
||||
offset_indices[i][k++] = constexpr_lsb(j);
|
||||
j &= j - 1;
|
||||
}
|
||||
while (k < 8)
|
||||
offset_indices[i][k++] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} Lookup;
|
||||
|
||||
// Find indices of nonzero numbers in an int32_t array
|
||||
template<const IndexType InputDimensions>
|
||||
@@ -74,7 +98,11 @@ void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_ou
|
||||
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)
|
||||
#if (USE_SSE41)
|
||||
#define vec128_load(a) _mm_cvtepu8_epi16(_mm_loadl_epi64(a))
|
||||
#else
|
||||
#define vec128_load(a) _mm_load_si128(a)
|
||||
#endif
|
||||
#define vec128_storeu(a, b) _mm_storeu_si128(a, b)
|
||||
#define vec128_add(a, b) _mm_add_epi16(a, b)
|
||||
#elif defined(USE_NEON)
|
||||
@@ -110,9 +138,9 @@ void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_ou
|
||||
}
|
||||
for (IndexType j = 0; j < OutputsPerChunk; ++j)
|
||||
{
|
||||
const auto lookup = (nnz >> (j * 8)) & 0xFF;
|
||||
const auto offsets =
|
||||
vec128_load(reinterpret_cast<const vec128_t*>(&lookup_indices[lookup]));
|
||||
const unsigned lookup = (nnz >> (j * 8)) & 0xFF;
|
||||
const vec128_t offsets =
|
||||
vec128_load(reinterpret_cast<const vec128_t*>(&Lookup.offset_indices[lookup]));
|
||||
vec128_storeu(reinterpret_cast<vec128_t*>(out + count), vec128_add(base, offsets));
|
||||
count += popcount(lookup);
|
||||
base = vec128_add(base, increment);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+36
-24
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -26,8 +26,10 @@
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "../evaluate.h"
|
||||
#define INCBIN_SILENCE_BITCODE_WARNING
|
||||
#include "../incbin/incbin.h"
|
||||
|
||||
#include "../evaluate.h"
|
||||
#include "../memory.h"
|
||||
#include "../misc.h"
|
||||
#include "../position.h"
|
||||
@@ -36,7 +38,6 @@
|
||||
#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
|
||||
@@ -56,6 +57,8 @@ const unsigned char* const gEmbeddedNNUESmallEnd = &gEmbeddedNNUESmallData[1
|
||||
const unsigned int gEmbeddedNNUESmallSize = 1;
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
struct EmbeddedNNUE {
|
||||
EmbeddedNNUE(const unsigned char* embeddedData,
|
||||
const unsigned char* embeddedEnd,
|
||||
@@ -98,7 +101,7 @@ bool read_parameters(std::istream& stream, T& reference) {
|
||||
|
||||
// Write evaluation function parameters
|
||||
template<typename T>
|
||||
bool write_parameters(std::ostream& stream, const T& reference) {
|
||||
bool write_parameters(std::ostream& stream, T& reference) {
|
||||
|
||||
write_little_endian<std::uint32_t>(stream, T::get_hash_value());
|
||||
return reference.write_parameters(stream);
|
||||
@@ -234,35 +237,44 @@ Network<Arch, Transformer>::evaluate(const Position& pos
|
||||
|
||||
|
||||
template<typename Arch, typename Transformer>
|
||||
void Network<Arch, Transformer>::verify(std::string evalfilePath) const {
|
||||
void Network<Arch, Transformer>::verify(std::string evalfilePath,
|
||||
const std::function<void(std::string_view)>& f) 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.";
|
||||
if (f)
|
||||
{
|
||||
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.";
|
||||
|
||||
std::string msg = "ERROR: " + msg1 + '\n' + "ERROR: " + msg2 + '\n' + "ERROR: " + msg3
|
||||
+ '\n' + "ERROR: " + msg4 + '\n' + "ERROR: " + msg5 + '\n';
|
||||
|
||||
f(msg);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
size_t size = sizeof(*featureTransformer) + sizeof(Arch) * LayerStacks;
|
||||
sync_cout << "info string NNUE evaluation using " << evalfilePath << " ("
|
||||
<< size / (1024 * 1024) << "MiB, (" << featureTransformer->InputDimensions << ", "
|
||||
<< network[0].TransformedFeatureDimensions << ", " << network[0].FC_0_OUTPUTS << ", "
|
||||
<< network[0].FC_1_OUTPUTS << ", 1))" << sync_endl;
|
||||
if (f)
|
||||
{
|
||||
size_t size = sizeof(*featureTransformer) + sizeof(Arch) * LayerStacks;
|
||||
f("info string NNUE evaluation using " + evalfilePath + " ("
|
||||
+ std::to_string(size / (1024 * 1024)) + "MiB, ("
|
||||
+ std::to_string(featureTransformer->InputDimensions) + ", "
|
||||
+ std::to_string(network[0].TransformedFeatureDimensions) + ", "
|
||||
+ std::to_string(network[0].FC_0_OUTPUTS) + ", " + std::to_string(network[0].FC_1_OUTPUTS)
|
||||
+ ", 1))");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -20,9 +20,11 @@
|
||||
#define NETWORK_H_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
@@ -68,7 +70,7 @@ class Network {
|
||||
void hint_common_access(const Position& pos,
|
||||
AccumulatorCaches::Cache<FTDimensions>* cache) const;
|
||||
|
||||
void verify(std::string evalfilePath) const;
|
||||
void verify(std::string evalfilePath, const std::function<void(std::string_view)>&) const;
|
||||
NnueEvalTrace trace_evaluate(const Position& pos,
|
||||
AccumulatorCaches::Cache<FTDimensions>* cache) const;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -80,11 +80,6 @@ struct AccumulatorCaches {
|
||||
entry.clear(network.featureTransformer->biases);
|
||||
}
|
||||
|
||||
void clear(const BiasType* biases) {
|
||||
for (auto& entry : entries)
|
||||
entry.clear(biases);
|
||||
}
|
||||
|
||||
std::array<Entry, COLOR_NB>& operator[](Square sq) { return entries[sq]; }
|
||||
|
||||
std::array<std::array<Entry, COLOR_NB>, SQUARE_NB> entries;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
|
||||
+292
-381
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iosfwd>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "../position.h"
|
||||
@@ -145,11 +146,51 @@ using psqt_vec_t = int32x4_t;
|
||||
|
||||
#endif
|
||||
|
||||
// Returns the inverse of a permutation
|
||||
template<std::size_t Len>
|
||||
constexpr std::array<std::size_t, Len>
|
||||
invert_permutation(const std::array<std::size_t, Len>& order) {
|
||||
std::array<std::size_t, Len> inverse{};
|
||||
for (std::size_t i = 0; i < order.size(); i++)
|
||||
inverse[order[i]] = i;
|
||||
return inverse;
|
||||
}
|
||||
|
||||
// Divide a byte region of size TotalSize to chunks of size
|
||||
// BlockSize, and permute the blocks by a given order
|
||||
template<std::size_t BlockSize, typename T, std::size_t N, std::size_t OrderSize>
|
||||
void permute(T (&data)[N], const std::array<std::size_t, OrderSize>& order) {
|
||||
constexpr std::size_t TotalSize = N * sizeof(T);
|
||||
|
||||
static_assert(TotalSize % (BlockSize * OrderSize) == 0,
|
||||
"ChunkSize * OrderSize must perfectly divide TotalSize");
|
||||
|
||||
constexpr std::size_t ProcessChunkSize = BlockSize * OrderSize;
|
||||
|
||||
std::array<std::byte, ProcessChunkSize> buffer{};
|
||||
|
||||
std::byte* const bytes = reinterpret_cast<std::byte*>(data);
|
||||
|
||||
for (std::size_t i = 0; i < TotalSize; i += ProcessChunkSize)
|
||||
{
|
||||
std::byte* const values = &bytes[i];
|
||||
|
||||
for (std::size_t j = 0; j < OrderSize; j++)
|
||||
{
|
||||
auto* const buffer_chunk = &buffer[j * BlockSize];
|
||||
auto* const value_chunk = &values[order[j] * BlockSize];
|
||||
|
||||
std::copy(value_chunk, value_chunk + BlockSize, buffer_chunk);
|
||||
}
|
||||
|
||||
std::copy(std::begin(buffer), std::end(buffer), values);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute optimal SIMD register count for feature transformer accumulation.
|
||||
template<IndexType TransformedFeatureWidth, IndexType HalfDimensions>
|
||||
class SIMDTiling {
|
||||
#ifdef VECTOR
|
||||
|
||||
// Compute optimal SIMD register count for feature transformer accumulation.
|
||||
|
||||
// We use __m* types as template arguments, which causes GCC to emit warnings
|
||||
// about losing some attribute information. This is irrelevant to us as we
|
||||
// only take their size, so the following pragma are harmless.
|
||||
@@ -158,33 +199,47 @@ using psqt_vec_t = int32x4_t;
|
||||
#pragma GCC diagnostic ignored "-Wignored-attributes"
|
||||
#endif
|
||||
|
||||
template<typename SIMDRegisterType, typename LaneType, int NumLanes, int MaxRegisters>
|
||||
static constexpr int BestRegisterCount() {
|
||||
#define RegisterSize sizeof(SIMDRegisterType)
|
||||
#define LaneSize sizeof(LaneType)
|
||||
template<typename SIMDRegisterType, typename LaneType, int NumLanes, int MaxRegisters>
|
||||
static constexpr int BestRegisterCount() {
|
||||
constexpr std::size_t RegisterSize = sizeof(SIMDRegisterType);
|
||||
constexpr std::size_t LaneSize = sizeof(LaneType);
|
||||
|
||||
static_assert(RegisterSize >= LaneSize);
|
||||
static_assert(MaxRegisters <= NumRegistersSIMD);
|
||||
static_assert(MaxRegisters > 0);
|
||||
static_assert(NumRegistersSIMD > 0);
|
||||
static_assert(RegisterSize % LaneSize == 0);
|
||||
static_assert((NumLanes * LaneSize) % RegisterSize == 0);
|
||||
static_assert(RegisterSize >= LaneSize);
|
||||
static_assert(MaxRegisters <= NumRegistersSIMD);
|
||||
static_assert(MaxRegisters > 0);
|
||||
static_assert(NumRegistersSIMD > 0);
|
||||
static_assert(RegisterSize % LaneSize == 0);
|
||||
static_assert((NumLanes * LaneSize) % RegisterSize == 0);
|
||||
|
||||
const int ideal = (NumLanes * LaneSize) / RegisterSize;
|
||||
if (ideal <= MaxRegisters)
|
||||
return ideal;
|
||||
const int ideal = (NumLanes * LaneSize) / RegisterSize;
|
||||
if (ideal <= MaxRegisters)
|
||||
return ideal;
|
||||
|
||||
// Look for the largest divisor of the ideal register count that is smaller than MaxRegisters
|
||||
for (int divisor = MaxRegisters; divisor > 1; --divisor)
|
||||
if (ideal % divisor == 0)
|
||||
return divisor;
|
||||
// Look for the largest divisor of the ideal register count that is smaller than MaxRegisters
|
||||
for (int divisor = MaxRegisters; divisor > 1; --divisor)
|
||||
if (ideal % divisor == 0)
|
||||
return divisor;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
public:
|
||||
static constexpr int NumRegs =
|
||||
BestRegisterCount<vec_t, WeightType, TransformedFeatureWidth, NumRegistersSIMD>();
|
||||
static constexpr int NumPsqtRegs =
|
||||
BestRegisterCount<psqt_vec_t, PSQTWeightType, PSQTBuckets, NumRegistersSIMD>();
|
||||
|
||||
static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2;
|
||||
static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4;
|
||||
|
||||
static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions");
|
||||
static_assert(PSQTBuckets % PsqtTileHeight == 0, "PsqtTileHeight must divide PSQTBuckets");
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
// Input feature converter
|
||||
@@ -196,17 +251,7 @@ class FeatureTransformer {
|
||||
static constexpr IndexType HalfDimensions = TransformedFeatureDimensions;
|
||||
|
||||
private:
|
||||
#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 PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4;
|
||||
static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions");
|
||||
static_assert(PSQTBuckets % PsqtTileHeight == 0, "PsqtTileHeight must divide PSQTBuckets");
|
||||
#endif
|
||||
using Tiling = SIMDTiling<TransformedFeatureDimensions, HalfDimensions>;
|
||||
|
||||
public:
|
||||
// Output type
|
||||
@@ -219,76 +264,54 @@ class FeatureTransformer {
|
||||
// Size of forward propagation buffer
|
||||
static constexpr std::size_t BufferSize = OutputDimensions * sizeof(OutputType);
|
||||
|
||||
// Store the order by which 128-bit blocks of a 1024-bit data must
|
||||
// be permuted so that calling packus on adjacent vectors of 16-bit
|
||||
// integers loaded from the data results in the pre-permutation order
|
||||
static constexpr auto PackusEpi16Order = []() -> std::array<std::size_t, 8> {
|
||||
#if defined(USE_AVX512)
|
||||
// _mm512_packus_epi16 after permutation:
|
||||
// | 0 | 2 | 4 | 6 | // Vector 0
|
||||
// | 1 | 3 | 5 | 7 | // Vector 1
|
||||
// | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | // Packed Result
|
||||
return {0, 2, 4, 6, 1, 3, 5, 7};
|
||||
#elif defined(USE_AVX2)
|
||||
// _mm256_packus_epi16 after permutation:
|
||||
// | 0 | 2 | | 4 | 6 | // Vector 0, 2
|
||||
// | 1 | 3 | | 5 | 7 | // Vector 1, 3
|
||||
// | 0 | 1 | 2 | 3 | | 4 | 5 | 6 | 7 | // Packed Result
|
||||
return {0, 2, 1, 3, 4, 6, 5, 7};
|
||||
#else
|
||||
return {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
#endif
|
||||
}();
|
||||
|
||||
static constexpr auto InversePackusEpi16Order = invert_permutation(PackusEpi16Order);
|
||||
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t get_hash_value() {
|
||||
return FeatureSet::HashValue ^ (OutputDimensions * 2);
|
||||
}
|
||||
|
||||
static constexpr void order_packs([[maybe_unused]] uint64_t* v) {
|
||||
#if defined(USE_AVX512) // _mm512_packs_epi16 ordering
|
||||
uint64_t tmp0 = v[2], tmp1 = v[3];
|
||||
v[2] = v[8], v[3] = v[9];
|
||||
v[8] = v[4], v[9] = v[5];
|
||||
v[4] = tmp0, v[5] = tmp1;
|
||||
tmp0 = v[6], tmp1 = v[7];
|
||||
v[6] = v[10], v[7] = v[11];
|
||||
v[10] = v[12], v[11] = v[13];
|
||||
v[12] = tmp0, v[13] = tmp1;
|
||||
#elif defined(USE_AVX2) // _mm256_packs_epi16 ordering
|
||||
std::swap(v[2], v[4]);
|
||||
std::swap(v[3], v[5]);
|
||||
#endif
|
||||
void permute_weights() {
|
||||
permute<16>(biases, PackusEpi16Order);
|
||||
permute<16>(weights, PackusEpi16Order);
|
||||
}
|
||||
|
||||
static constexpr void inverse_order_packs([[maybe_unused]] uint64_t* v) {
|
||||
#if defined(USE_AVX512) // Inverse _mm512_packs_epi16 ordering
|
||||
uint64_t tmp0 = v[2], tmp1 = v[3];
|
||||
v[2] = v[4], v[3] = v[5];
|
||||
v[4] = v[8], v[5] = v[9];
|
||||
v[8] = tmp0, v[9] = tmp1;
|
||||
tmp0 = v[6], tmp1 = v[7];
|
||||
v[6] = v[12], v[7] = v[13];
|
||||
v[12] = v[10], v[13] = v[11];
|
||||
v[10] = tmp0, v[11] = tmp1;
|
||||
#elif defined(USE_AVX2) // Inverse _mm256_packs_epi16 ordering
|
||||
std::swap(v[2], v[4]);
|
||||
std::swap(v[3], v[5]);
|
||||
#endif
|
||||
void unpermute_weights() {
|
||||
permute<16>(biases, InversePackusEpi16Order);
|
||||
permute<16>(weights, InversePackusEpi16Order);
|
||||
}
|
||||
|
||||
void permute_weights([[maybe_unused]] void (*order_fn)(uint64_t*)) const {
|
||||
#if defined(USE_AVX2)
|
||||
#if defined(USE_AVX512)
|
||||
constexpr IndexType di = 16;
|
||||
#else
|
||||
constexpr IndexType di = 8;
|
||||
#endif
|
||||
uint64_t* b = reinterpret_cast<uint64_t*>(const_cast<BiasType*>(&biases[0]));
|
||||
for (IndexType i = 0; i < HalfDimensions * sizeof(BiasType) / sizeof(uint64_t); i += di)
|
||||
order_fn(&b[i]);
|
||||
|
||||
inline void scale_weights(bool read) {
|
||||
for (IndexType j = 0; j < InputDimensions; ++j)
|
||||
{
|
||||
uint64_t* w =
|
||||
reinterpret_cast<uint64_t*>(const_cast<WeightType*>(&weights[j * HalfDimensions]));
|
||||
for (IndexType i = 0; i < HalfDimensions * sizeof(WeightType) / sizeof(uint64_t);
|
||||
i += di)
|
||||
order_fn(&w[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void scale_weights(bool read) const {
|
||||
for (IndexType j = 0; j < InputDimensions; ++j)
|
||||
{
|
||||
WeightType* w = const_cast<WeightType*>(&weights[j * HalfDimensions]);
|
||||
WeightType* w = &weights[j * HalfDimensions];
|
||||
for (IndexType i = 0; i < HalfDimensions; ++i)
|
||||
w[i] = read ? w[i] * 2 : w[i] / 2;
|
||||
}
|
||||
|
||||
BiasType* b = const_cast<BiasType*>(biases);
|
||||
for (IndexType i = 0; i < HalfDimensions; ++i)
|
||||
b[i] = read ? b[i] * 2 : b[i] / 2;
|
||||
biases[i] = read ? biases[i] * 2 : biases[i] / 2;
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
@@ -298,22 +321,22 @@ class FeatureTransformer {
|
||||
read_leb_128<WeightType>(stream, weights, HalfDimensions * InputDimensions);
|
||||
read_leb_128<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
|
||||
|
||||
permute_weights(inverse_order_packs);
|
||||
permute_weights();
|
||||
scale_weights(true);
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
// Write network parameters
|
||||
bool write_parameters(std::ostream& stream) const {
|
||||
bool write_parameters(std::ostream& stream) {
|
||||
|
||||
permute_weights(order_packs);
|
||||
unpermute_weights();
|
||||
scale_weights(false);
|
||||
|
||||
write_leb_128<BiasType>(stream, biases, HalfDimensions);
|
||||
write_leb_128<WeightType>(stream, weights, HalfDimensions * InputDimensions);
|
||||
write_leb_128<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
|
||||
|
||||
permute_weights(inverse_order_packs);
|
||||
permute_weights();
|
||||
scale_weights(true);
|
||||
return !stream.fail();
|
||||
}
|
||||
@@ -447,17 +470,16 @@ class FeatureTransformer {
|
||||
|
||||
void hint_common_access(const Position& pos,
|
||||
AccumulatorCaches::Cache<HalfDimensions>* cache) const {
|
||||
hint_common_access_for_perspective<WHITE>(pos, cache);
|
||||
hint_common_access_for_perspective<BLACK>(pos, cache);
|
||||
update_accumulator<WHITE>(pos, cache);
|
||||
update_accumulator<BLACK>(pos, cache);
|
||||
}
|
||||
|
||||
private:
|
||||
template<Color Perspective>
|
||||
[[nodiscard]] std::pair<StateInfo*, StateInfo*>
|
||||
try_find_computed_accumulator(const Position& pos) const {
|
||||
StateInfo* try_find_computed_accumulator(const Position& pos) const {
|
||||
// Look for a usable accumulator of an earlier position. We keep track
|
||||
// of the estimated gain in terms of features to be added/subtracted.
|
||||
StateInfo *st = pos.state(), *next = nullptr;
|
||||
StateInfo* st = pos.state();
|
||||
int gain = FeatureSet::refresh_cost(pos);
|
||||
while (st->previous && !(st->*accPtr).computed[Perspective])
|
||||
{
|
||||
@@ -466,239 +488,170 @@ class FeatureTransformer {
|
||||
if (FeatureSet::requires_refresh(st, Perspective)
|
||||
|| (gain -= FeatureSet::update_cost(st) + 1) < 0)
|
||||
break;
|
||||
next = st;
|
||||
st = st->previous;
|
||||
st = st->previous;
|
||||
}
|
||||
return {st, next};
|
||||
return st;
|
||||
}
|
||||
|
||||
// NOTE: The parameter states_to_update is an array of position states.
|
||||
// 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], and computed_st must be reachable by
|
||||
// repeatedly applying ->previous on states_to_update[0].
|
||||
template<Color Perspective, size_t N>
|
||||
void update_accumulator_incremental(const Position& pos,
|
||||
StateInfo* computed_st,
|
||||
StateInfo* states_to_update[N]) const {
|
||||
static_assert(N > 0);
|
||||
assert([&]() {
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
{
|
||||
if (states_to_update[i] == nullptr)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}());
|
||||
// Given a computed accumulator, computes the accumulator of the next position.
|
||||
template<Color Perspective>
|
||||
void update_accumulator_incremental(const Position& pos, StateInfo* computed) const {
|
||||
assert((computed->*accPtr).computed[Perspective]);
|
||||
assert(computed->next != nullptr);
|
||||
|
||||
#ifdef VECTOR
|
||||
// Gcc-10.2 unnecessarily spills AVX2 registers if this array
|
||||
// is defined in the VECTOR code below, once in each branch.
|
||||
vec_t acc[NumRegs];
|
||||
psqt_vec_t psqt[NumPsqtRegs];
|
||||
#endif
|
||||
|
||||
// Update incrementally going back through states_to_update.
|
||||
// Gather all features to be updated.
|
||||
const Square ksq = pos.square<KING>(Perspective);
|
||||
|
||||
// The size must be enough to contain the largest possible update.
|
||||
// That might depend on the feature set and generally relies on the
|
||||
// feature set's update cost calculation to be correct and never allow
|
||||
// updates with more added/removed features than MaxActiveDimensions.
|
||||
FeatureSet::IndexList removed[N], added[N];
|
||||
// In this case, the maximum size of both feature addition and removal
|
||||
// is 2, since we are incrementally updating one move at a time.
|
||||
FeatureSet::IndexList removed, added;
|
||||
FeatureSet::append_changed_indices<Perspective>(ksq, computed->next->dirtyPiece, removed,
|
||||
added);
|
||||
|
||||
for (int i = N - 1; i >= 0; --i)
|
||||
StateInfo* next = computed->next;
|
||||
assert(!(next->*accPtr).computed[Perspective]);
|
||||
|
||||
if (removed.size() == 0 && added.size() == 0)
|
||||
{
|
||||
(states_to_update[i]->*accPtr).computed[Perspective] = true;
|
||||
|
||||
const StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1];
|
||||
|
||||
for (StateInfo* st2 = states_to_update[i]; st2 != end_state; st2 = st2->previous)
|
||||
FeatureSet::append_changed_indices<Perspective>(ksq, st2->dirtyPiece, removed[i],
|
||||
added[i]);
|
||||
}
|
||||
|
||||
StateInfo* st = computed_st;
|
||||
|
||||
// Now update the accumulators listed in states_to_update[],
|
||||
// where the last element is a sentinel.
|
||||
#ifdef VECTOR
|
||||
|
||||
if (N == 1 && (removed[0].size() == 1 || removed[0].size() == 2) && added[0].size() == 1)
|
||||
{
|
||||
assert(states_to_update[0]);
|
||||
|
||||
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]));
|
||||
}
|
||||
std::memcpy((next->*accPtr).accumulation[Perspective],
|
||||
(computed->*accPtr).accumulation[Perspective],
|
||||
HalfDimensions * sizeof(BiasType));
|
||||
std::memcpy((next->*accPtr).psqtAccumulation[Perspective],
|
||||
(computed->*accPtr).psqtAccumulation[Perspective],
|
||||
PSQTBuckets * sizeof(PSQTWeightType));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
|
||||
assert(added.size() == 1 || added.size() == 2);
|
||||
assert(removed.size() == 1 || removed.size() == 2);
|
||||
assert(added.size() <= removed.size());
|
||||
|
||||
#ifdef VECTOR
|
||||
auto* accIn =
|
||||
reinterpret_cast<const vec_t*>(&(computed->*accPtr).accumulation[Perspective][0]);
|
||||
auto* accOut = reinterpret_cast<vec_t*>(&(next->*accPtr).accumulation[Perspective][0]);
|
||||
|
||||
const IndexType offsetA0 = HalfDimensions * added[0];
|
||||
auto* columnA0 = reinterpret_cast<const vec_t*>(&weights[offsetA0]);
|
||||
const IndexType offsetR0 = HalfDimensions * removed[0];
|
||||
auto* columnR0 = reinterpret_cast<const vec_t*>(&weights[offsetR0]);
|
||||
|
||||
if (removed.size() == 1)
|
||||
{
|
||||
// Load accumulator
|
||||
auto accTileIn = reinterpret_cast<const vec_t*>(
|
||||
&(st->*accPtr).accumulation[Perspective][j * TileHeight]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
acc[k] = vec_load(&accTileIn[k]);
|
||||
for (IndexType i = 0; i < HalfDimensions * sizeof(WeightType) / sizeof(vec_t); ++i)
|
||||
accOut[i] = vec_add_16(vec_sub_16(accIn[i], columnR0[i]), columnA0[i]);
|
||||
}
|
||||
else if (added.size() == 1)
|
||||
{
|
||||
const IndexType offsetR1 = HalfDimensions * removed[1];
|
||||
auto* columnR1 = reinterpret_cast<const vec_t*>(&weights[offsetR1]);
|
||||
|
||||
for (IndexType i = 0; i < N; ++i)
|
||||
{
|
||||
// Difference calculation for the deactivated features
|
||||
for (const auto index : removed[i])
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index + j * TileHeight;
|
||||
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
acc[k] = vec_sub_16(acc[k], column[k]);
|
||||
}
|
||||
for (IndexType i = 0; i < HalfDimensions * sizeof(WeightType) / sizeof(vec_t); ++i)
|
||||
accOut[i] = vec_sub_16(vec_add_16(accIn[i], columnA0[i]),
|
||||
vec_add_16(columnR0[i], columnR1[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const IndexType offsetA1 = HalfDimensions * added[1];
|
||||
auto* columnA1 = reinterpret_cast<const vec_t*>(&weights[offsetA1]);
|
||||
const IndexType offsetR1 = HalfDimensions * removed[1];
|
||||
auto* columnR1 = reinterpret_cast<const vec_t*>(&weights[offsetR1]);
|
||||
|
||||
// Difference calculation for the activated features
|
||||
for (const auto index : added[i])
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index + j * TileHeight;
|
||||
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
acc[k] = vec_add_16(acc[k], column[k]);
|
||||
}
|
||||
|
||||
// Store accumulator
|
||||
auto accTileOut = reinterpret_cast<vec_t*>(
|
||||
&(states_to_update[i]->*accPtr).accumulation[Perspective][j * TileHeight]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
vec_store(&accTileOut[k], acc[k]);
|
||||
}
|
||||
for (IndexType i = 0; i < HalfDimensions * sizeof(WeightType) / sizeof(vec_t); ++i)
|
||||
accOut[i] =
|
||||
vec_add_16(accIn[i], vec_sub_16(vec_add_16(columnA0[i], columnA1[i]),
|
||||
vec_add_16(columnR0[i], columnR1[i])));
|
||||
}
|
||||
|
||||
for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j)
|
||||
auto* accPsqtIn = reinterpret_cast<const psqt_vec_t*>(
|
||||
&(computed->*accPtr).psqtAccumulation[Perspective][0]);
|
||||
auto* accPsqtOut =
|
||||
reinterpret_cast<psqt_vec_t*>(&(next->*accPtr).psqtAccumulation[Perspective][0]);
|
||||
|
||||
const IndexType offsetPsqtA0 = PSQTBuckets * added[0];
|
||||
auto* columnPsqtA0 = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtA0]);
|
||||
const IndexType offsetPsqtR0 = PSQTBuckets * removed[0];
|
||||
auto* columnPsqtR0 = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtR0]);
|
||||
|
||||
if (removed.size() == 1)
|
||||
{
|
||||
// Load accumulator
|
||||
auto accTilePsqtIn = reinterpret_cast<const psqt_vec_t*>(
|
||||
&(st->*accPtr).psqtAccumulation[Perspective][j * PsqtTileHeight]);
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
psqt[k] = vec_load_psqt(&accTilePsqtIn[k]);
|
||||
|
||||
for (IndexType i = 0; i < N; ++i)
|
||||
{
|
||||
// Difference calculation for the deactivated features
|
||||
for (const auto index : removed[i])
|
||||
{
|
||||
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
|
||||
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]);
|
||||
}
|
||||
|
||||
// Difference calculation for the activated features
|
||||
for (const auto index : added[i])
|
||||
{
|
||||
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
|
||||
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
|
||||
}
|
||||
|
||||
// Store accumulator
|
||||
auto accTilePsqtOut = reinterpret_cast<psqt_vec_t*>(
|
||||
&(states_to_update[i]->*accPtr)
|
||||
.psqtAccumulation[Perspective][j * PsqtTileHeight]);
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
vec_store_psqt(&accTilePsqtOut[k], psqt[k]);
|
||||
}
|
||||
for (std::size_t i = 0;
|
||||
i < PSQTBuckets * sizeof(PSQTWeightType) / sizeof(psqt_vec_t); ++i)
|
||||
accPsqtOut[i] = vec_add_psqt_32(vec_sub_psqt_32(accPsqtIn[i], columnPsqtR0[i]),
|
||||
columnPsqtA0[i]);
|
||||
}
|
||||
else if (added.size() == 1)
|
||||
{
|
||||
const IndexType offsetPsqtR1 = PSQTBuckets * removed[1];
|
||||
auto* columnPsqtR1 =
|
||||
reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtR1]);
|
||||
|
||||
for (std::size_t i = 0;
|
||||
i < PSQTBuckets * sizeof(PSQTWeightType) / sizeof(psqt_vec_t); ++i)
|
||||
accPsqtOut[i] =
|
||||
vec_sub_psqt_32(vec_add_psqt_32(accPsqtIn[i], columnPsqtA0[i]),
|
||||
vec_add_psqt_32(columnPsqtR0[i], columnPsqtR1[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
const IndexType offsetPsqtA1 = PSQTBuckets * added[1];
|
||||
auto* columnPsqtA1 =
|
||||
reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtA1]);
|
||||
const IndexType offsetPsqtR1 = PSQTBuckets * removed[1];
|
||||
auto* columnPsqtR1 =
|
||||
reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtR1]);
|
||||
|
||||
for (std::size_t i = 0;
|
||||
i < PSQTBuckets * sizeof(PSQTWeightType) / sizeof(psqt_vec_t); ++i)
|
||||
accPsqtOut[i] = vec_add_psqt_32(
|
||||
accPsqtIn[i],
|
||||
vec_sub_psqt_32(vec_add_psqt_32(columnPsqtA0[i], columnPsqtA1[i]),
|
||||
vec_add_psqt_32(columnPsqtR0[i], columnPsqtR1[i])));
|
||||
}
|
||||
}
|
||||
#else
|
||||
for (IndexType i = 0; i < N; ++i)
|
||||
{
|
||||
std::memcpy((states_to_update[i]->*accPtr).accumulation[Perspective],
|
||||
(st->*accPtr).accumulation[Perspective], HalfDimensions * sizeof(BiasType));
|
||||
|
||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||
(states_to_update[i]->*accPtr).psqtAccumulation[Perspective][k] =
|
||||
(st->*accPtr).psqtAccumulation[Perspective][k];
|
||||
|
||||
st = states_to_update[i];
|
||||
std::memcpy((next->*accPtr).accumulation[Perspective],
|
||||
(computed->*accPtr).accumulation[Perspective],
|
||||
HalfDimensions * sizeof(BiasType));
|
||||
std::memcpy((next->*accPtr).psqtAccumulation[Perspective],
|
||||
(computed->*accPtr).psqtAccumulation[Perspective],
|
||||
PSQTBuckets * sizeof(PSQTWeightType));
|
||||
|
||||
// Difference calculation for the deactivated features
|
||||
for (const auto index : removed[i])
|
||||
for (const auto index : removed)
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index;
|
||||
for (IndexType j = 0; j < HalfDimensions; ++j)
|
||||
(st->*accPtr).accumulation[Perspective][j] -= weights[offset + j];
|
||||
for (IndexType i = 0; i < HalfDimensions; ++i)
|
||||
(next->*accPtr).accumulation[Perspective][i] -= weights[offset + i];
|
||||
|
||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||
(st->*accPtr).psqtAccumulation[Perspective][k] -=
|
||||
psqtWeights[index * PSQTBuckets + k];
|
||||
for (std::size_t i = 0; i < PSQTBuckets; ++i)
|
||||
(next->*accPtr).psqtAccumulation[Perspective][i] -=
|
||||
psqtWeights[index * PSQTBuckets + i];
|
||||
}
|
||||
|
||||
// Difference calculation for the activated features
|
||||
for (const auto index : added[i])
|
||||
for (const auto index : added)
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index;
|
||||
for (IndexType j = 0; j < HalfDimensions; ++j)
|
||||
(st->*accPtr).accumulation[Perspective][j] += weights[offset + j];
|
||||
for (IndexType i = 0; i < HalfDimensions; ++i)
|
||||
(next->*accPtr).accumulation[Perspective][i] += weights[offset + i];
|
||||
|
||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||
(st->*accPtr).psqtAccumulation[Perspective][k] +=
|
||||
psqtWeights[index * PSQTBuckets + k];
|
||||
for (std::size_t i = 0; i < PSQTBuckets; ++i)
|
||||
(next->*accPtr).psqtAccumulation[Perspective][i] +=
|
||||
psqtWeights[index * PSQTBuckets + i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
(next->*accPtr).computed[Perspective] = true;
|
||||
|
||||
if (next != pos.state())
|
||||
update_accumulator_incremental<Perspective>(pos, next);
|
||||
}
|
||||
|
||||
|
||||
template<Color Perspective>
|
||||
void update_accumulator_refresh_cache(const Position& pos,
|
||||
AccumulatorCaches::Cache<HalfDimensions>* cache) const {
|
||||
@@ -735,88 +688,88 @@ class FeatureTransformer {
|
||||
accumulator.computed[Perspective] = true;
|
||||
|
||||
#ifdef VECTOR
|
||||
vec_t acc[NumRegs];
|
||||
psqt_vec_t psqt[NumPsqtRegs];
|
||||
vec_t acc[Tiling::NumRegs];
|
||||
psqt_vec_t psqt[Tiling::NumPsqtRegs];
|
||||
|
||||
for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
|
||||
for (IndexType j = 0; j < HalfDimensions / Tiling::TileHeight; ++j)
|
||||
{
|
||||
auto accTile =
|
||||
reinterpret_cast<vec_t*>(&accumulator.accumulation[Perspective][j * TileHeight]);
|
||||
auto entryTile = reinterpret_cast<vec_t*>(&entry.accumulation[j * TileHeight]);
|
||||
auto* accTile = reinterpret_cast<vec_t*>(
|
||||
&accumulator.accumulation[Perspective][j * Tiling::TileHeight]);
|
||||
auto* entryTile = reinterpret_cast<vec_t*>(&entry.accumulation[j * Tiling::TileHeight]);
|
||||
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
for (IndexType k = 0; k < Tiling::NumRegs; ++k)
|
||||
acc[k] = entryTile[k];
|
||||
|
||||
int i = 0;
|
||||
for (; i < int(std::min(removed.size(), added.size())); ++i)
|
||||
std::size_t i = 0;
|
||||
for (; i < std::min(removed.size(), added.size()); ++i)
|
||||
{
|
||||
IndexType indexR = removed[i];
|
||||
const IndexType offsetR = HalfDimensions * indexR + j * TileHeight;
|
||||
auto columnR = reinterpret_cast<const vec_t*>(&weights[offsetR]);
|
||||
const IndexType offsetR = HalfDimensions * indexR + j * Tiling::TileHeight;
|
||||
auto* columnR = reinterpret_cast<const vec_t*>(&weights[offsetR]);
|
||||
IndexType indexA = added[i];
|
||||
const IndexType offsetA = HalfDimensions * indexA + j * TileHeight;
|
||||
auto columnA = reinterpret_cast<const vec_t*>(&weights[offsetA]);
|
||||
const IndexType offsetA = HalfDimensions * indexA + j * Tiling::TileHeight;
|
||||
auto* columnA = reinterpret_cast<const vec_t*>(&weights[offsetA]);
|
||||
|
||||
for (unsigned k = 0; k < NumRegs; ++k)
|
||||
for (IndexType k = 0; k < Tiling::NumRegs; ++k)
|
||||
acc[k] = vec_add_16(acc[k], vec_sub_16(columnA[k], columnR[k]));
|
||||
}
|
||||
for (; i < int(removed.size()); ++i)
|
||||
for (; i < removed.size(); ++i)
|
||||
{
|
||||
IndexType index = removed[i];
|
||||
const IndexType offset = HalfDimensions * index + j * TileHeight;
|
||||
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
|
||||
const IndexType offset = HalfDimensions * index + j * Tiling::TileHeight;
|
||||
auto* column = reinterpret_cast<const vec_t*>(&weights[offset]);
|
||||
|
||||
for (unsigned k = 0; k < NumRegs; ++k)
|
||||
for (IndexType k = 0; k < Tiling::NumRegs; ++k)
|
||||
acc[k] = vec_sub_16(acc[k], column[k]);
|
||||
}
|
||||
for (; i < int(added.size()); ++i)
|
||||
for (; i < added.size(); ++i)
|
||||
{
|
||||
IndexType index = added[i];
|
||||
const IndexType offset = HalfDimensions * index + j * TileHeight;
|
||||
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
|
||||
const IndexType offset = HalfDimensions * index + j * Tiling::TileHeight;
|
||||
auto* column = reinterpret_cast<const vec_t*>(&weights[offset]);
|
||||
|
||||
for (unsigned k = 0; k < NumRegs; ++k)
|
||||
for (IndexType k = 0; k < Tiling::NumRegs; ++k)
|
||||
acc[k] = vec_add_16(acc[k], column[k]);
|
||||
}
|
||||
|
||||
for (IndexType k = 0; k < NumRegs; k++)
|
||||
for (IndexType k = 0; k < Tiling::NumRegs; k++)
|
||||
vec_store(&entryTile[k], acc[k]);
|
||||
for (IndexType k = 0; k < NumRegs; k++)
|
||||
for (IndexType k = 0; k < Tiling::NumRegs; k++)
|
||||
vec_store(&accTile[k], acc[k]);
|
||||
}
|
||||
|
||||
for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j)
|
||||
for (IndexType j = 0; j < PSQTBuckets / Tiling::PsqtTileHeight; ++j)
|
||||
{
|
||||
auto accTilePsqt = reinterpret_cast<psqt_vec_t*>(
|
||||
&accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]);
|
||||
auto entryTilePsqt =
|
||||
reinterpret_cast<psqt_vec_t*>(&entry.psqtAccumulation[j * PsqtTileHeight]);
|
||||
auto* accTilePsqt = reinterpret_cast<psqt_vec_t*>(
|
||||
&accumulator.psqtAccumulation[Perspective][j * Tiling::PsqtTileHeight]);
|
||||
auto* entryTilePsqt =
|
||||
reinterpret_cast<psqt_vec_t*>(&entry.psqtAccumulation[j * Tiling::PsqtTileHeight]);
|
||||
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
for (std::size_t k = 0; k < Tiling::NumPsqtRegs; ++k)
|
||||
psqt[k] = entryTilePsqt[k];
|
||||
|
||||
for (int i = 0; i < int(removed.size()); ++i)
|
||||
for (std::size_t i = 0; i < removed.size(); ++i)
|
||||
{
|
||||
IndexType index = removed[i];
|
||||
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
|
||||
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
||||
const IndexType offset = PSQTBuckets * index + j * Tiling::PsqtTileHeight;
|
||||
auto* columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
||||
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
for (std::size_t k = 0; k < Tiling::NumPsqtRegs; ++k)
|
||||
psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]);
|
||||
}
|
||||
for (int i = 0; i < int(added.size()); ++i)
|
||||
for (std::size_t i = 0; i < added.size(); ++i)
|
||||
{
|
||||
IndexType index = added[i];
|
||||
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
|
||||
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
||||
const IndexType offset = PSQTBuckets * index + j * Tiling::PsqtTileHeight;
|
||||
auto* columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
||||
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
for (std::size_t k = 0; k < Tiling::NumPsqtRegs; ++k)
|
||||
psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
|
||||
}
|
||||
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
for (std::size_t k = 0; k < Tiling::NumPsqtRegs; ++k)
|
||||
vec_store_psqt(&entryTilePsqt[k], psqt[k]);
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
for (std::size_t k = 0; k < Tiling::NumPsqtRegs; ++k)
|
||||
vec_store_psqt(&accTilePsqt[k], psqt[k]);
|
||||
}
|
||||
|
||||
@@ -858,60 +811,18 @@ class FeatureTransformer {
|
||||
entry.byTypeBB[pt] = pos.pieces(pt);
|
||||
}
|
||||
|
||||
template<Color Perspective>
|
||||
void hint_common_access_for_perspective(const Position& pos,
|
||||
AccumulatorCaches::Cache<HalfDimensions>* cache) const {
|
||||
|
||||
// Works like update_accumulator, but performs less work.
|
||||
// Updates ONLY the accumulator for pos.
|
||||
|
||||
// Look for a usable accumulator of an earlier position. We keep track
|
||||
// of the estimated gain in terms of features to be added/subtracted.
|
||||
// Fast early exit.
|
||||
if ((pos.state()->*accPtr).computed[Perspective])
|
||||
return;
|
||||
|
||||
auto [oldest_st, _] = try_find_computed_accumulator<Perspective>(pos);
|
||||
|
||||
if ((oldest_st->*accPtr).computed[Perspective])
|
||||
{
|
||||
// Only update current position accumulator to minimize work
|
||||
StateInfo* states_to_update[1] = {pos.state()};
|
||||
update_accumulator_incremental<Perspective, 1>(pos, oldest_st, states_to_update);
|
||||
}
|
||||
else
|
||||
update_accumulator_refresh_cache<Perspective>(pos, cache);
|
||||
}
|
||||
|
||||
template<Color Perspective>
|
||||
void update_accumulator(const Position& pos,
|
||||
AccumulatorCaches::Cache<HalfDimensions>* cache) const {
|
||||
if ((pos.state()->*accPtr).computed[Perspective])
|
||||
return;
|
||||
StateInfo* oldest = try_find_computed_accumulator<Perspective>(pos);
|
||||
|
||||
auto [oldest_st, next] = try_find_computed_accumulator<Perspective>(pos);
|
||||
|
||||
if ((oldest_st->*accPtr).computed[Perspective])
|
||||
{
|
||||
if (next == nullptr)
|
||||
return;
|
||||
|
||||
// Now update the accumulators listed in states_to_update[], where
|
||||
// the last element is a sentinel. Currently we update two accumulators:
|
||||
// 1. for the current position
|
||||
// 2. the next accumulator after the computed one
|
||||
// The heuristic may change in the future.
|
||||
if (next == pos.state())
|
||||
{
|
||||
StateInfo* states_to_update[1] = {next};
|
||||
|
||||
update_accumulator_incremental<Perspective, 1>(pos, oldest_st, states_to_update);
|
||||
}
|
||||
else
|
||||
{
|
||||
StateInfo* states_to_update[2] = {next, pos.state()};
|
||||
|
||||
update_accumulator_incremental<Perspective, 2>(pos, oldest_st, states_to_update);
|
||||
}
|
||||
}
|
||||
if ((oldest->*accPtr).computed[Perspective] && oldest != pos.state())
|
||||
// Start from the oldest computed accumulator, update all the
|
||||
// accumulators up to the current position.
|
||||
update_accumulator_incremental<Perspective>(pos, oldest);
|
||||
else
|
||||
update_accumulator_refresh_cache<Perspective>(pos, cache);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -126,7 +126,7 @@ trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::Accumulat
|
||||
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)
|
||||
if (is_valid(value))
|
||||
format_cp_compact(value, &board[y + 2][x + 2], pos);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
|
||||
+7
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
@@ -653,7 +654,7 @@ class NumaConfig {
|
||||
NumaIndex n = 0;
|
||||
for (auto&& nodeStr : split(s, ":"))
|
||||
{
|
||||
auto indices = indices_from_shortened_string(nodeStr);
|
||||
auto indices = indices_from_shortened_string(std::string(nodeStr));
|
||||
if (!indices.empty())
|
||||
{
|
||||
for (auto idx : indices)
|
||||
@@ -1015,7 +1016,7 @@ class NumaConfig {
|
||||
if (s.empty())
|
||||
return indices;
|
||||
|
||||
for (const std::string& ss : split(s, ","))
|
||||
for (const auto& ss : split(s, ","))
|
||||
{
|
||||
if (ss.empty())
|
||||
continue;
|
||||
@@ -1023,13 +1024,13 @@ class NumaConfig {
|
||||
auto parts = split(ss, "-");
|
||||
if (parts.size() == 1)
|
||||
{
|
||||
const CpuIndex c = CpuIndex{str_to_size_t(parts[0])};
|
||||
const CpuIndex c = CpuIndex{str_to_size_t(std::string(parts[0]))};
|
||||
indices.emplace_back(c);
|
||||
}
|
||||
else if (parts.size() == 2)
|
||||
{
|
||||
const CpuIndex cfirst = CpuIndex{str_to_size_t(parts[0])};
|
||||
const CpuIndex clast = CpuIndex{str_to_size_t(parts[1])};
|
||||
const CpuIndex cfirst = CpuIndex{str_to_size_t(std::string(parts[0]))};
|
||||
const CpuIndex clast = CpuIndex{str_to_size_t(std::string(parts[1]))};
|
||||
for (size_t c = cfirst; c <= clast; ++c)
|
||||
{
|
||||
indices.emplace_back(c);
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
|
||||
+89
-56
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -119,6 +119,9 @@ void Position::init() {
|
||||
for (Piece pc : Pieces)
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
Zobrist::psq[pc][s] = rng.rand<Key>();
|
||||
// pawns on these squares will promote
|
||||
std::fill_n(Zobrist::psq[W_PAWN] + SQ_A8, 8, 0);
|
||||
std::fill_n(Zobrist::psq[B_PAWN], 8, 0);
|
||||
|
||||
for (File f = FILE_A; f <= FILE_H; ++f)
|
||||
Zobrist::enpassant[f] = rng.rand<Key>();
|
||||
@@ -334,8 +337,10 @@ void Position::set_check_info() const {
|
||||
// The function is only used when a new position is set up
|
||||
void Position::set_state() const {
|
||||
|
||||
st->key = st->materialKey = 0;
|
||||
st->pawnKey = Zobrist::noPawns;
|
||||
st->key = st->materialKey = 0;
|
||||
st->minorPieceKey = 0;
|
||||
st->nonPawnKey[WHITE] = st->nonPawnKey[BLACK] = 0;
|
||||
st->pawnKey = Zobrist::noPawns;
|
||||
st->nonPawnMaterial[WHITE] = st->nonPawnMaterial[BLACK] = VALUE_ZERO;
|
||||
st->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);
|
||||
|
||||
@@ -350,8 +355,18 @@ void Position::set_state() const {
|
||||
if (type_of(pc) == PAWN)
|
||||
st->pawnKey ^= Zobrist::psq[pc][s];
|
||||
|
||||
else if (type_of(pc) != KING)
|
||||
st->nonPawnMaterial[color_of(pc)] += PieceValue[pc];
|
||||
else
|
||||
{
|
||||
st->nonPawnKey[color_of(pc)] ^= Zobrist::psq[pc][s];
|
||||
|
||||
if (type_of(pc) != KING)
|
||||
{
|
||||
st->nonPawnMaterial[color_of(pc)] += PieceValue[pc];
|
||||
|
||||
if (type_of(pc) <= BISHOP)
|
||||
st->minorPieceKey ^= Zobrist::psq[pc][s];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (st->epSquare != SQ_NONE)
|
||||
@@ -364,7 +379,7 @@ void Position::set_state() const {
|
||||
|
||||
for (Piece pc : Pieces)
|
||||
for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
|
||||
st->materialKey ^= Zobrist::psq[pc][cnt];
|
||||
st->materialKey ^= Zobrist::psq[pc][8 + cnt];
|
||||
}
|
||||
|
||||
|
||||
@@ -472,14 +487,23 @@ void Position::update_slider_blockers(Color c) const {
|
||||
// Slider attacks use the occupied bitboard to indicate occupancy.
|
||||
Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
|
||||
|
||||
return (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN))
|
||||
| (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN))
|
||||
| (attacks_bb<KNIGHT>(s) & pieces(KNIGHT))
|
||||
| (attacks_bb<ROOK>(s, occupied) & pieces(ROOK, QUEEN))
|
||||
return (attacks_bb<ROOK>(s, occupied) & pieces(ROOK, QUEEN))
|
||||
| (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
|
||||
| (attacks_bb<KING>(s) & pieces(KING));
|
||||
| (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN))
|
||||
| (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN))
|
||||
| (attacks_bb<KNIGHT>(s) & pieces(KNIGHT)) | (attacks_bb<KING>(s) & pieces(KING));
|
||||
}
|
||||
|
||||
bool Position::attackers_to_exist(Square s, Bitboard occupied, Color c) const {
|
||||
|
||||
return ((attacks_bb<ROOK>(s) & pieces(c, ROOK, QUEEN))
|
||||
&& (attacks_bb<ROOK>(s, occupied) & pieces(c, ROOK, QUEEN)))
|
||||
|| ((attacks_bb<BISHOP>(s) & pieces(c, BISHOP, QUEEN))
|
||||
&& (attacks_bb<BISHOP>(s, occupied) & pieces(c, BISHOP, QUEEN)))
|
||||
|| (((pawn_attacks_bb(~c, s) & pieces(PAWN)) | (attacks_bb<KNIGHT>(s) & pieces(KNIGHT))
|
||||
| (attacks_bb<KING>(s) & pieces(KING)))
|
||||
& pieces(c));
|
||||
}
|
||||
|
||||
// Tests whether a pseudo-legal move is legal
|
||||
bool Position::legal(Move m) const {
|
||||
@@ -521,7 +545,7 @@ bool Position::legal(Move m) const {
|
||||
Direction step = to > from ? WEST : EAST;
|
||||
|
||||
for (Square s = to; s != from; s += step)
|
||||
if (attackers_to(s) & pieces(~us))
|
||||
if (attackers_to_exist(s, pieces(), ~us))
|
||||
return false;
|
||||
|
||||
// In case of Chess960, verify if the Rook blocks some checks.
|
||||
@@ -532,7 +556,7 @@ bool Position::legal(Move m) const {
|
||||
// If the moving piece is a king, check whether the destination square is
|
||||
// attacked by the opponent.
|
||||
if (type_of(piece_on(from)) == KING)
|
||||
return !(attackers_to(to, pieces() ^ from) & pieces(~us));
|
||||
return !(attackers_to_exist(to, pieces() ^ from, ~us));
|
||||
|
||||
// A non-king move is legal if and only if it is not pinned or it
|
||||
// is moving along the ray towards or away from the king.
|
||||
@@ -601,7 +625,7 @@ bool Position::pseudo_legal(const Move m) const {
|
||||
}
|
||||
// In case of king moves under check we have to remove the king so as to catch
|
||||
// invalid moves like b1a1 when opposite queen is on c1.
|
||||
else if (attackers_to(to, pieces() ^ from) & pieces(~us))
|
||||
else if (attackers_to_exist(to, pieces() ^ from, ~us))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -659,7 +683,12 @@ bool Position::gives_check(Move m) const {
|
||||
// Makes a move, and saves all information necessary
|
||||
// to a StateInfo object. The move is assumed to be legal. Pseudo-legal
|
||||
// moves should be filtered out before this function is called.
|
||||
void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
// If a pointer to the TT table is passed, the entry for the new position
|
||||
// will be prefetched
|
||||
void Position::do_move(Move m,
|
||||
StateInfo& newSt,
|
||||
bool givesCheck,
|
||||
const TranspositionTable* tt = nullptr) {
|
||||
|
||||
assert(m.is_ok());
|
||||
assert(&newSt != st);
|
||||
@@ -671,6 +700,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
// our state pointer to point to the new (ready to be updated) state.
|
||||
std::memcpy(&newSt, st, offsetof(StateInfo, key));
|
||||
newSt.previous = st;
|
||||
st->next = &newSt;
|
||||
st = &newSt;
|
||||
|
||||
// Increment ply counters. In particular, rule50 will be reset to zero later on
|
||||
@@ -706,6 +736,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
do_castling<true>(us, from, to, rfrom, rto);
|
||||
|
||||
k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
|
||||
st->nonPawnKey[us] ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
|
||||
captured = NO_PIECE;
|
||||
}
|
||||
|
||||
@@ -731,7 +762,13 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
st->pawnKey ^= Zobrist::psq[captured][capsq];
|
||||
}
|
||||
else
|
||||
{
|
||||
st->nonPawnMaterial[them] -= PieceValue[captured];
|
||||
st->nonPawnKey[them] ^= Zobrist::psq[captured][capsq];
|
||||
|
||||
if (type_of(captured) <= BISHOP)
|
||||
st->minorPieceKey ^= Zobrist::psq[captured][capsq];
|
||||
}
|
||||
|
||||
dp.dirty_num = 2; // 1 piece moved, 1 piece captured
|
||||
dp.piece[1] = captured;
|
||||
@@ -742,7 +779,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
remove_piece(capsq);
|
||||
|
||||
k ^= Zobrist::psq[captured][capsq];
|
||||
st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]];
|
||||
st->materialKey ^= Zobrist::psq[captured][8 + pieceCount[captured]];
|
||||
|
||||
// Reset rule 50 counter
|
||||
st->rule50 = 0;
|
||||
@@ -789,7 +826,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
|
||||
else if (m.type_of() == PROMOTION)
|
||||
{
|
||||
Piece promotion = make_piece(us, m.promotion_type());
|
||||
Piece promotion = make_piece(us, m.promotion_type());
|
||||
PieceType promotionType = type_of(promotion);
|
||||
|
||||
assert(relative_rank(us, to) == RANK_8);
|
||||
assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
|
||||
@@ -805,10 +843,13 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
dp.dirty_num++;
|
||||
|
||||
// Update hash keys
|
||||
k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to];
|
||||
st->pawnKey ^= Zobrist::psq[pc][to];
|
||||
st->materialKey ^=
|
||||
Zobrist::psq[promotion][pieceCount[promotion] - 1] ^ Zobrist::psq[pc][pieceCount[pc]];
|
||||
// Zobrist::psq[pc][to] is zero, so we don't need to clear it
|
||||
k ^= Zobrist::psq[promotion][to];
|
||||
st->materialKey ^= Zobrist::psq[promotion][8 + pieceCount[promotion] - 1]
|
||||
^ Zobrist::psq[pc][8 + pieceCount[pc]];
|
||||
|
||||
if (promotionType <= BISHOP)
|
||||
st->minorPieceKey ^= Zobrist::psq[promotion][to];
|
||||
|
||||
// Update material
|
||||
st->nonPawnMaterial[us] += PieceValue[promotion];
|
||||
@@ -821,11 +862,21 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
st->rule50 = 0;
|
||||
}
|
||||
|
||||
// Set capture piece
|
||||
st->capturedPiece = captured;
|
||||
else
|
||||
{
|
||||
st->nonPawnKey[us] ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
|
||||
|
||||
if (type_of(pc) <= BISHOP)
|
||||
st->minorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
|
||||
}
|
||||
|
||||
// Update the key with the final value
|
||||
st->key = k;
|
||||
if (tt)
|
||||
prefetch(tt->first_entry(key()));
|
||||
|
||||
// Set capture piece
|
||||
st->capturedPiece = captured;
|
||||
|
||||
// Calculate checkers bitboard (if move gives check)
|
||||
st->checkersBB = givesCheck ? attackers_to(square<KING>(them)) & pieces(us) : 0;
|
||||
@@ -955,7 +1006,7 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ
|
||||
|
||||
// Used to do a "null move": it flips
|
||||
// the side to move without executing any move on the board.
|
||||
void Position::do_null_move(StateInfo& newSt, TranspositionTable& tt) {
|
||||
void Position::do_null_move(StateInfo& newSt, const TranspositionTable& tt) {
|
||||
|
||||
assert(!checkers());
|
||||
assert(&newSt != st);
|
||||
@@ -963,13 +1014,9 @@ void Position::do_null_move(StateInfo& newSt, TranspositionTable& tt) {
|
||||
std::memcpy(&newSt, st, offsetof(StateInfo, accumulatorBig));
|
||||
|
||||
newSt.previous = st;
|
||||
st->next = &newSt;
|
||||
st = &newSt;
|
||||
|
||||
st->dirtyPiece.dirty_num = 0;
|
||||
st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator()
|
||||
st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] =
|
||||
st->accumulatorSmall.computed[WHITE] = st->accumulatorSmall.computed[BLACK] = false;
|
||||
|
||||
if (st->epSquare != SQ_NONE)
|
||||
{
|
||||
st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
|
||||
@@ -977,9 +1024,13 @@ void Position::do_null_move(StateInfo& newSt, TranspositionTable& tt) {
|
||||
}
|
||||
|
||||
st->key ^= Zobrist::side;
|
||||
++st->rule50;
|
||||
prefetch(tt.first_entry(key()));
|
||||
|
||||
st->dirtyPiece.dirty_num = 0;
|
||||
st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator()
|
||||
st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] =
|
||||
st->accumulatorSmall.computed[WHITE] = st->accumulatorSmall.computed[BLACK] = false;
|
||||
|
||||
st->pliesFromNull = 0;
|
||||
|
||||
sideToMove = ~sideToMove;
|
||||
@@ -1002,26 +1053,6 @@ void Position::undo_null_move() {
|
||||
}
|
||||
|
||||
|
||||
// Computes the new hash key after the given move. Needed
|
||||
// for speculative prefetch. It doesn't recognize special moves like castling,
|
||||
// en passant and promotions.
|
||||
Key Position::key_after(Move m) const {
|
||||
|
||||
Square from = m.from_sq();
|
||||
Square to = m.to_sq();
|
||||
Piece pc = piece_on(from);
|
||||
Piece captured = piece_on(to);
|
||||
Key k = st->key ^ Zobrist::side;
|
||||
|
||||
if (captured)
|
||||
k ^= Zobrist::psq[captured][to];
|
||||
|
||||
k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from];
|
||||
|
||||
return (captured || type_of(pc) == PAWN) ? k : adjust_key50<true>(k);
|
||||
}
|
||||
|
||||
|
||||
// Tests if the SEE (Static Exchange Evaluation)
|
||||
// value of move is greater or equal to the given threshold. We'll use an
|
||||
// algorithm similar to alpha-beta pruning with a null window.
|
||||
@@ -1109,8 +1140,9 @@ bool Position::see_ge(Move m, int threshold) const {
|
||||
|
||||
else if ((bb = stmAttackers & pieces(QUEEN)))
|
||||
{
|
||||
if ((swap = QueenValue - swap) < res)
|
||||
break;
|
||||
swap = QueenValue - swap;
|
||||
// implies that the previous recapture was done by a higher rated piece than a Queen (King is excluded)
|
||||
assert(swap >= res);
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
|
||||
attackers |= (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN))
|
||||
@@ -1133,11 +1165,12 @@ bool Position::is_draw(int ply) const {
|
||||
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
|
||||
return true;
|
||||
|
||||
// Return a draw score if a position repeats once earlier but strictly
|
||||
// after the root, or repeats twice before or at the root.
|
||||
return st->repetition && st->repetition < ply;
|
||||
return is_repetition(ply);
|
||||
}
|
||||
|
||||
// Return a draw score if a position repeats once earlier but strictly
|
||||
// after the root, or repeats twice before or at the root.
|
||||
bool Position::is_repetition(int ply) const { return st->repetition && st->repetition < ply; }
|
||||
|
||||
// Tests whether there has been at least one repetition
|
||||
// of positions since the last capture or pawn move.
|
||||
@@ -1253,7 +1286,7 @@ bool Position::pos_is_ok() const {
|
||||
return true;
|
||||
|
||||
if (pieceCount[W_KING] != 1 || pieceCount[B_KING] != 1
|
||||
|| attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove))
|
||||
|| attackers_to_exist(square<KING>(~sideToMove), pieces(), sideToMove))
|
||||
assert(0 && "pos_is_ok: Kings");
|
||||
|
||||
if ((pieces(PAWN) & (Rank1BB | Rank8BB)) || pieceCount[W_PAWN] > 8 || pieceCount[B_PAWN] > 8)
|
||||
|
||||
+18
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -43,6 +43,8 @@ struct StateInfo {
|
||||
// Copied when making a move
|
||||
Key materialKey;
|
||||
Key pawnKey;
|
||||
Key minorPieceKey;
|
||||
Key nonPawnKey[COLOR_NB];
|
||||
Value nonPawnMaterial[COLOR_NB];
|
||||
int castlingRights;
|
||||
int rule50;
|
||||
@@ -53,6 +55,7 @@ struct StateInfo {
|
||||
Key key;
|
||||
Bitboard checkersBB;
|
||||
StateInfo* previous;
|
||||
StateInfo* next;
|
||||
Bitboard blockersForKing[COLOR_NB];
|
||||
Bitboard pinners[COLOR_NB];
|
||||
Bitboard checkSquares[PIECE_TYPE_NB];
|
||||
@@ -122,6 +125,7 @@ class Position {
|
||||
// Attacks to/from a given square
|
||||
Bitboard attackers_to(Square s) const;
|
||||
Bitboard attackers_to(Square s, Bitboard occupied) const;
|
||||
bool attackers_to_exist(Square s, Bitboard occupied, Color c) const;
|
||||
void update_slider_blockers(Color c) const;
|
||||
template<PieceType Pt>
|
||||
Bitboard attacks_by(Color c) const;
|
||||
@@ -136,10 +140,10 @@ class Position {
|
||||
Piece captured_piece() const;
|
||||
|
||||
// Doing and undoing moves
|
||||
void do_move(Move m, StateInfo& newSt);
|
||||
void do_move(Move m, StateInfo& newSt, bool givesCheck);
|
||||
void do_move(Move m, StateInfo& newSt, const TranspositionTable* tt);
|
||||
void do_move(Move m, StateInfo& newSt, bool givesCheck, const TranspositionTable* tt);
|
||||
void undo_move(Move m);
|
||||
void do_null_move(StateInfo& newSt, TranspositionTable& tt);
|
||||
void do_null_move(StateInfo& newSt, const TranspositionTable& tt);
|
||||
void undo_null_move();
|
||||
|
||||
// Static Exchange Evaluation
|
||||
@@ -147,15 +151,17 @@ class Position {
|
||||
|
||||
// Accessing hash keys
|
||||
Key key() const;
|
||||
Key key_after(Move m) const;
|
||||
Key material_key() const;
|
||||
Key pawn_key() const;
|
||||
Key minor_piece_key() const;
|
||||
Key non_pawn_key(Color c) const;
|
||||
|
||||
// Other properties of the position
|
||||
Color side_to_move() const;
|
||||
int game_ply() const;
|
||||
bool is_chess960() const;
|
||||
bool is_draw(int ply) const;
|
||||
bool is_repetition(int ply) const;
|
||||
bool upcoming_repetition(int ply) const;
|
||||
bool has_repeated() const;
|
||||
int rule50_count() const;
|
||||
@@ -297,6 +303,10 @@ inline Key Position::pawn_key() const { return st->pawnKey; }
|
||||
|
||||
inline Key Position::material_key() const { return st->materialKey; }
|
||||
|
||||
inline Key Position::minor_piece_key() const { return st->minorPieceKey; }
|
||||
|
||||
inline Key Position::non_pawn_key(Color c) const { return st->nonPawnKey[c]; }
|
||||
|
||||
inline Value Position::non_pawn_material(Color c) const { return st->nonPawnMaterial[c]; }
|
||||
|
||||
inline Value Position::non_pawn_material() const {
|
||||
@@ -355,7 +365,9 @@ inline void Position::move_piece(Square from, Square to) {
|
||||
board[to] = pc;
|
||||
}
|
||||
|
||||
inline void Position::do_move(Move m, StateInfo& newSt) { do_move(m, newSt, gives_check(m)); }
|
||||
inline void Position::do_move(Move m, StateInfo& newSt, const TranspositionTable* tt = nullptr) {
|
||||
do_move(m, newSt, gives_check(m), tt);
|
||||
}
|
||||
|
||||
inline StateInfo* Position::state() const { return st; }
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -29,7 +29,7 @@ namespace Stockfish {
|
||||
Score::Score(Value v, const Position& pos) {
|
||||
assert(-VALUE_INFINITE < v && v < VALUE_INFINITE);
|
||||
|
||||
if (std::abs(v) < VALUE_TB_WIN_IN_MAX_PLY)
|
||||
if (!is_decisive(v))
|
||||
{
|
||||
score = InternalUnits{UCIEngine::to_cp(v, pos)};
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
|
||||
+401
-344
File diff suppressed because it is too large
Load Diff
+42
-26
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -31,8 +31,8 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "history.h"
|
||||
#include "misc.h"
|
||||
#include "movepick.h"
|
||||
#include "nnue/network.h"
|
||||
#include "nnue/nnue_accumulator.h"
|
||||
#include "numa.h"
|
||||
@@ -61,18 +61,21 @@ namespace Search {
|
||||
// shallower and deeper in the tree during the search. Each search thread has
|
||||
// its own array of Stack objects, indexed by the current ply.
|
||||
struct Stack {
|
||||
Move* pv;
|
||||
PieceToHistory* continuationHistory;
|
||||
int ply;
|
||||
Move currentMove;
|
||||
Move excludedMove;
|
||||
Value staticEval;
|
||||
int statScore;
|
||||
int moveCount;
|
||||
bool inCheck;
|
||||
bool ttPv;
|
||||
bool ttHit;
|
||||
int cutoffCnt;
|
||||
Move* pv;
|
||||
PieceToHistory* continuationHistory;
|
||||
CorrectionHistory<PieceTo>* continuationCorrectionHistory;
|
||||
int ply;
|
||||
Move currentMove;
|
||||
Move excludedMove;
|
||||
Value staticEval;
|
||||
int statScore;
|
||||
int moveCount;
|
||||
bool inCheck;
|
||||
bool ttPv;
|
||||
bool ttHit;
|
||||
int cutoffCnt;
|
||||
int reduction;
|
||||
bool isTTMove;
|
||||
};
|
||||
|
||||
|
||||
@@ -90,15 +93,16 @@ struct RootMove {
|
||||
return m.score != score ? m.score < score : m.previousScore < previousScore;
|
||||
}
|
||||
|
||||
uint64_t effort = 0;
|
||||
Value score = -VALUE_INFINITE;
|
||||
Value previousScore = -VALUE_INFINITE;
|
||||
Value averageScore = -VALUE_INFINITE;
|
||||
Value uciScore = -VALUE_INFINITE;
|
||||
bool scoreLowerbound = false;
|
||||
bool scoreUpperbound = false;
|
||||
int selDepth = 0;
|
||||
int tbRank = 0;
|
||||
uint64_t effort = 0;
|
||||
Value score = -VALUE_INFINITE;
|
||||
Value previousScore = -VALUE_INFINITE;
|
||||
Value averageScore = -VALUE_INFINITE;
|
||||
Value meanSquaredScore = -VALUE_INFINITE * VALUE_INFINITE;
|
||||
Value uciScore = -VALUE_INFINITE;
|
||||
bool scoreLowerbound = false;
|
||||
bool scoreUpperbound = false;
|
||||
int selDepth = 0;
|
||||
int tbRank = 0;
|
||||
Value tbScore;
|
||||
std::vector<Move> pv;
|
||||
};
|
||||
@@ -124,7 +128,6 @@ struct LimitsType {
|
||||
int movestogo, depth, mate, perft, infinite;
|
||||
uint64_t nodes;
|
||||
bool ponderMode;
|
||||
Square capSq;
|
||||
};
|
||||
|
||||
|
||||
@@ -277,11 +280,17 @@ class Worker {
|
||||
void ensure_network_replicated();
|
||||
|
||||
// Public because they need to be updatable by the stats
|
||||
ButterflyHistory mainHistory;
|
||||
ButterflyHistory mainHistory;
|
||||
LowPlyHistory lowPlyHistory;
|
||||
|
||||
CapturePieceToHistory captureHistory;
|
||||
ContinuationHistory continuationHistory[2][2];
|
||||
PawnHistory pawnHistory;
|
||||
CorrectionHistory correctionHistory;
|
||||
|
||||
CorrectionHistory<Pawn> pawnCorrectionHistory;
|
||||
CorrectionHistory<Minor> minorPieceCorrectionHistory;
|
||||
CorrectionHistory<NonPawn> nonPawnCorrectionHistory[COLOR_NB];
|
||||
CorrectionHistory<Continuation> continuationCorrectionHistory;
|
||||
|
||||
private:
|
||||
void iterative_deepening();
|
||||
@@ -305,6 +314,8 @@ class Worker {
|
||||
TimePoint elapsed() const;
|
||||
TimePoint elapsed_time() const;
|
||||
|
||||
Value evaluate(const Position&);
|
||||
|
||||
LimitsType limits;
|
||||
|
||||
size_t pvIdx, pvLast;
|
||||
@@ -342,6 +353,11 @@ class Worker {
|
||||
friend class SearchManager;
|
||||
};
|
||||
|
||||
struct ConthistBonus {
|
||||
int index;
|
||||
int weight;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Search
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -1620,7 +1620,7 @@ bool Tablebases::root_probe(Position& pos,
|
||||
WDLScore wdl = -probe_wdl(pos, &result);
|
||||
dtz = dtz_before_zeroing(wdl);
|
||||
}
|
||||
else if (pos.is_draw(1))
|
||||
else if ((rule50 && pos.is_draw(1)) || pos.is_repetition(1))
|
||||
{
|
||||
// In case a root move leads to a draw by repetition or 50-move rule,
|
||||
// we set dtz to zero. Note: since we are only 1 ply from the root,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -329,13 +329,13 @@ Thread* ThreadPool::get_best_thread() const {
|
||||
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 bestThreadInProvenWin = is_win(bestThreadScore);
|
||||
const bool newThreadInProvenWin = is_win(newThreadScore);
|
||||
|
||||
const bool bestThreadInProvenLoss =
|
||||
bestThreadScore != -VALUE_INFINITE && bestThreadScore <= VALUE_TB_LOSS_IN_MAX_PLY;
|
||||
bestThreadScore != -VALUE_INFINITE && is_loss(bestThreadScore);
|
||||
const bool newThreadInProvenLoss =
|
||||
newThreadScore != -VALUE_INFINITE && newThreadScore <= VALUE_TB_LOSS_IN_MAX_PLY;
|
||||
newThreadScore != -VALUE_INFINITE && is_loss(newThreadScore);
|
||||
|
||||
// We make sure not to pick a thread with truncated principal variation
|
||||
const bool betterVotingValue =
|
||||
@@ -355,7 +355,7 @@ Thread* ThreadPool::get_best_thread() const {
|
||||
bestThread = th.get();
|
||||
}
|
||||
else if (newThreadInProvenWin || newThreadInProvenLoss
|
||||
|| (newThreadScore > VALUE_TB_LOSS_IN_MAX_PLY
|
||||
|| (!is_loss(newThreadScore)
|
||||
&& (newThreadMoveVote > bestThreadMoveVote
|
||||
|| (newThreadMoveVote == bestThreadMoveVote && betterVotingValue))))
|
||||
bestThread = th.get();
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
|
||||
+19
-16
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -87,18 +87,20 @@ void TimeManagement::init(Search::LimitsType& limits,
|
||||
const TimePoint scaledTime = limits.time[us] / scaleFactor;
|
||||
const TimePoint scaledInc = limits.inc[us] / scaleFactor;
|
||||
|
||||
// Maximum move horizon of 50 moves
|
||||
int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50;
|
||||
// Maximum move horizon
|
||||
int centiMTG = limits.movestogo ? std::min(limits.movestogo * 100, 5000) : 5051;
|
||||
|
||||
// If less than one second, gradually reduce mtg
|
||||
if (scaledTime < 1000 && double(mtg) / scaledInc > 0.05)
|
||||
if (scaledTime < 1000 && double(centiMTG) / scaledInc > 5.051)
|
||||
{
|
||||
mtg = scaledTime * 0.05;
|
||||
centiMTG = scaledTime * 5.051;
|
||||
}
|
||||
|
||||
// Make sure timeLeft is > 0 since we may use it as a divisor
|
||||
TimePoint timeLeft = std::max(TimePoint(1), limits.time[us] + limits.inc[us] * (mtg - 1)
|
||||
- moveOverhead * (2 + mtg));
|
||||
TimePoint timeLeft =
|
||||
std::max(TimePoint(1),
|
||||
limits.time[us]
|
||||
+ (limits.inc[us] * (centiMTG - 100) - moveOverhead * (200 + centiMTG)) / 100);
|
||||
|
||||
// x basetime (+ z increment)
|
||||
// If there is a healthy increment, timeLeft can exceed the actual available
|
||||
@@ -107,31 +109,32 @@ void TimeManagement::init(Search::LimitsType& limits,
|
||||
{
|
||||
// Extra time according to timeLeft
|
||||
if (originalTimeAdjust < 0)
|
||||
originalTimeAdjust = 0.3285 * std::log10(timeLeft) - 0.4830;
|
||||
originalTimeAdjust = 0.3128 * std::log10(timeLeft) - 0.4354;
|
||||
|
||||
// Calculate time constants based on current time left.
|
||||
double logTimeInSec = std::log10(scaledTime / 1000.0);
|
||||
double optConstant = std::min(0.00308 + 0.000319 * logTimeInSec, 0.00506);
|
||||
double maxConstant = std::max(3.39 + 3.01 * logTimeInSec, 2.93);
|
||||
double optConstant = std::min(0.0032116 + 0.000321123 * logTimeInSec, 0.00508017);
|
||||
double maxConstant = std::max(3.3977 + 3.03950 * logTimeInSec, 2.94761);
|
||||
|
||||
optScale = std::min(0.0122 + std::pow(ply + 2.95, 0.462) * optConstant,
|
||||
0.213 * limits.time[us] / timeLeft)
|
||||
optScale = std::min(0.0121431 + std::pow(ply + 2.94693, 0.461073) * optConstant,
|
||||
0.213035 * limits.time[us] / timeLeft)
|
||||
* originalTimeAdjust;
|
||||
|
||||
maxScale = std::min(6.64, maxConstant + ply / 12.0);
|
||||
maxScale = std::min(6.67704, maxConstant + ply / 11.9847);
|
||||
}
|
||||
|
||||
// x moves in y seconds (+ z increment)
|
||||
else
|
||||
{
|
||||
optScale = std::min((0.88 + ply / 116.4) / mtg, 0.88 * limits.time[us] / timeLeft);
|
||||
maxScale = std::min(6.3, 1.5 + 0.11 * mtg);
|
||||
optScale =
|
||||
std::min((0.88 + ply / 116.4) / (centiMTG / 100.0), 0.88 * limits.time[us] / timeLeft);
|
||||
maxScale = 1.3 + 0.11 * (centiMTG / 100.0);
|
||||
}
|
||||
|
||||
// Limit the maximum possible time for this move
|
||||
optimumTime = TimePoint(optScale * timeLeft);
|
||||
maximumTime =
|
||||
TimePoint(std::min(0.825 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10;
|
||||
TimePoint(std::min(0.825179 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10;
|
||||
|
||||
if (options["Ponder"])
|
||||
optimumTime += optimumTime / 4;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
|
||||
+8
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -193,13 +193,13 @@ void TranspositionTable::clear(ThreadPool& threads) {
|
||||
// Returns an approximation of the hashtable
|
||||
// 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 cnt = 0;
|
||||
int TranspositionTable::hashfull(int maxAge) const {
|
||||
int maxAgeInternal = maxAge << GENERATION_BITS;
|
||||
int cnt = 0;
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
for (int j = 0; j < ClusterSize; ++j)
|
||||
cnt += table[i].entry[j].is_occupied()
|
||||
&& (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8;
|
||||
&& table[i].entry[j].relative_age(generation8) <= maxAgeInternal;
|
||||
|
||||
return cnt / ClusterSize;
|
||||
}
|
||||
@@ -238,7 +238,9 @@ std::tuple<bool, TTData, TTWriter> TranspositionTable::probe(const Key key) cons
|
||||
> tte[i].depth8 - tte[i].relative_age(generation8) * 2)
|
||||
replace = &tte[i];
|
||||
|
||||
return {false, TTData(), TTWriter(replace)};
|
||||
return {false,
|
||||
TTData{Move::none(), VALUE_NONE, VALUE_NONE, DEPTH_ENTRY_OFFSET, BOUND_NONE, false},
|
||||
TTWriter(replace)};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -51,6 +51,18 @@ struct TTData {
|
||||
Depth depth;
|
||||
Bound bound;
|
||||
bool is_pv;
|
||||
|
||||
TTData() = delete;
|
||||
|
||||
// clang-format off
|
||||
TTData(Move m, Value v, Value ev, Depth d, Bound b, bool pv) :
|
||||
move(m),
|
||||
value(v),
|
||||
eval(ev),
|
||||
depth(d),
|
||||
bound(b),
|
||||
is_pv(pv) {};
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
|
||||
@@ -73,7 +85,7 @@ class TranspositionTable {
|
||||
|
||||
void resize(size_t mbSize, ThreadPool& threads); // Set TT size
|
||||
void clear(ThreadPool& threads); // Re-initialize memory, multithreaded
|
||||
int hashfull()
|
||||
int hashfull(int maxAge = 0)
|
||||
const; // Approximate what fraction of entries (permille) have been written to during this root search
|
||||
|
||||
void
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -55,7 +55,7 @@ void Tune::make_option(OptionsMap* opts, const string& n, int v, const SetRange&
|
||||
if (TuneResults.count(n))
|
||||
v = TuneResults[n];
|
||||
|
||||
(*opts)[n] << Option(v, r(v).first, r(v).second, on_tune);
|
||||
opts->add(n, Option(v, r(v).first, r(v).second, on_tune));
|
||||
LastOption = &((*opts)[n]);
|
||||
|
||||
// Print formatted parameters, ready to be copy-pasted in Fishtest
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
+16
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -155,6 +155,21 @@ 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;
|
||||
|
||||
|
||||
constexpr bool is_valid(Value value) { return value != VALUE_NONE; }
|
||||
|
||||
constexpr bool is_win(Value value) {
|
||||
assert(is_valid(value));
|
||||
return value >= VALUE_TB_WIN_IN_MAX_PLY;
|
||||
}
|
||||
|
||||
constexpr bool is_loss(Value value) {
|
||||
assert(is_valid(value));
|
||||
return value <= VALUE_TB_LOSS_IN_MAX_PLY;
|
||||
}
|
||||
|
||||
constexpr bool is_decisive(Value value) { return is_win(value) || is_loss(value); }
|
||||
|
||||
// In the code, we make the assumption that these values
|
||||
// are such that non_pawn_material() can be used to uniquely
|
||||
// identify the material on the board.
|
||||
|
||||
+177
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
@@ -30,6 +31,7 @@
|
||||
|
||||
#include "benchmark.h"
|
||||
#include "engine.h"
|
||||
#include "memory.h"
|
||||
#include "movegen.h"
|
||||
#include "position.h"
|
||||
#include "score.h"
|
||||
@@ -39,6 +41,8 @@
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
constexpr auto BenchmarkCommand = "speedtest";
|
||||
|
||||
constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
template<typename... Ts>
|
||||
struct overload: Ts... {
|
||||
@@ -48,7 +52,7 @@ struct overload: Ts... {
|
||||
template<typename... Ts>
|
||||
overload(Ts...) -> overload<Ts...>;
|
||||
|
||||
void UCIEngine::print_info_string(const std::string& str) {
|
||||
void UCIEngine::print_info_string(std::string_view str) {
|
||||
sync_cout_start();
|
||||
for (auto& line : split(str, "\n"))
|
||||
{
|
||||
@@ -69,11 +73,16 @@ UCIEngine::UCIEngine(int argc, char** argv) :
|
||||
print_info_string(*str);
|
||||
});
|
||||
|
||||
init_search_update_listeners();
|
||||
}
|
||||
|
||||
void UCIEngine::init_search_update_listeners() {
|
||||
engine.set_on_iter([](const auto& i) { on_iter(i); });
|
||||
engine.set_on_update_no_moves([](const auto& i) { on_update_no_moves(i); });
|
||||
engine.set_on_update_full(
|
||||
[this](const auto& i) { on_update_full(i, engine.get_options()["UCI_ShowWDL"]); });
|
||||
engine.set_on_bestmove([](const auto& bm, const auto& p) { on_bestmove(bm, p); });
|
||||
engine.set_on_verify_networks([](const auto& s) { print_info_string(s); });
|
||||
}
|
||||
|
||||
void UCIEngine::loop() {
|
||||
@@ -117,7 +126,7 @@ void UCIEngine::loop() {
|
||||
{
|
||||
// send info strings after the go command is sent for old GUIs and python-chess
|
||||
print_info_string(engine.numa_config_information_as_string());
|
||||
print_info_string(engine.thread_binding_information_as_string());
|
||||
print_info_string(engine.thread_allocation_information_as_string());
|
||||
go(is);
|
||||
}
|
||||
else if (token == "position")
|
||||
@@ -133,6 +142,8 @@ void UCIEngine::loop() {
|
||||
engine.flip();
|
||||
else if (token == "bench")
|
||||
bench(is);
|
||||
else if (token == BenchmarkCommand)
|
||||
benchmark(is);
|
||||
else if (token == "d")
|
||||
sync_cout << engine.visualize() << sync_endl;
|
||||
else if (token == "eval")
|
||||
@@ -285,6 +296,169 @@ void UCIEngine::bench(std::istream& args) {
|
||||
engine.set_on_update_full([&](const auto& i) { on_update_full(i, options["UCI_ShowWDL"]); });
|
||||
}
|
||||
|
||||
void UCIEngine::benchmark(std::istream& args) {
|
||||
// Probably not very important for a test this long, but include for completeness and sanity.
|
||||
static constexpr int NUM_WARMUP_POSITIONS = 3;
|
||||
|
||||
std::string token;
|
||||
uint64_t nodes = 0, cnt = 1;
|
||||
uint64_t nodesSearched = 0;
|
||||
|
||||
engine.set_on_update_full([&](const Engine::InfoFull& i) { nodesSearched = i.nodes; });
|
||||
|
||||
engine.set_on_iter([](const auto&) {});
|
||||
engine.set_on_update_no_moves([](const auto&) {});
|
||||
engine.set_on_bestmove([](const auto&, const auto&) {});
|
||||
engine.set_on_verify_networks([](const auto&) {});
|
||||
|
||||
Benchmark::BenchmarkSetup setup = Benchmark::setup_benchmark(args);
|
||||
|
||||
const int numGoCommands = count_if(setup.commands.begin(), setup.commands.end(),
|
||||
[](const std::string& s) { return s.find("go ") == 0; });
|
||||
|
||||
TimePoint totalTime = 0;
|
||||
|
||||
// Set options once at the start.
|
||||
auto ss = std::istringstream("name Threads value " + std::to_string(setup.threads));
|
||||
setoption(ss);
|
||||
ss = std::istringstream("name Hash value " + std::to_string(setup.ttSize));
|
||||
setoption(ss);
|
||||
ss = std::istringstream("name UCI_Chess960 value false");
|
||||
setoption(ss);
|
||||
|
||||
// Warmup
|
||||
for (const auto& cmd : setup.commands)
|
||||
{
|
||||
std::istringstream is(cmd);
|
||||
is >> std::skipws >> token;
|
||||
|
||||
if (token == "go")
|
||||
{
|
||||
// One new line is produced by the search, so omit it here
|
||||
std::cerr << "\rWarmup position " << cnt++ << '/' << NUM_WARMUP_POSITIONS;
|
||||
|
||||
Search::LimitsType limits = parse_limits(is);
|
||||
|
||||
TimePoint elapsed = now();
|
||||
|
||||
// Run with silenced network verification
|
||||
engine.go(limits);
|
||||
engine.wait_for_search_finished();
|
||||
|
||||
totalTime += now() - elapsed;
|
||||
|
||||
nodes += nodesSearched;
|
||||
nodesSearched = 0;
|
||||
}
|
||||
else if (token == "position")
|
||||
position(is);
|
||||
else if (token == "ucinewgame")
|
||||
{
|
||||
engine.search_clear(); // search_clear may take a while
|
||||
}
|
||||
|
||||
if (cnt > NUM_WARMUP_POSITIONS)
|
||||
break;
|
||||
}
|
||||
|
||||
std::cerr << "\n";
|
||||
|
||||
cnt = 1;
|
||||
nodes = 0;
|
||||
|
||||
int numHashfullReadings = 0;
|
||||
constexpr int hashfullAges[] = {0, 999}; // Only normal hashfull and touched hash.
|
||||
int totalHashfull[std::size(hashfullAges)] = {0};
|
||||
int maxHashfull[std::size(hashfullAges)] = {0};
|
||||
|
||||
auto updateHashfullReadings = [&]() {
|
||||
numHashfullReadings += 1;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(std::size(hashfullAges)); ++i)
|
||||
{
|
||||
const int hashfull = engine.get_hashfull(hashfullAges[i]);
|
||||
maxHashfull[i] = std::max(maxHashfull[i], hashfull);
|
||||
totalHashfull[i] += hashfull;
|
||||
}
|
||||
};
|
||||
|
||||
engine.search_clear(); // search_clear may take a while
|
||||
|
||||
for (const auto& cmd : setup.commands)
|
||||
{
|
||||
std::istringstream is(cmd);
|
||||
is >> std::skipws >> token;
|
||||
|
||||
if (token == "go")
|
||||
{
|
||||
// One new line is produced by the search, so omit it here
|
||||
std::cerr << "\rPosition " << cnt++ << '/' << numGoCommands;
|
||||
|
||||
Search::LimitsType limits = parse_limits(is);
|
||||
|
||||
TimePoint elapsed = now();
|
||||
|
||||
// Run with silenced network verification
|
||||
engine.go(limits);
|
||||
engine.wait_for_search_finished();
|
||||
|
||||
totalTime += now() - elapsed;
|
||||
|
||||
updateHashfullReadings();
|
||||
|
||||
nodes += nodesSearched;
|
||||
nodesSearched = 0;
|
||||
}
|
||||
else if (token == "position")
|
||||
position(is);
|
||||
else if (token == "ucinewgame")
|
||||
{
|
||||
engine.search_clear(); // search_clear may take a while
|
||||
}
|
||||
}
|
||||
|
||||
totalTime = std::max<TimePoint>(totalTime, 1); // Ensure positivity to avoid a 'divide by zero'
|
||||
|
||||
dbg_print();
|
||||
|
||||
std::cerr << "\n";
|
||||
|
||||
static_assert(
|
||||
std::size(hashfullAges) == 2 && hashfullAges[0] == 0 && hashfullAges[1] == 999,
|
||||
"Hardcoded for display. Would complicate the code needlessly in the current state.");
|
||||
|
||||
std::string threadBinding = engine.thread_binding_information_as_string();
|
||||
if (threadBinding.empty())
|
||||
threadBinding = "none";
|
||||
|
||||
// clang-format off
|
||||
|
||||
std::cerr << "==========================="
|
||||
<< "\nVersion : "
|
||||
<< engine_version_info()
|
||||
// "\nCompiled by : "
|
||||
<< compiler_info()
|
||||
<< "Large pages : " << (has_large_pages() ? "yes" : "no")
|
||||
<< "\nUser invocation : " << BenchmarkCommand << " "
|
||||
<< setup.originalInvocation << "\nFilled invocation : " << BenchmarkCommand
|
||||
<< " " << setup.filledInvocation
|
||||
<< "\nAvailable processors : " << engine.get_numa_config_as_string()
|
||||
<< "\nThread count : " << setup.threads
|
||||
<< "\nThread binding : " << threadBinding
|
||||
<< "\nTT size [MiB] : " << setup.ttSize
|
||||
<< "\nHash max, avg [per mille] : "
|
||||
<< "\n single search : " << maxHashfull[0] << ", "
|
||||
<< totalHashfull[0] / numHashfullReadings
|
||||
<< "\n single game : " << maxHashfull[1] << ", "
|
||||
<< totalHashfull[1] / numHashfullReadings
|
||||
<< "\nTotal nodes searched : " << nodes
|
||||
<< "\nTotal search time [s] : " << totalTime / 1000.0
|
||||
<< "\nNodes/second : " << 1000 * nodes / totalTime << std::endl;
|
||||
|
||||
// clang-format on
|
||||
|
||||
init_search_update_listeners();
|
||||
}
|
||||
|
||||
void UCIEngine::setoption(std::istringstream& is) {
|
||||
engine.wait_for_search_finished();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -58,10 +58,11 @@ class UCIEngine {
|
||||
Engine engine;
|
||||
CommandLine cli;
|
||||
|
||||
static void print_info_string(const std::string& str);
|
||||
static void print_info_string(std::string_view str);
|
||||
|
||||
void go(std::istringstream& is);
|
||||
void bench(std::istream& args);
|
||||
void benchmark(std::istream& args);
|
||||
void position(std::istringstream& is);
|
||||
void setoption(std::istringstream& is);
|
||||
std::uint64_t perft(const Search::LimitsType&);
|
||||
@@ -70,6 +71,8 @@ class UCIEngine {
|
||||
static void on_update_full(const Engine::InfoFull& info, bool showWDL);
|
||||
static void on_iter(const Engine::InfoIter& info);
|
||||
static void on_bestmove(std::string_view bestmove, std::string_view ponder);
|
||||
|
||||
void init_search_update_listeners();
|
||||
};
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+22
-20
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
@@ -57,17 +58,31 @@ void OptionsMap::setoption(std::istringstream& is) {
|
||||
sync_cout << "No such option: " << name << sync_endl;
|
||||
}
|
||||
|
||||
Option OptionsMap::operator[](const std::string& name) const {
|
||||
const Option& OptionsMap::operator[](const std::string& name) const {
|
||||
auto it = options_map.find(name);
|
||||
return it != options_map.end() ? it->second : Option(this);
|
||||
assert(it != options_map.end());
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Option& OptionsMap::operator[](const std::string& name) {
|
||||
// Inits options and assigns idx in the correct printing order
|
||||
void OptionsMap::add(const std::string& name, const Option& option) {
|
||||
if (!options_map.count(name))
|
||||
options_map[name] = Option(this);
|
||||
return options_map[name];
|
||||
{
|
||||
static size_t insert_order = 0;
|
||||
|
||||
options_map[name] = option;
|
||||
|
||||
options_map[name].parent = this;
|
||||
options_map[name].idx = insert_order++;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Option \"" << name << "\" was already added!" << std::endl;
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::size_t OptionsMap::count(const std::string& name) const { return options_map.count(name); }
|
||||
|
||||
Option::Option(const OptionsMap* map) :
|
||||
@@ -130,19 +145,6 @@ bool Option::operator==(const char* s) const {
|
||||
bool Option::operator!=(const char* s) const { return !(*this == s); }
|
||||
|
||||
|
||||
// Inits options and assigns idx in the correct printing order
|
||||
|
||||
void Option::operator<<(const Option& o) {
|
||||
|
||||
static size_t insert_order = 0;
|
||||
|
||||
auto p = this->parent;
|
||||
*this = o;
|
||||
|
||||
this->parent = p;
|
||||
idx = insert_order++;
|
||||
}
|
||||
|
||||
// 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
|
||||
// from the user by console window, so let's check the bounds anyway.
|
||||
@@ -161,7 +163,7 @@ Option& Option::operator=(const std::string& v) {
|
||||
std::string token;
|
||||
std::istringstream ss(defaultValue);
|
||||
while (ss >> token)
|
||||
comboMap[token] << Option();
|
||||
comboMap.add(token, Option());
|
||||
if (!comboMap.count(v) || v == "var")
|
||||
return *this;
|
||||
}
|
||||
|
||||
+6
-4
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2025 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
|
||||
@@ -54,12 +54,13 @@ class Option {
|
||||
|
||||
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
|
||||
|
||||
int operator<<(const Option&) = delete;
|
||||
|
||||
private:
|
||||
friend class OptionsMap;
|
||||
friend class Engine;
|
||||
friend class Tune;
|
||||
|
||||
void operator<<(const Option&);
|
||||
|
||||
std::string defaultValue, currentValue, type;
|
||||
int min, max;
|
||||
@@ -82,8 +83,9 @@ class OptionsMap {
|
||||
|
||||
void setoption(std::istringstream&);
|
||||
|
||||
Option operator[](const std::string&) const;
|
||||
Option& operator[](const std::string&);
|
||||
const Option& operator[](const std::string&) const;
|
||||
|
||||
void add(const std::string&, const Option& option);
|
||||
|
||||
std::size_t count(const std::string&) const;
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
*.sh text eol=lf
|
||||
@@ -0,0 +1,520 @@
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
import pathlib
|
||||
import os
|
||||
|
||||
from testing import (
|
||||
EPD,
|
||||
TSAN,
|
||||
Stockfish as Engine,
|
||||
MiniTestFramework,
|
||||
OrderedClassMembers,
|
||||
Valgrind,
|
||||
Syzygy,
|
||||
)
|
||||
|
||||
PATH = pathlib.Path(__file__).parent.resolve()
|
||||
CWD = os.getcwd()
|
||||
|
||||
|
||||
def get_prefix():
|
||||
if args.valgrind:
|
||||
return Valgrind.get_valgrind_command()
|
||||
if args.valgrind_thread:
|
||||
return Valgrind.get_valgrind_thread_command()
|
||||
|
||||
return []
|
||||
|
||||
|
||||
def get_threads():
|
||||
if args.valgrind_thread or args.sanitizer_thread:
|
||||
return 2
|
||||
return 1
|
||||
|
||||
|
||||
def get_path():
|
||||
return os.path.abspath(os.path.join(CWD, args.stockfish_path))
|
||||
|
||||
|
||||
def postfix_check(output):
|
||||
if args.sanitizer_undefined:
|
||||
for idx, line in enumerate(output):
|
||||
if "runtime error:" in line:
|
||||
# print next possible 50 lines
|
||||
for i in range(50):
|
||||
debug_idx = idx + i
|
||||
if debug_idx < len(output):
|
||||
print(output[debug_idx])
|
||||
return False
|
||||
|
||||
if args.sanitizer_thread:
|
||||
for idx, line in enumerate(output):
|
||||
if "WARNING: ThreadSanitizer:" in line:
|
||||
# print next possible 50 lines
|
||||
for i in range(50):
|
||||
debug_idx = idx + i
|
||||
if debug_idx < len(output):
|
||||
print(output[debug_idx])
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def Stockfish(*args, **kwargs):
|
||||
return Engine(get_prefix(), get_path(), *args, **kwargs)
|
||||
|
||||
|
||||
class TestCLI(metaclass=OrderedClassMembers):
|
||||
|
||||
def beforeAll(self):
|
||||
pass
|
||||
|
||||
def afterAll(self):
|
||||
pass
|
||||
|
||||
def beforeEach(self):
|
||||
self.stockfish = None
|
||||
|
||||
def afterEach(self):
|
||||
assert postfix_check(self.stockfish.get_output()) == True
|
||||
self.stockfish.clear_output()
|
||||
|
||||
def test_eval(self):
|
||||
self.stockfish = Stockfish("eval".split(" "), True)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_go_nodes_1000(self):
|
||||
self.stockfish = Stockfish("go nodes 1000".split(" "), True)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_go_depth_10(self):
|
||||
self.stockfish = Stockfish("go depth 10".split(" "), True)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_go_perft_4(self):
|
||||
self.stockfish = Stockfish("go perft 4".split(" "), True)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_go_movetime_1000(self):
|
||||
self.stockfish = Stockfish("go movetime 1000".split(" "), True)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_go_wtime_8000_btime_8000_winc_500_binc_500(self):
|
||||
self.stockfish = Stockfish(
|
||||
"go wtime 8000 btime 8000 winc 500 binc 500".split(" "),
|
||||
True,
|
||||
)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_go_wtime_1000_btime_1000_winc_0_binc_0(self):
|
||||
self.stockfish = Stockfish(
|
||||
"go wtime 1000 btime 1000 winc 0 binc 0".split(" "),
|
||||
True,
|
||||
)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_go_wtime_1000_btime_1000_winc_0_binc_0_movestogo_5(self):
|
||||
self.stockfish = Stockfish(
|
||||
"go wtime 1000 btime 1000 winc 0 binc 0 movestogo 5".split(" "),
|
||||
True,
|
||||
)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_go_movetime_200(self):
|
||||
self.stockfish = Stockfish("go movetime 200".split(" "), True)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_go_nodes_20000_searchmoves_e2e4_d2d4(self):
|
||||
self.stockfish = Stockfish(
|
||||
"go nodes 20000 searchmoves e2e4 d2d4".split(" "), True
|
||||
)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_bench_128_threads_8_default_depth(self):
|
||||
self.stockfish = Stockfish(
|
||||
f"bench 128 {get_threads()} 8 default depth".split(" "),
|
||||
True,
|
||||
)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_bench_128_threads_3_bench_tmp_epd_depth(self):
|
||||
self.stockfish = Stockfish(
|
||||
f"bench 128 {get_threads()} 3 {os.path.join(PATH,'bench_tmp.epd')} depth".split(
|
||||
" "
|
||||
),
|
||||
True,
|
||||
)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_d(self):
|
||||
self.stockfish = Stockfish("d".split(" "), True)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_compiler(self):
|
||||
self.stockfish = Stockfish("compiler".split(" "), True)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_license(self):
|
||||
self.stockfish = Stockfish("license".split(" "), True)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_uci(self):
|
||||
self.stockfish = Stockfish("uci".split(" "), True)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
def test_export_net_verify_nnue(self):
|
||||
current_path = os.path.abspath(os.getcwd())
|
||||
self.stockfish = Stockfish(
|
||||
f"export_net {os.path.join(current_path , 'verify.nnue')}".split(" "), True
|
||||
)
|
||||
assert self.stockfish.process.returncode == 0
|
||||
|
||||
# verify the generated net equals the base net
|
||||
|
||||
def test_network_equals_base(self):
|
||||
self.stockfish = Stockfish(
|
||||
["uci"],
|
||||
True,
|
||||
)
|
||||
|
||||
output = self.stockfish.process.stdout
|
||||
|
||||
# find line
|
||||
for line in output.split("\n"):
|
||||
if "option name EvalFile type string default" in line:
|
||||
network = line.split(" ")[-1]
|
||||
break
|
||||
|
||||
# find network file in src dir
|
||||
network = os.path.join(PATH.parent.resolve(), "src", network)
|
||||
|
||||
if not os.path.exists(network):
|
||||
print(
|
||||
f"Network file {network} not found, please download the network file over the make command."
|
||||
)
|
||||
assert False
|
||||
|
||||
diff = subprocess.run(["diff", network, f"verify.nnue"])
|
||||
|
||||
assert diff.returncode == 0
|
||||
|
||||
|
||||
class TestInteractive(metaclass=OrderedClassMembers):
|
||||
def beforeAll(self):
|
||||
self.stockfish = Stockfish()
|
||||
|
||||
def afterAll(self):
|
||||
self.stockfish.quit()
|
||||
assert self.stockfish.close() == 0
|
||||
|
||||
def afterEach(self):
|
||||
assert postfix_check(self.stockfish.get_output()) == True
|
||||
self.stockfish.clear_output()
|
||||
|
||||
def test_startup_output(self):
|
||||
self.stockfish.starts_with("Stockfish")
|
||||
|
||||
def test_uci_command(self):
|
||||
self.stockfish.send_command("uci")
|
||||
self.stockfish.equals("uciok")
|
||||
|
||||
def test_set_threads_option(self):
|
||||
self.stockfish.send_command(f"setoption name Threads value {get_threads()}")
|
||||
|
||||
def test_ucinewgame_and_startpos_nodes_1000(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command("position startpos")
|
||||
self.stockfish.send_command("go nodes 1000")
|
||||
self.stockfish.starts_with("bestmove")
|
||||
|
||||
def test_ucinewgame_and_startpos_moves(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command("position startpos moves e2e4 e7e6")
|
||||
self.stockfish.send_command("go nodes 1000")
|
||||
self.stockfish.starts_with("bestmove")
|
||||
|
||||
def test_fen_position_1(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command("position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1")
|
||||
self.stockfish.send_command("go nodes 1000")
|
||||
self.stockfish.starts_with("bestmove")
|
||||
|
||||
def test_fen_position_2_flip(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command("position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1")
|
||||
self.stockfish.send_command("flip")
|
||||
self.stockfish.send_command("go nodes 1000")
|
||||
self.stockfish.starts_with("bestmove")
|
||||
|
||||
def test_depth_5_with_callback(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command("position startpos")
|
||||
self.stockfish.send_command("go depth 5")
|
||||
|
||||
def callback(output):
|
||||
regex = r"info depth \d+ seldepth \d+ multipv \d+ score cp \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv"
|
||||
if output.startswith("info depth") and not re.match(regex, output):
|
||||
assert False
|
||||
if output.startswith("bestmove"):
|
||||
return True
|
||||
return False
|
||||
|
||||
self.stockfish.check_output(callback)
|
||||
|
||||
def test_ucinewgame_and_go_depth_9(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command("setoption name UCI_ShowWDL value true")
|
||||
self.stockfish.send_command("position startpos")
|
||||
self.stockfish.send_command("go depth 9")
|
||||
|
||||
depth = 1
|
||||
|
||||
def callback(output):
|
||||
nonlocal depth
|
||||
|
||||
regex = rf"info depth {depth} seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv"
|
||||
|
||||
if output.startswith("info depth"):
|
||||
if not re.match(regex, output):
|
||||
assert False
|
||||
depth += 1
|
||||
|
||||
if output.startswith("bestmove"):
|
||||
assert depth == 10
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
self.stockfish.check_output(callback)
|
||||
|
||||
def test_clear_hash(self):
|
||||
self.stockfish.send_command("setoption name Clear Hash")
|
||||
|
||||
def test_fen_position_mate_1(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command(
|
||||
"position fen 5K2/8/2qk4/2nPp3/3r4/6B1/B7/3R4 w - e6"
|
||||
)
|
||||
self.stockfish.send_command("go depth 18")
|
||||
|
||||
self.stockfish.expect("* score mate 1 * pv d5e6")
|
||||
self.stockfish.equals("bestmove d5e6")
|
||||
|
||||
def test_fen_position_mate_minus_1(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command(
|
||||
"position fen 2brrb2/8/p7/Q7/1p1kpPp1/1P1pN1K1/3P4/8 b - -"
|
||||
)
|
||||
self.stockfish.send_command("go depth 18")
|
||||
self.stockfish.expect("* score mate -1 *")
|
||||
self.stockfish.starts_with("bestmove")
|
||||
|
||||
def test_fen_position_fixed_node(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command(
|
||||
"position fen 5K2/8/2P1P1Pk/6pP/3p2P1/1P6/3P4/8 w - - 0 1"
|
||||
)
|
||||
self.stockfish.send_command("go nodes 500000")
|
||||
self.stockfish.starts_with("bestmove")
|
||||
|
||||
def test_fen_position_with_mate_go_depth(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command(
|
||||
"position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -"
|
||||
)
|
||||
self.stockfish.send_command("go depth 18 searchmoves c6d7")
|
||||
self.stockfish.expect("* score mate 2 * pv c6d7 * f7f5")
|
||||
|
||||
self.stockfish.starts_with("bestmove")
|
||||
|
||||
def test_fen_position_with_mate_go_mate(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command(
|
||||
"position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -"
|
||||
)
|
||||
self.stockfish.send_command("go mate 2 searchmoves c6d7")
|
||||
self.stockfish.expect("* score mate 2 * pv c6d7 *")
|
||||
|
||||
self.stockfish.starts_with("bestmove")
|
||||
|
||||
def test_fen_position_with_mate_go_nodes(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command(
|
||||
"position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -"
|
||||
)
|
||||
self.stockfish.send_command("go nodes 500000 searchmoves c6d7")
|
||||
self.stockfish.expect("* score mate 2 * pv c6d7 * f7f5")
|
||||
|
||||
self.stockfish.starts_with("bestmove")
|
||||
|
||||
def test_fen_position_depth_27(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command(
|
||||
"position fen r1b2r1k/pp1p2pp/2p5/2B1q3/8/8/P1PN2PP/R4RK1 w - - 0 18"
|
||||
)
|
||||
self.stockfish.send_command("go")
|
||||
self.stockfish.contains("score mate 1")
|
||||
|
||||
self.stockfish.starts_with("bestmove")
|
||||
|
||||
def test_fen_position_with_mate_go_depth_and_promotion(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command(
|
||||
"position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - - moves c6d7 f2f1q"
|
||||
)
|
||||
self.stockfish.send_command("go depth 18")
|
||||
self.stockfish.expect("* score mate 1 * pv f7f5")
|
||||
self.stockfish.starts_with("bestmove f7f5")
|
||||
|
||||
def test_fen_position_with_mate_go_depth_and_searchmoves(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command(
|
||||
"position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -"
|
||||
)
|
||||
self.stockfish.send_command("go depth 18 searchmoves c6d7")
|
||||
self.stockfish.expect("* score mate 2 * pv c6d7 * f7f5")
|
||||
|
||||
self.stockfish.starts_with("bestmove c6d7")
|
||||
|
||||
def test_fen_position_with_moves_with_mate_go_depth_and_searchmoves(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command(
|
||||
"position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - - moves c6d7"
|
||||
)
|
||||
self.stockfish.send_command("go depth 18 searchmoves e3e2")
|
||||
self.stockfish.expect("* score mate -1 * pv e3e2 f7f5")
|
||||
self.stockfish.starts_with("bestmove e3e2")
|
||||
|
||||
def test_verify_nnue_network(self):
|
||||
current_path = os.path.abspath(os.getcwd())
|
||||
Stockfish(
|
||||
f"export_net {os.path.join(current_path , 'verify.nnue')}".split(" "), True
|
||||
)
|
||||
|
||||
self.stockfish.send_command("setoption name EvalFile value verify.nnue")
|
||||
self.stockfish.send_command("position startpos")
|
||||
self.stockfish.send_command("go depth 5")
|
||||
self.stockfish.starts_with("bestmove")
|
||||
|
||||
def test_multipv_setting(self):
|
||||
self.stockfish.send_command("setoption name MultiPV value 4")
|
||||
self.stockfish.send_command("position startpos")
|
||||
self.stockfish.send_command("go depth 5")
|
||||
self.stockfish.starts_with("bestmove")
|
||||
|
||||
def test_fen_position_with_skill_level(self):
|
||||
self.stockfish.send_command("setoption name Skill Level value 10")
|
||||
self.stockfish.send_command("position startpos")
|
||||
self.stockfish.send_command("go depth 5")
|
||||
self.stockfish.starts_with("bestmove")
|
||||
|
||||
self.stockfish.send_command("setoption name Skill Level value 20")
|
||||
|
||||
|
||||
class TestSyzygy(metaclass=OrderedClassMembers):
|
||||
def beforeAll(self):
|
||||
self.stockfish = Stockfish()
|
||||
|
||||
def afterAll(self):
|
||||
self.stockfish.quit()
|
||||
assert self.stockfish.close() == 0
|
||||
|
||||
def afterEach(self):
|
||||
assert postfix_check(self.stockfish.get_output()) == True
|
||||
self.stockfish.clear_output()
|
||||
|
||||
def test_syzygy_setup(self):
|
||||
self.stockfish.starts_with("Stockfish")
|
||||
self.stockfish.send_command("uci")
|
||||
self.stockfish.send_command(
|
||||
f"setoption name SyzygyPath value {os.path.join(PATH, 'syzygy')}"
|
||||
)
|
||||
self.stockfish.expect(
|
||||
"info string Found 35 WDL and 35 DTZ tablebase files (up to 4-man)."
|
||||
)
|
||||
|
||||
def test_syzygy_bench(self):
|
||||
self.stockfish.send_command("bench 128 1 8 default depth")
|
||||
self.stockfish.expect("Nodes searched :*")
|
||||
|
||||
def test_syzygy_position(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command("position fen 4k3/PP6/8/8/8/8/8/4K3 w - - 0 1")
|
||||
self.stockfish.send_command("go depth 5")
|
||||
|
||||
def check_output(output):
|
||||
if "score cp 20000" in output or "score mate" in output:
|
||||
return True
|
||||
|
||||
self.stockfish.check_output(check_output)
|
||||
self.stockfish.expect("bestmove *")
|
||||
|
||||
def test_syzygy_position_2(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command("position fen 8/1P6/2B5/8/4K3/8/6k1/8 w - - 0 1")
|
||||
self.stockfish.send_command("go depth 5")
|
||||
|
||||
def check_output(output):
|
||||
if "score cp 20000" in output or "score mate" in output:
|
||||
return True
|
||||
|
||||
self.stockfish.check_output(check_output)
|
||||
self.stockfish.expect("bestmove *")
|
||||
|
||||
def test_syzygy_position_3(self):
|
||||
self.stockfish.send_command("ucinewgame")
|
||||
self.stockfish.send_command("position fen 8/1P6/2B5/8/4K3/8/6k1/8 b - - 0 1")
|
||||
self.stockfish.send_command("go depth 5")
|
||||
|
||||
def check_output(output):
|
||||
if "score cp -20000" in output or "score mate" in output:
|
||||
return True
|
||||
|
||||
self.stockfish.check_output(check_output)
|
||||
self.stockfish.expect("bestmove *")
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description="Run Stockfish with testing options")
|
||||
parser.add_argument("--valgrind", action="store_true", help="Run valgrind testing")
|
||||
parser.add_argument(
|
||||
"--valgrind-thread", action="store_true", help="Run valgrind-thread testing"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sanitizer-undefined",
|
||||
action="store_true",
|
||||
help="Run sanitizer-undefined testing",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sanitizer-thread", action="store_true", help="Run sanitizer-thread testing"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--none", action="store_true", help="Run without any testing options"
|
||||
)
|
||||
parser.add_argument("stockfish_path", type=str, help="Path to Stockfish binary")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_args()
|
||||
|
||||
EPD.create_bench_epd()
|
||||
TSAN.set_tsan_option()
|
||||
Syzygy.download_syzygy()
|
||||
|
||||
framework = MiniTestFramework()
|
||||
|
||||
# Each test suite will be ran inside a temporary directory
|
||||
framework.run([TestCLI, TestInteractive, TestSyzygy])
|
||||
|
||||
EPD.delete_bench_epd()
|
||||
TSAN.unset_tsan_option()
|
||||
|
||||
if framework.has_failed():
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
||||
@@ -1,301 +0,0 @@
|
||||
#!/bin/bash
|
||||
# check for errors under Valgrind or sanitizers.
|
||||
|
||||
error()
|
||||
{
|
||||
echo "instrumented testing failed on line $1"
|
||||
exit 1
|
||||
}
|
||||
trap 'error ${LINENO}' ERR
|
||||
|
||||
# define suitable post and prefixes for testing options
|
||||
case $1 in
|
||||
--valgrind)
|
||||
echo "valgrind testing started"
|
||||
prefix=''
|
||||
exeprefix='valgrind --error-exitcode=42 --errors-for-leak-kinds=all --leak-check=full'
|
||||
postfix=''
|
||||
threads="1"
|
||||
;;
|
||||
--valgrind-thread)
|
||||
echo "valgrind-thread testing started"
|
||||
prefix=''
|
||||
exeprefix='valgrind --fair-sched=try --error-exitcode=42'
|
||||
postfix=''
|
||||
threads="2"
|
||||
;;
|
||||
--sanitizer-undefined)
|
||||
echo "sanitizer-undefined testing started"
|
||||
prefix='!'
|
||||
exeprefix=''
|
||||
postfix='2>&1 | grep -A50 "runtime error:"'
|
||||
threads="1"
|
||||
;;
|
||||
--sanitizer-thread)
|
||||
echo "sanitizer-thread testing started"
|
||||
prefix='!'
|
||||
exeprefix=''
|
||||
postfix='2>&1 | grep -A50 "WARNING: ThreadSanitizer:"'
|
||||
threads="2"
|
||||
|
||||
cat << EOF > tsan.supp
|
||||
race:Stockfish::TTEntry::read
|
||||
race:Stockfish::TTEntry::save
|
||||
|
||||
race:Stockfish::TranspositionTable::probe
|
||||
race:Stockfish::TranspositionTable::hashfull
|
||||
|
||||
EOF
|
||||
|
||||
export TSAN_OPTIONS="suppressions=./tsan.supp"
|
||||
|
||||
;;
|
||||
*)
|
||||
echo "unknown testing started"
|
||||
prefix=''
|
||||
exeprefix=''
|
||||
postfix=''
|
||||
threads="1"
|
||||
;;
|
||||
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
|
||||
for args in "eval" \
|
||||
"go nodes 1000" \
|
||||
"go depth 10" \
|
||||
"go perft 4" \
|
||||
"go movetime 1000" \
|
||||
"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 3 bench_tmp.epd depth" \
|
||||
"export_net verify.nnue" \
|
||||
"d" \
|
||||
"compiler" \
|
||||
"license" \
|
||||
"uci"
|
||||
do
|
||||
|
||||
echo "$prefix $exeprefix ./stockfish $args $postfix"
|
||||
eval "$prefix $exeprefix ./stockfish $args $postfix"
|
||||
|
||||
done
|
||||
|
||||
# verify the generated net equals the base net
|
||||
network=`./stockfish uci | grep 'option name EvalFile type string default' | awk '{print $NF}'`
|
||||
echo "Comparing $network to the written verify.nnue"
|
||||
diff $network verify.nnue
|
||||
|
||||
# more general testing, following an uci protocol exchange
|
||||
cat << EOF > game.exp
|
||||
set timeout 240
|
||||
# to correctly catch eof we need the following line
|
||||
# expect_before timeout { exit 2 } eof { exit 3 }
|
||||
expect_before timeout { exit 2 }
|
||||
|
||||
spawn $exeprefix ./stockfish
|
||||
expect "Stockfish"
|
||||
|
||||
send "uci\n"
|
||||
expect "uciok"
|
||||
|
||||
# send "setoption name Debug Log File value debug.log\n"
|
||||
send "setoption name Threads value $threads\n"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position startpos\n"
|
||||
send "go nodes 1000\n"
|
||||
expect "bestmove"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position startpos moves e2e4 e7e6\n"
|
||||
send "go nodes 1000\n"
|
||||
expect "bestmove"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n"
|
||||
send "go depth 10\n"
|
||||
expect "bestmove"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n"
|
||||
send "flip\n"
|
||||
send "go depth 10\n"
|
||||
expect "bestmove"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position startpos\n"
|
||||
send "go depth 5\n"
|
||||
expect -re {info depth \d+ seldepth \d+ multipv \d+ score cp \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv}
|
||||
expect "bestmove"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "setoption name UCI_ShowWDL value true\n"
|
||||
send "position startpos\n"
|
||||
send "go depth 9\n"
|
||||
expect -re {info depth 1 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv}
|
||||
expect -re {info depth 2 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv}
|
||||
expect -re {info depth 3 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv}
|
||||
expect -re {info depth 4 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv}
|
||||
expect -re {info depth 5 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv}
|
||||
expect -re {info depth 6 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv}
|
||||
expect -re {info depth 7 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv}
|
||||
expect -re {info depth 8 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv}
|
||||
expect -re {info depth 9 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv}
|
||||
expect "bestmove"
|
||||
|
||||
send "setoption name Clear Hash\n"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position fen 5K2/8/2qk4/2nPp3/3r4/6B1/B7/3R4 w - e6\n"
|
||||
send "go depth 18\n"
|
||||
expect "score mate 1"
|
||||
expect "pv d5e6"
|
||||
expect "bestmove d5e6"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position fen 2brrb2/8/p7/Q7/1p1kpPp1/1P1pN1K1/3P4/8 b - -\n"
|
||||
send "go depth 18\n"
|
||||
expect "score mate -1"
|
||||
expect "bestmove"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position fen 7K/P1p1p1p1/2P1P1Pk/6pP/3p2P1/1P6/3P4/8 w - - 0 1\n"
|
||||
send "go nodes 500000\n"
|
||||
expect "bestmove"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n"
|
||||
send "go depth 18 searchmoves c6d7\n"
|
||||
expect "score mate 2 * pv c6d7 * f7f5"
|
||||
expect "bestmove c6d7"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n"
|
||||
send "go mate 2 searchmoves c6d7\n"
|
||||
expect "score mate 2 * pv c6d7"
|
||||
expect "bestmove c6d7"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n"
|
||||
send "go nodes 500000 searchmoves c6d7\n"
|
||||
expect "score mate 2 * pv c6d7 * f7f5"
|
||||
expect "bestmove c6d7"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position fen 1NR2B2/5p2/5p2/1p1kpp2/1P2rp2/2P1pB2/2P1P1K1/8 b - - \n"
|
||||
send "go depth 27\n"
|
||||
expect "score mate -2"
|
||||
expect "pv d5e6 c8d8"
|
||||
expect "bestmove d5e6"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - - moves c6d7 f2f1q\n"
|
||||
send "go depth 18\n"
|
||||
expect "score mate 1 * pv f7f5"
|
||||
expect "bestmove f7f5"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n"
|
||||
send "go depth 18 searchmoves c6d7\n"
|
||||
expect "score mate 2 * pv c6d7 * f7f5"
|
||||
expect "bestmove c6d7"
|
||||
|
||||
send "ucinewgame\n"
|
||||
send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - - moves c6d7\n"
|
||||
send "go depth 18 searchmoves e3e2\n"
|
||||
expect "score mate -1 * pv e3e2 f7f5"
|
||||
expect "bestmove e3e2"
|
||||
|
||||
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"
|
||||
expect "bestmove"
|
||||
|
||||
send "setoption name Skill Level value 10\n"
|
||||
send "position startpos\n"
|
||||
send "go depth 5\n"
|
||||
expect "bestmove"
|
||||
send "setoption name Skill Level value 20\n"
|
||||
|
||||
send "quit\n"
|
||||
expect eof
|
||||
|
||||
# return error code of the spawned program, useful for Valgrind
|
||||
lassign [wait] pid spawnid os_error_flag value
|
||||
exit \$value
|
||||
EOF
|
||||
|
||||
#download TB as needed
|
||||
if [ ! -d ../tests/syzygy ]; then
|
||||
curl -sL https://api.github.com/repos/niklasf/python-chess/tarball/9b9aa13f9f36d08aadfabff872882f4ab1494e95 | tar -xzf -
|
||||
mv niklasf-python-chess-9b9aa13 ../tests/syzygy
|
||||
fi
|
||||
|
||||
cat << EOF > syzygy.exp
|
||||
set timeout 240
|
||||
# to correctly catch eof we need the following line
|
||||
# expect_before timeout { exit 2 } eof { exit 3 }
|
||||
expect_before timeout { exit 2 }
|
||||
spawn $exeprefix ./stockfish
|
||||
expect "Stockfish"
|
||||
send "uci\n"
|
||||
send "setoption name SyzygyPath value ../tests/syzygy/\n"
|
||||
expect "info string Found 35 WDL and 35 DTZ tablebase files (up to 4-man)."
|
||||
send "bench 128 1 8 default depth\n"
|
||||
expect "Nodes searched :"
|
||||
send "ucinewgame\n"
|
||||
send "position fen 4k3/PP6/8/8/8/8/8/4K3 w - - 0 1\n"
|
||||
send "go depth 5\n"
|
||||
expect -re {score cp 20000|score mate}
|
||||
expect "bestmove"
|
||||
send "ucinewgame\n"
|
||||
send "position fen 8/1P6/2B5/8/4K3/8/6k1/8 w - - 0 1\n"
|
||||
send "go depth 5\n"
|
||||
expect -re {score cp 20000|score mate}
|
||||
expect "bestmove"
|
||||
send "ucinewgame\n"
|
||||
send "position fen 8/1P6/2B5/8/4K3/8/6k1/8 b - - 0 1\n"
|
||||
send "go depth 5\n"
|
||||
expect -re {score cp -20000|score mate}
|
||||
expect "bestmove"
|
||||
send "quit\n"
|
||||
expect eof
|
||||
|
||||
# return error code of the spawned program, useful for Valgrind
|
||||
lassign [wait] pid spawnid os_error_flag value
|
||||
exit \$value
|
||||
EOF
|
||||
|
||||
for exp in game.exp syzygy.exp
|
||||
do
|
||||
|
||||
echo "======== $exp =============="
|
||||
cat $exp
|
||||
echo "============================"
|
||||
echo "$prefix expect $exp $postfix"
|
||||
eval "$prefix expect $exp $postfix"
|
||||
|
||||
rm $exp
|
||||
|
||||
done
|
||||
|
||||
rm -f tsan.supp bench_tmp.epd
|
||||
|
||||
echo "instrumented testing OK"
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
# verify perft numbers (positions from www.chessprogramming.org/Perft_Results)
|
||||
# verify perft numbers (positions from https://www.chessprogramming.org/Perft_Results)
|
||||
|
||||
error()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,400 @@
|
||||
import subprocess
|
||||
from typing import List
|
||||
import os
|
||||
import collections
|
||||
import time
|
||||
import sys
|
||||
import traceback
|
||||
import fnmatch
|
||||
from functools import wraps
|
||||
from contextlib import redirect_stdout
|
||||
import io
|
||||
import tarfile
|
||||
import pathlib
|
||||
import concurrent.futures
|
||||
import tempfile
|
||||
import shutil
|
||||
import requests
|
||||
|
||||
CYAN_COLOR = "\033[36m"
|
||||
GRAY_COLOR = "\033[2m"
|
||||
RED_COLOR = "\033[31m"
|
||||
GREEN_COLOR = "\033[32m"
|
||||
RESET_COLOR = "\033[0m"
|
||||
WHITE_BOLD = "\033[1m"
|
||||
|
||||
MAX_TIMEOUT = 60 * 5
|
||||
|
||||
PATH = pathlib.Path(__file__).parent.resolve()
|
||||
|
||||
|
||||
class Valgrind:
|
||||
@staticmethod
|
||||
def get_valgrind_command():
|
||||
return [
|
||||
"valgrind",
|
||||
"--error-exitcode=42",
|
||||
"--errors-for-leak-kinds=all",
|
||||
"--leak-check=full",
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def get_valgrind_thread_command():
|
||||
return ["valgrind", "--error-exitcode=42", "--fair-sched=try"]
|
||||
|
||||
|
||||
class TSAN:
|
||||
@staticmethod
|
||||
def set_tsan_option():
|
||||
with open(f"tsan.supp", "w") as f:
|
||||
f.write(
|
||||
"""
|
||||
race:Stockfish::TTEntry::read
|
||||
race:Stockfish::TTEntry::save
|
||||
race:Stockfish::TranspositionTable::probe
|
||||
race:Stockfish::TranspositionTable::hashfull
|
||||
"""
|
||||
)
|
||||
|
||||
os.environ["TSAN_OPTIONS"] = "suppressions=./tsan.supp"
|
||||
|
||||
@staticmethod
|
||||
def unset_tsan_option():
|
||||
os.environ.pop("TSAN_OPTIONS", None)
|
||||
os.remove(f"tsan.supp")
|
||||
|
||||
|
||||
class EPD:
|
||||
@staticmethod
|
||||
def create_bench_epd():
|
||||
with open(f"{os.path.join(PATH,'bench_tmp.epd')}", "w") as f:
|
||||
f.write(
|
||||
"""
|
||||
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
|
||||
"""
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def delete_bench_epd():
|
||||
os.remove(f"{os.path.join(PATH,'bench_tmp.epd')}")
|
||||
|
||||
|
||||
class Syzygy:
|
||||
@staticmethod
|
||||
def get_syzygy_path():
|
||||
return os.path.abspath("syzygy")
|
||||
|
||||
@staticmethod
|
||||
def download_syzygy():
|
||||
if not os.path.isdir(os.path.join(PATH, "syzygy")):
|
||||
url = "https://api.github.com/repos/niklasf/python-chess/tarball/9b9aa13f9f36d08aadfabff872882f4ab1494e95"
|
||||
file = "niklasf-python-chess-9b9aa13"
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
tarball_path = os.path.join(tmpdirname, f"{file}.tar.gz")
|
||||
|
||||
response = requests.get(url, stream=True)
|
||||
with open(tarball_path, "wb") as f:
|
||||
for chunk in response.iter_content(chunk_size=8192):
|
||||
f.write(chunk)
|
||||
|
||||
with tarfile.open(tarball_path, "r:gz") as tar:
|
||||
tar.extractall(tmpdirname)
|
||||
|
||||
shutil.move(
|
||||
os.path.join(tmpdirname, file), os.path.join(PATH, "syzygy")
|
||||
)
|
||||
|
||||
|
||||
class OrderedClassMembers(type):
|
||||
@classmethod
|
||||
def __prepare__(self, name, bases):
|
||||
return collections.OrderedDict()
|
||||
|
||||
def __new__(self, name, bases, classdict):
|
||||
classdict["__ordered__"] = [
|
||||
key for key in classdict.keys() if key not in ("__module__", "__qualname__")
|
||||
]
|
||||
return type.__new__(self, name, bases, classdict)
|
||||
|
||||
|
||||
class TimeoutException(Exception):
|
||||
def __init__(self, message: str, timeout: int):
|
||||
self.message = message
|
||||
self.timeout = timeout
|
||||
|
||||
|
||||
def timeout_decorator(timeout: float):
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
future = executor.submit(func, *args, **kwargs)
|
||||
try:
|
||||
result = future.result(timeout=timeout)
|
||||
except concurrent.futures.TimeoutError:
|
||||
raise TimeoutException(
|
||||
f"Function {func.__name__} timed out after {timeout} seconds",
|
||||
timeout,
|
||||
)
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
class MiniTestFramework:
|
||||
def __init__(self):
|
||||
self.passed_test_suites = 0
|
||||
self.failed_test_suites = 0
|
||||
self.passed_tests = 0
|
||||
self.failed_tests = 0
|
||||
self.stop_on_failure = True
|
||||
|
||||
def has_failed(self) -> bool:
|
||||
return self.failed_test_suites > 0
|
||||
|
||||
def run(self, classes: List[type]) -> bool:
|
||||
self.start_time = time.time()
|
||||
|
||||
for test_class in classes:
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
original_cwd = os.getcwd()
|
||||
os.chdir(tmpdirname)
|
||||
|
||||
try:
|
||||
if self.__run(test_class):
|
||||
self.failed_test_suites += 1
|
||||
else:
|
||||
self.passed_test_suites += 1
|
||||
except Exception as e:
|
||||
self.failed_test_suites += 1
|
||||
print(f"\n{RED_COLOR}Error: {e}{RESET_COLOR}")
|
||||
finally:
|
||||
os.chdir(original_cwd)
|
||||
|
||||
self.__print_summary(round(time.time() - self.start_time, 2))
|
||||
return self.has_failed()
|
||||
|
||||
def __run(self, test_class) -> bool:
|
||||
test_instance = test_class()
|
||||
test_name = test_instance.__class__.__name__
|
||||
test_methods = [m for m in test_instance.__ordered__ if m.startswith("test_")]
|
||||
|
||||
print(f"\nTest Suite: {test_name}")
|
||||
|
||||
if hasattr(test_instance, "beforeAll"):
|
||||
test_instance.beforeAll()
|
||||
|
||||
fails = 0
|
||||
|
||||
for method in test_methods:
|
||||
fails += self.__run_test_method(test_instance, method)
|
||||
|
||||
if hasattr(test_instance, "afterAll"):
|
||||
test_instance.afterAll()
|
||||
|
||||
self.failed_tests += fails
|
||||
|
||||
return fails > 0
|
||||
|
||||
def __run_test_method(self, test_instance, method: str) -> int:
|
||||
print(f" Running {method}... \r", end="", flush=True)
|
||||
|
||||
buffer = io.StringIO()
|
||||
fails = 0
|
||||
|
||||
try:
|
||||
t0 = time.time()
|
||||
|
||||
with redirect_stdout(buffer):
|
||||
if hasattr(test_instance, "beforeEach"):
|
||||
test_instance.beforeEach()
|
||||
|
||||
getattr(test_instance, method)()
|
||||
|
||||
if hasattr(test_instance, "afterEach"):
|
||||
test_instance.afterEach()
|
||||
|
||||
duration = time.time() - t0
|
||||
|
||||
self.print_success(f" {method} ({duration * 1000:.2f}ms)")
|
||||
self.passed_tests += 1
|
||||
except Exception as e:
|
||||
if isinstance(e, TimeoutException):
|
||||
self.print_failure(
|
||||
f" {method} (hit execution limit of {e.timeout} seconds)"
|
||||
)
|
||||
|
||||
if isinstance(e, AssertionError):
|
||||
self.__handle_assertion_error(t0, method)
|
||||
|
||||
if self.stop_on_failure:
|
||||
self.__print_buffer_output(buffer)
|
||||
raise e
|
||||
|
||||
fails += 1
|
||||
finally:
|
||||
self.__print_buffer_output(buffer)
|
||||
|
||||
return fails
|
||||
|
||||
def __handle_assertion_error(self, start_time, method: str):
|
||||
duration = time.time() - start_time
|
||||
self.print_failure(f" {method} ({duration * 1000:.2f}ms)")
|
||||
traceback_output = "".join(traceback.format_tb(sys.exc_info()[2]))
|
||||
|
||||
colored_traceback = "\n".join(
|
||||
f" {CYAN_COLOR}{line}{RESET_COLOR}"
|
||||
for line in traceback_output.splitlines()
|
||||
)
|
||||
|
||||
print(colored_traceback)
|
||||
|
||||
def __print_buffer_output(self, buffer: io.StringIO):
|
||||
output = buffer.getvalue()
|
||||
if output:
|
||||
indented_output = "\n".join(f" {line}" for line in output.splitlines())
|
||||
print(f" {RED_COLOR}⎯⎯⎯⎯⎯OUTPUT⎯⎯⎯⎯⎯{RESET_COLOR}")
|
||||
print(f"{GRAY_COLOR}{indented_output}{RESET_COLOR}")
|
||||
print(f" {RED_COLOR}⎯⎯⎯⎯⎯OUTPUT⎯⎯⎯⎯⎯{RESET_COLOR}")
|
||||
|
||||
def __print_summary(self, duration: float):
|
||||
print(f"\n{WHITE_BOLD}Test Summary{RESET_COLOR}\n")
|
||||
print(
|
||||
f" Test Suites: {GREEN_COLOR}{self.passed_test_suites} passed{RESET_COLOR}, {RED_COLOR}{self.failed_test_suites} failed{RESET_COLOR}, {self.passed_test_suites + self.failed_test_suites} total"
|
||||
)
|
||||
print(
|
||||
f" Tests: {GREEN_COLOR}{self.passed_tests} passed{RESET_COLOR}, {RED_COLOR}{self.failed_tests} failed{RESET_COLOR}, {self.passed_tests + self.failed_tests} total"
|
||||
)
|
||||
print(f" Time: {duration}s\n")
|
||||
|
||||
def print_failure(self, add: str):
|
||||
print(f" {RED_COLOR}✗{RESET_COLOR}{add}", flush=True)
|
||||
|
||||
def print_success(self, add: str):
|
||||
print(f" {GREEN_COLOR}✓{RESET_COLOR}{add}", flush=True)
|
||||
|
||||
|
||||
class Stockfish:
|
||||
def __init__(
|
||||
self,
|
||||
prefix: List[str],
|
||||
path: str,
|
||||
args: List[str] = [],
|
||||
cli: bool = False,
|
||||
):
|
||||
self.path = path
|
||||
self.process = None
|
||||
self.args = args
|
||||
self.cli = cli
|
||||
self.prefix = prefix
|
||||
self.output = []
|
||||
|
||||
self.start()
|
||||
|
||||
def _check_process_alive(self):
|
||||
if not self.process or self.process.poll() is not None:
|
||||
print("\n".join(self.output))
|
||||
raise RuntimeError("Stockfish process has terminated")
|
||||
|
||||
def start(self):
|
||||
if self.cli:
|
||||
self.process = subprocess.run(
|
||||
self.prefix + [self.path] + self.args,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
if self.process.returncode != 0:
|
||||
print(self.process.stdout)
|
||||
print(self.process.stderr)
|
||||
print(f"Process failed with return code {self.process.returncode}")
|
||||
|
||||
return
|
||||
|
||||
self.process = subprocess.Popen(
|
||||
self.prefix + [self.path] + self.args,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True,
|
||||
bufsize=1,
|
||||
)
|
||||
|
||||
def setoption(self, name: str, value: str):
|
||||
self.send_command(f"setoption name {name} value {value}")
|
||||
|
||||
def send_command(self, command: str):
|
||||
if not self.process:
|
||||
raise RuntimeError("Stockfish process is not started")
|
||||
|
||||
self._check_process_alive()
|
||||
|
||||
self.process.stdin.write(command + "\n")
|
||||
self.process.stdin.flush()
|
||||
|
||||
@timeout_decorator(MAX_TIMEOUT)
|
||||
def equals(self, expected_output: str):
|
||||
for line in self.readline():
|
||||
if line == expected_output:
|
||||
return
|
||||
|
||||
@timeout_decorator(MAX_TIMEOUT)
|
||||
def expect(self, expected_output: str):
|
||||
for line in self.readline():
|
||||
if fnmatch.fnmatch(line, expected_output):
|
||||
return
|
||||
|
||||
@timeout_decorator(MAX_TIMEOUT)
|
||||
def contains(self, expected_output: str):
|
||||
for line in self.readline():
|
||||
if expected_output in line:
|
||||
return
|
||||
|
||||
@timeout_decorator(MAX_TIMEOUT)
|
||||
def starts_with(self, expected_output: str):
|
||||
for line in self.readline():
|
||||
if line.startswith(expected_output):
|
||||
return
|
||||
|
||||
@timeout_decorator(MAX_TIMEOUT)
|
||||
def check_output(self, callback):
|
||||
if not callback:
|
||||
raise ValueError("Callback function is required")
|
||||
|
||||
for line in self.readline():
|
||||
if callback(line) == True:
|
||||
return
|
||||
|
||||
def readline(self):
|
||||
if not self.process:
|
||||
raise RuntimeError("Stockfish process is not started")
|
||||
|
||||
while True:
|
||||
self._check_process_alive()
|
||||
line = self.process.stdout.readline().strip()
|
||||
self.output.append(line)
|
||||
|
||||
yield line
|
||||
|
||||
def clear_output(self):
|
||||
self.output = []
|
||||
|
||||
def get_output(self) -> List[str]:
|
||||
return self.output
|
||||
|
||||
def quit(self):
|
||||
self.send_command("quit")
|
||||
|
||||
def close(self):
|
||||
if self.process:
|
||||
self.process.stdin.close()
|
||||
self.process.stdout.close()
|
||||
return self.process.wait()
|
||||
|
||||
return 0
|
||||
Reference in New Issue
Block a user