mirror of
https://github.com/opelly27/Stockfish.git
synced 2026-05-20 08:37:44 +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": [
|
"config": [
|
||||||
{
|
{
|
||||||
"name": "Ubuntu 20.04 GCC",
|
"name": "Ubuntu 22.04 GCC",
|
||||||
"os": "ubuntu-20.04",
|
"os": "ubuntu-22.04",
|
||||||
"simple_name": "ubuntu",
|
"simple_name": "ubuntu",
|
||||||
"compiler": "g++",
|
"compiler": "g++",
|
||||||
"comp": "gcc",
|
"comp": "gcc",
|
||||||
@@ -111,7 +111,7 @@
|
|||||||
{
|
{
|
||||||
"binaries": "x86-64-avxvnni",
|
"binaries": "x86-64-avxvnni",
|
||||||
"config": {
|
"config": {
|
||||||
"ubuntu-20.04": null
|
"ubuntu-22.04": null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -153,7 +153,7 @@
|
|||||||
{
|
{
|
||||||
"binaries": "apple-silicon",
|
"binaries": "apple-silicon",
|
||||||
"config": {
|
"config": {
|
||||||
"os": "ubuntu-20.04"
|
"os": "ubuntu-22.04"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
Clang-Format:
|
Clang-Format:
|
||||||
name: Clang-Format
|
name: Clang-Format
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -19,22 +19,22 @@ jobs:
|
|||||||
working-directory: Stockfish/src
|
working-directory: Stockfish/src
|
||||||
run: make -j build debug=yes
|
run: make -j build debug=yes
|
||||||
|
|
||||||
- name: Checkout fast-chess repo
|
- name: Checkout fastchess repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: Disservin/fast-chess
|
repository: Disservin/fastchess
|
||||||
path: fast-chess
|
path: fastchess
|
||||||
ref: d54af1910d5479c669dc731f1f54f9108a251951
|
ref: 894616028492ae6114835195f14a899f6fa237d3
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: fast-chess build
|
- name: fastchess build
|
||||||
working-directory: fast-chess
|
working-directory: fastchess
|
||||||
run: make -j
|
run: make -j
|
||||||
|
|
||||||
- name: Run games
|
- name: Run games
|
||||||
working-directory: fast-chess
|
working-directory: fastchess
|
||||||
run: |
|
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=sf1 cmd=/home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish\
|
||||||
-engine name=sf2 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
|
-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:
|
with:
|
||||||
repository: vondele/matetrack
|
repository: vondele/matetrack
|
||||||
path: matetrack
|
path: matetrack
|
||||||
ref: 814160f82e6428ed2f6522dc06c2a6fa539cd413
|
ref: 4f8a80860ed8f3607f05a9195df8b40203bdc360
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: matetrack install deps
|
- name: matetrack install deps
|
||||||
@@ -50,5 +50,22 @@ jobs:
|
|||||||
- name: Run matetrack
|
- name: Run matetrack
|
||||||
working-directory: matetrack
|
working-directory: matetrack
|
||||||
run: |
|
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
|
! 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:
|
sanitizers:
|
||||||
- name: Run with thread sanitizer
|
- name: Run with thread sanitizer
|
||||||
make_option: sanitize=thread
|
make_option: sanitize=thread
|
||||||
|
cxx_extra_flags: ""
|
||||||
instrumented_option: sanitizer-thread
|
instrumented_option: sanitizer-thread
|
||||||
- name: Run with UB sanitizer
|
- name: Run with UB sanitizer
|
||||||
make_option: sanitize=undefined
|
make_option: sanitize=undefined
|
||||||
|
cxx_extra_flags: ""
|
||||||
instrumented_option: sanitizer-undefined
|
instrumented_option: sanitizer-undefined
|
||||||
- name: Run under valgrind
|
- name: Run under valgrind
|
||||||
make_option: ""
|
make_option: ""
|
||||||
|
cxx_extra_flags: ""
|
||||||
instrumented_option: valgrind
|
instrumented_option: valgrind
|
||||||
- name: Run under valgrind-thread
|
- name: Run under valgrind-thread
|
||||||
make_option: ""
|
make_option: ""
|
||||||
|
cxx_extra_flags: ""
|
||||||
instrumented_option: valgrind-thread
|
instrumented_option: valgrind-thread
|
||||||
- name: Run non-instrumented
|
- name: Run non-instrumented
|
||||||
make_option: ""
|
make_option: ""
|
||||||
|
cxx_extra_flags: ""
|
||||||
instrumented_option: none
|
instrumented_option: none
|
||||||
|
- name: Run with glibcxx assertions
|
||||||
|
make_option: ""
|
||||||
|
cxx_extra_flags: -D_GLIBCXX_ASSERTIONS
|
||||||
|
instrumented_option: non
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: src
|
working-directory: src
|
||||||
@@ -72,7 +81,7 @@ jobs:
|
|||||||
|
|
||||||
- name: ${{ matrix.sanitizers.name }}
|
- name: ${{ matrix.sanitizers.name }}
|
||||||
run: |
|
run: |
|
||||||
export CXXFLAGS="-O1 -fno-inline"
|
export CXXFLAGS="-O1 -fno-inline ${{ matrix.sanitizers.cxx_extra_flags }}"
|
||||||
make clean
|
make clean
|
||||||
make -j4 ARCH=x86-64-sse41-popcnt ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null
|
make -j4 ARCH=x86-64-sse41-popcnt ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null
|
||||||
../tests/instrumented.sh --${{ matrix.sanitizers.instrumented_option }}
|
python3 ../tests/instrumented.py --${{ matrix.sanitizers.instrumented_option }} ./stockfish
|
||||||
|
|||||||
+11
-11
@@ -13,15 +13,15 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
config:
|
config:
|
||||||
- name: Ubuntu 20.04 GCC
|
- name: Ubuntu 22.04 GCC
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
compiler: g++
|
compiler: g++
|
||||||
comp: gcc
|
comp: gcc
|
||||||
run_32bit_tests: true
|
run_32bit_tests: true
|
||||||
run_64bit_tests: true
|
run_64bit_tests: true
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Ubuntu 20.04 Clang
|
- name: Ubuntu 22.04 Clang
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
compiler: clang++
|
compiler: clang++
|
||||||
comp: clang
|
comp: clang
|
||||||
run_32bit_tests: true
|
run_32bit_tests: true
|
||||||
@@ -139,11 +139,11 @@ jobs:
|
|||||||
- name: Build Docker container
|
- name: Build Docker container
|
||||||
if: matrix.config.base_image
|
if: matrix.config.base_image
|
||||||
run: |
|
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 }}
|
FROM ${{ matrix.config.base_image }}
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN apk update && apk add make g++
|
RUN apk update && apk add make g++
|
||||||
CMD ["sh", "script.sh"]
|
CMD ["sh", "src/script.sh"]
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
- name: Download required macOS packages
|
- name: Download required macOS packages
|
||||||
@@ -176,7 +176,7 @@ jobs:
|
|||||||
$COMPCXX -v
|
$COMPCXX -v
|
||||||
else
|
else
|
||||||
echo "$COMPCXX -v" > script.sh
|
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
|
fi
|
||||||
|
|
||||||
- name: Test help target
|
- name: Test help target
|
||||||
@@ -342,8 +342,8 @@ jobs:
|
|||||||
- name: Test riscv64 build
|
- name: Test riscv64 build
|
||||||
if: matrix.config.run_riscv64_tests
|
if: matrix.config.run_riscv64_tests
|
||||||
run: |
|
run: |
|
||||||
echo "export LDFLAGS='-static' && make clean && make -j4 ARCH=riscv64 build" > script.sh
|
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 }}/src:/app sf_builder
|
docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}:/app sf_builder
|
||||||
../tests/signature.sh $benchref
|
../tests/signature.sh $benchref
|
||||||
|
|
||||||
# ppc64 tests
|
# ppc64 tests
|
||||||
@@ -351,8 +351,8 @@ jobs:
|
|||||||
- name: Test ppc64 build
|
- name: Test ppc64 build
|
||||||
if: matrix.config.run_ppc64_tests
|
if: matrix.config.run_ppc64_tests
|
||||||
run: |
|
run: |
|
||||||
echo "export LDFLAGS='-static' && make clean && make -j4 ARCH=ppc-64 build" > script.sh
|
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 }}/src:/app sf_builder
|
docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}:/app sf_builder
|
||||||
../tests/signature.sh $benchref
|
../tests/signature.sh $benchref
|
||||||
|
|
||||||
# Other tests
|
# Other tests
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ jobs:
|
|||||||
mv "${{ matrix.config.simple_name }} ${{ matrix.binaries }}" stockfish-workflow
|
mv "${{ matrix.config.simple_name }} ${{ matrix.binaries }}" stockfish-workflow
|
||||||
cd stockfish-workflow
|
cd stockfish-workflow
|
||||||
cp -r src ../stockfish/
|
cp -r src ../stockfish/
|
||||||
|
cp -r scripts ../stockfish/
|
||||||
cp stockfish-$NAME-$BINARY$EXT ../stockfish/
|
cp stockfish-$NAME-$BINARY$EXT ../stockfish/
|
||||||
cp "Top CPU Contributors.txt" ../stockfish/
|
cp "Top CPU Contributors.txt" ../stockfish/
|
||||||
cp Copying.txt ../stockfish/
|
cp Copying.txt ../stockfish/
|
||||||
|
|||||||
@@ -10,3 +10,8 @@ src/-lstdc++.res
|
|||||||
# Neural network for the NNUE evaluation
|
# Neural network for the NNUE evaluation
|
||||||
**/*.nnue
|
**/*.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)
|
Bruno Pellanda (pellanda)
|
||||||
Bryan Cross (crossbr)
|
Bryan Cross (crossbr)
|
||||||
candirufish
|
candirufish
|
||||||
|
Carlos Esparza Sánchez (ces42)
|
||||||
Chess13234
|
Chess13234
|
||||||
|
Chris Bao (sscg13)
|
||||||
Chris Cain (ceebo)
|
Chris Cain (ceebo)
|
||||||
Ciekce
|
Ciekce
|
||||||
clefrks
|
clefrks
|
||||||
@@ -123,10 +125,12 @@ jundery
|
|||||||
Justin Blanchard (UncombedCoconut)
|
Justin Blanchard (UncombedCoconut)
|
||||||
Kelly Wilson
|
Kelly Wilson
|
||||||
Ken Takusagawa
|
Ken Takusagawa
|
||||||
|
Kenneth Lee (kennethlee33)
|
||||||
Kian E (KJE-98)
|
Kian E (KJE-98)
|
||||||
kinderchocolate
|
kinderchocolate
|
||||||
Kiran Panditrao (Krgp)
|
Kiran Panditrao (Krgp)
|
||||||
Kojirion
|
Kojirion
|
||||||
|
Krisztián Peőcz
|
||||||
Krystian Kuzniarek (kuzkry)
|
Krystian Kuzniarek (kuzkry)
|
||||||
Leonardo Ljubičić (ICCF World Champion)
|
Leonardo Ljubičić (ICCF World Champion)
|
||||||
Leonid Pechenik (lp--)
|
Leonid Pechenik (lp--)
|
||||||
@@ -143,6 +147,7 @@ Maciej Żenczykowski (zenczykowski)
|
|||||||
Malcolm Campbell (xoto10)
|
Malcolm Campbell (xoto10)
|
||||||
Mark Tenzer (31m059)
|
Mark Tenzer (31m059)
|
||||||
marotear
|
marotear
|
||||||
|
Mathias Parnaudeau (mparnaudeau)
|
||||||
Matt Ginsberg (mattginsberg)
|
Matt Ginsberg (mattginsberg)
|
||||||
Matthew Lai (matthewlai)
|
Matthew Lai (matthewlai)
|
||||||
Matthew Sullivan (Matt14916)
|
Matthew Sullivan (Matt14916)
|
||||||
@@ -176,6 +181,7 @@ Ofek Shochat (OfekShochat, ghostway)
|
|||||||
Ondrej Mosnáček (WOnder93)
|
Ondrej Mosnáček (WOnder93)
|
||||||
Ondřej Mišina (AndrovT)
|
Ondřej Mišina (AndrovT)
|
||||||
Oskar Werkelin Ahlin
|
Oskar Werkelin Ahlin
|
||||||
|
Ömer Faruk Tutkun (OmerFarukTutkun)
|
||||||
Pablo Vazquez
|
Pablo Vazquez
|
||||||
Panthee
|
Panthee
|
||||||
Pascal Romaret
|
Pascal Romaret
|
||||||
@@ -237,6 +243,7 @@ Unai Corzo (unaiic)
|
|||||||
Uri Blass (uriblass)
|
Uri Blass (uriblass)
|
||||||
Vince Negri (cuddlestmonkey)
|
Vince Negri (cuddlestmonkey)
|
||||||
Viren
|
Viren
|
||||||
|
Wencey Wang
|
||||||
windfishballad
|
windfishballad
|
||||||
xefoci7612
|
xefoci7612
|
||||||
Xiang Wang (KatyushaScarlet)
|
Xiang Wang (KatyushaScarlet)
|
||||||
|
|||||||
+1
-2
@@ -49,7 +49,7 @@ further discussion._
|
|||||||
- Provide a clear and concise description of the changes in the pull request
|
- Provide a clear and concise description of the changes in the pull request
|
||||||
description.
|
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
|
_Stockfish's development is not focused on adding new features. Thus any pull
|
||||||
request introducing new features will potentially be closed without further
|
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!
|
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
|
[copying-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt
|
||||||
[discord-link]: https://discord.gg/GWDRS3kU6R
|
[discord-link]: https://discord.gg/GWDRS3kU6R
|
||||||
[discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new
|
[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
|
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
|
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
|
distributing. If you make any changes to the source code, these changes must
|
||||||
also be made available under GPL v3.
|
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
|
[authors-link]: https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS
|
||||||
[build-link]: https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml
|
[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-uci-link]: https://github.com/official-stockfish/Stockfish/wiki/UCI-&-Commands
|
||||||
[wiki-usage-link]: https://github.com/official-stockfish/Stockfish/wiki/Download-and-usage
|
[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
|
[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
|
[build-badge]: https://img.shields.io/github/actions/workflow/status/official-stockfish/Stockfish/stockfish.yml?branch=master&style=for-the-badge&label=stockfish&logo=github
|
||||||
[commits-badge]: https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge
|
[commits-badge]: https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge
|
||||||
|
|||||||
@@ -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
|
[ "$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 the file CPU x86_64 architecture
|
||||||
set_arch_x86_64() {
|
set_arch_x86_64() {
|
||||||
if check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then
|
if check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then
|
||||||
@@ -43,6 +54,20 @@ set_arch_x86_64() {
|
|||||||
fi
|
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
|
# Check the system type
|
||||||
uname_s=$(uname -s)
|
uname_s=$(uname -s)
|
||||||
uname_m=$(uname -m)
|
uname_m=$(uname -m)
|
||||||
@@ -51,7 +76,7 @@ case $uname_s in
|
|||||||
case $uname_m in
|
case $uname_m in
|
||||||
'arm64')
|
'arm64')
|
||||||
true_arch='apple-silicon'
|
true_arch='apple-silicon'
|
||||||
file_arch='x86-64-sse41-popcnt' # Supported by Rosetta 2
|
file_arch='m1-apple-silicon'
|
||||||
;;
|
;;
|
||||||
'x86_64')
|
'x86_64')
|
||||||
flags=$(sysctl -n machdep.cpu.features machdep.cpu.leaf7_features | tr '\n' ' ' | tr '[:upper:]' '[:lower:]' | tr -d '_.')
|
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'
|
file_os='ubuntu'
|
||||||
true_arch='x86-32'
|
true_arch='x86-32'
|
||||||
;;
|
;;
|
||||||
|
'ppc64'*)
|
||||||
|
file_os='ubuntu'
|
||||||
|
set_arch_ppc_64
|
||||||
|
;;
|
||||||
'aarch64')
|
'aarch64')
|
||||||
file_os='android'
|
file_os='android'
|
||||||
true_arch='armv8'
|
true_arch='armv8'
|
||||||
@@ -90,6 +119,10 @@ case $uname_s in
|
|||||||
true_arch="$true_arch-neon"
|
true_arch="$true_arch-neon"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
'loongarch64'*)
|
||||||
|
file_os='linux'
|
||||||
|
set_arch_loongarch64
|
||||||
|
;;
|
||||||
*) # Unsupported machine type, exit with error
|
*) # Unsupported machine type, exit with error
|
||||||
printf 'Unsupported machine type: %s\n' "$uname_m"
|
printf 'Unsupported machine type: %s\n' "$uname_m"
|
||||||
exit 1
|
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
|
# 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
|
# Stockfish is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -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 \
|
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
|
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/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/affine_transform_sparse_input.h nnue/layers/clipped_relu.h nnue/layers/simd.h \
|
||||||
nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \
|
nnue/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
|
# avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512
|
||||||
# vnni256 = yes/no --- -mavx256vnni --- Use Intel Vector Neural Network Instructions 512 with 256bit operands
|
# 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
|
# 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
|
# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture
|
||||||
# dotprod = yes/no --- -DUSE_NEON_DOTPROD --- Use ARM advanced SIMD Int8 dot product instructions
|
# dotprod = yes/no --- -DUSE_NEON_DOTPROD --- Use ARM advanced SIMD Int8 dot product instructions
|
||||||
|
# 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
|
# Note that Makefile is space sensitive, so when adding new architectures
|
||||||
# or modifying existing flags, you have to make sure there are no extra spaces
|
# or modifying existing flags, you have to make sure there are no extra spaces
|
||||||
@@ -124,8 +128,9 @@ endif
|
|||||||
ifeq ($(ARCH), $(filter $(ARCH), \
|
ifeq ($(ARCH), $(filter $(ARCH), \
|
||||||
x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \
|
x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \
|
||||||
x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \
|
x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \
|
||||||
x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \
|
x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-64-altivec ppc-64-vsx ppc-32 e2k \
|
||||||
armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64 loongarch64))
|
armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64 \
|
||||||
|
loongarch64 loongarch64-lsx loongarch64-lasx))
|
||||||
SUPPORTED_ARCH=true
|
SUPPORTED_ARCH=true
|
||||||
else
|
else
|
||||||
SUPPORTED_ARCH=false
|
SUPPORTED_ARCH=false
|
||||||
@@ -148,9 +153,13 @@ avxvnni = no
|
|||||||
avx512 = no
|
avx512 = no
|
||||||
vnni256 = no
|
vnni256 = no
|
||||||
vnni512 = no
|
vnni512 = no
|
||||||
|
altivec = no
|
||||||
|
vsx = no
|
||||||
neon = no
|
neon = no
|
||||||
dotprod = no
|
dotprod = no
|
||||||
arm_version = 0
|
arm_version = 0
|
||||||
|
lsx = no
|
||||||
|
lasx = no
|
||||||
STRIP = strip
|
STRIP = strip
|
||||||
|
|
||||||
ifneq ($(shell which clang-format-18 2> /dev/null),)
|
ifneq ($(shell which clang-format-18 2> /dev/null),)
|
||||||
@@ -355,6 +364,20 @@ ifeq ($(ARCH),ppc-64)
|
|||||||
prefetch = yes
|
prefetch = yes
|
||||||
endif
|
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)
|
ifeq ($(findstring e2k,$(ARCH)),e2k)
|
||||||
arch = e2k
|
arch = e2k
|
||||||
mmx = yes
|
mmx = yes
|
||||||
@@ -370,8 +393,19 @@ ifeq ($(ARCH),riscv64)
|
|||||||
arch = riscv64
|
arch = riscv64
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ARCH),loongarch64)
|
ifeq ($(findstring loongarch64,$(ARCH)),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
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -408,7 +442,7 @@ ifeq ($(COMP),gcc)
|
|||||||
ifeq ($(ARCH),riscv64)
|
ifeq ($(ARCH),riscv64)
|
||||||
CXXFLAGS += -latomic
|
CXXFLAGS += -latomic
|
||||||
endif
|
endif
|
||||||
else ifeq ($(ARCH),loongarch64)
|
else ifeq ($(arch),loongarch64)
|
||||||
CXXFLAGS += -latomic
|
CXXFLAGS += -latomic
|
||||||
else
|
else
|
||||||
CXXFLAGS += -m$(bits)
|
CXXFLAGS += -m$(bits)
|
||||||
@@ -480,7 +514,7 @@ ifeq ($(COMP),clang)
|
|||||||
ifeq ($(ARCH),riscv64)
|
ifeq ($(ARCH),riscv64)
|
||||||
CXXFLAGS += -latomic
|
CXXFLAGS += -latomic
|
||||||
endif
|
endif
|
||||||
else ifeq ($(ARCH),loongarch64)
|
else ifeq ($(arch),loongarch64)
|
||||||
CXXFLAGS += -latomic
|
CXXFLAGS += -latomic
|
||||||
else
|
else
|
||||||
CXXFLAGS += -m$(bits)
|
CXXFLAGS += -m$(bits)
|
||||||
@@ -634,7 +668,7 @@ else
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(popcnt),yes)
|
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
|
CXXFLAGS += -DUSE_POPCNT
|
||||||
else
|
else
|
||||||
CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT
|
CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT
|
||||||
@@ -704,6 +738,20 @@ ifeq ($(mmx),yes)
|
|||||||
endif
|
endif
|
||||||
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)
|
ifeq ($(neon),yes)
|
||||||
CXXFLAGS += -DUSE_NEON=$(arm_version)
|
CXXFLAGS += -DUSE_NEON=$(arm_version)
|
||||||
ifeq ($(KERNEL),Linux)
|
ifeq ($(KERNEL),Linux)
|
||||||
@@ -719,6 +767,18 @@ ifeq ($(dotprod),yes)
|
|||||||
CXXFLAGS += -march=armv8.2-a+dotprod -DUSE_NEON_DOTPROD
|
CXXFLAGS += -march=armv8.2-a+dotprod -DUSE_NEON_DOTPROD
|
||||||
endif
|
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
|
### 3.7 pext
|
||||||
ifeq ($(pext),yes)
|
ifeq ($(pext),yes)
|
||||||
CXXFLAGS += -DUSE_PEXT
|
CXXFLAGS += -DUSE_PEXT
|
||||||
@@ -791,71 +851,75 @@ endif
|
|||||||
### ==========================================================================
|
### ==========================================================================
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo ""
|
@echo "" && \
|
||||||
@echo "To compile stockfish, type: "
|
echo "To compile stockfish, type: " && \
|
||||||
@echo ""
|
echo "" && \
|
||||||
@echo "make -j target [ARCH=arch] [COMP=compiler] [COMPCXX=cxx]"
|
echo "make -j target [ARCH=arch] [COMP=compiler] [COMPCXX=cxx]" && \
|
||||||
@echo ""
|
echo "" && \
|
||||||
@echo "Supported targets:"
|
echo "Supported targets:" && \
|
||||||
@echo ""
|
echo "" && \
|
||||||
@echo "help > Display architecture details"
|
echo "help > Display architecture details" && \
|
||||||
@echo "profile-build > standard build with profile-guided optimization"
|
echo "profile-build > standard build with profile-guided optimization" && \
|
||||||
@echo "build > skip profile-guided optimization"
|
echo "build > skip profile-guided optimization" && \
|
||||||
@echo "net > Download the default nnue nets"
|
echo "net > Download the default nnue nets" && \
|
||||||
@echo "strip > Strip executable"
|
echo "strip > Strip executable" && \
|
||||||
@echo "install > Install executable"
|
echo "install > Install executable" && \
|
||||||
@echo "clean > Clean up"
|
echo "clean > Clean up" && \
|
||||||
@echo ""
|
echo "" && \
|
||||||
@echo "Supported archs:"
|
echo "Supported archs:" && \
|
||||||
@echo ""
|
echo "" && \
|
||||||
@echo "native > select the best architecture for the host processor (default)"
|
echo "native > select the best architecture for the host processor (default)" && \
|
||||||
@echo "x86-64-vnni512 > x86 64-bit with vnni 512bit support"
|
echo "x86-64-vnni512 > x86 64-bit with vnni 512bit support" && \
|
||||||
@echo "x86-64-vnni256 > x86 64-bit with vnni 512bit support, limit operands to 256bit wide"
|
echo "x86-64-vnni256 > x86 64-bit with vnni 512bit support, limit operands to 256bit wide" && \
|
||||||
@echo "x86-64-avx512 > x86 64-bit with avx512 support"
|
echo "x86-64-avx512 > x86 64-bit with avx512 support" && \
|
||||||
@echo "x86-64-avxvnni > x86 64-bit with vnni 256bit 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-bmi2 > x86 64-bit with bmi2 support" && \
|
||||||
@echo "x86-64-avx2 > x86 64-bit with avx2 support"
|
echo "x86-64-avx2 > x86 64-bit with avx2 support" && \
|
||||||
@echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support"
|
echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support" && \
|
||||||
@echo "x86-64-modern > deprecated, currently x86-64-sse41-popcnt"
|
echo "x86-64-modern > deprecated, currently x86-64-sse41-popcnt" && \
|
||||||
@echo "x86-64-ssse3 > x86 64-bit with ssse3 support"
|
echo "x86-64-ssse3 > x86 64-bit with ssse3 support" && \
|
||||||
@echo "x86-64-sse3-popcnt > x86 64-bit with sse3 compile and popcnt support"
|
echo "x86-64-sse3-popcnt > x86 64-bit with sse3 compile and popcnt support" && \
|
||||||
@echo "x86-64 > x86 64-bit generic (with sse2 support)"
|
echo "x86-64 > x86 64-bit generic (with sse2 support)" && \
|
||||||
@echo "x86-32-sse41-popcnt > x86 32-bit with sse41 and popcnt support"
|
echo "x86-32-sse41-popcnt > x86 32-bit with sse41 and popcnt support" && \
|
||||||
@echo "x86-32-sse2 > x86 32-bit with sse2 support"
|
echo "x86-32-sse2 > x86 32-bit with sse2 support" && \
|
||||||
@echo "x86-32 > x86 32-bit generic (with mmx compile support)"
|
echo "x86-32 > x86 32-bit generic (with mmx compile support)" && \
|
||||||
@echo "ppc-64 > PPC 64-bit"
|
echo "ppc-64 > PPC 64-bit" && \
|
||||||
@echo "ppc-32 > PPC 32-bit"
|
echo "ppc-64-altivec > PPC 64-bit with altivec support" && \
|
||||||
@echo "armv7 > ARMv7 32-bit"
|
echo "ppc-64-vsx > PPC 64-bit with vsx support" && \
|
||||||
@echo "armv7-neon > ARMv7 32-bit with popcnt and neon"
|
echo "ppc-32 > PPC 32-bit" && \
|
||||||
@echo "armv8 > ARMv8 64-bit with popcnt and neon"
|
echo "armv7 > ARMv7 32-bit" && \
|
||||||
@echo "armv8-dotprod > ARMv8 64-bit with popcnt, neon and dot product support"
|
echo "armv7-neon > ARMv7 32-bit with popcnt and neon" && \
|
||||||
@echo "e2k > Elbrus 2000"
|
echo "armv8 > ARMv8 64-bit with popcnt and neon" && \
|
||||||
@echo "apple-silicon > Apple silicon ARM64"
|
echo "armv8-dotprod > ARMv8 64-bit with popcnt, neon and dot product support" && \
|
||||||
@echo "general-64 > unspecified 64-bit"
|
echo "e2k > Elbrus 2000" && \
|
||||||
@echo "general-32 > unspecified 32-bit"
|
echo "apple-silicon > Apple silicon ARM64" && \
|
||||||
@echo "riscv64 > RISC-V 64-bit"
|
echo "general-64 > unspecified 64-bit" && \
|
||||||
@echo "loongarch64 > LoongArch 64-bit"
|
echo "general-32 > unspecified 32-bit" && \
|
||||||
@echo ""
|
echo "riscv64 > RISC-V 64-bit" && \
|
||||||
@echo "Supported compilers:"
|
echo "loongarch64 > LoongArch 64-bit" && \
|
||||||
@echo ""
|
echo "loongarch64-lsx > LoongArch 64-bit with SIMD eXtension" && \
|
||||||
@echo "gcc > GNU compiler (default)"
|
echo "loongarch64-lasx > LoongArch 64-bit with Advanced SIMD eXtension" && \
|
||||||
@echo "mingw > GNU compiler with MinGW under Windows"
|
echo "" && \
|
||||||
@echo "clang > LLVM Clang compiler"
|
echo "Supported compilers:" && \
|
||||||
@echo "icx > Intel oneAPI DPC++/C++ Compiler"
|
echo "" && \
|
||||||
@echo "ndk > Google NDK to cross-compile for Android"
|
echo "gcc > GNU compiler (default)" && \
|
||||||
@echo ""
|
echo "mingw > GNU compiler with MinGW under Windows" && \
|
||||||
@echo "Simple examples. If you don't know what to do, you likely want to run one of: "
|
echo "clang > LLVM Clang compiler" && \
|
||||||
@echo ""
|
echo "icx > Intel oneAPI DPC++/C++ Compiler" && \
|
||||||
@echo "make -j profile-build ARCH=x86-64-avx2 # typically a fast compile for common systems "
|
echo "ndk > Google NDK to cross-compile for Android" && \
|
||||||
@echo "make -j profile-build ARCH=x86-64-sse41-popcnt # A more portable compile for 64-bit systems "
|
echo "" && \
|
||||||
@echo "make -j profile-build ARCH=x86-64 # A portable compile for 64-bit systems "
|
echo "Simple examples. If you don't know what to do, you likely want to run one of: " && \
|
||||||
@echo ""
|
echo "" && \
|
||||||
@echo "Advanced examples, for experienced users: "
|
echo "make -j profile-build ARCH=x86-64-avx2 # typically a fast compile for common systems " && \
|
||||||
@echo ""
|
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-avxvnni"
|
echo "make -j profile-build ARCH=x86-64 # A portable compile for 64-bit systems " && \
|
||||||
@echo "make -j profile-build ARCH=x86-64-avxvnni COMP=gcc COMPCXX=g++-12.0"
|
echo "" && \
|
||||||
@echo "make -j build ARCH=x86-64-ssse3 COMP=clang"
|
echo "Advanced examples, for experienced users: " && \
|
||||||
@echo ""
|
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)
|
ifneq ($(SUPPORTED_ARCH), true)
|
||||||
@echo "Specify a supported architecture with the ARCH option for more details"
|
@echo "Specify a supported architecture with the ARCH option for more details"
|
||||||
@echo ""
|
@echo ""
|
||||||
@@ -917,59 +981,9 @@ profileclean:
|
|||||||
@rm -f stockfish.res
|
@rm -f stockfish.res
|
||||||
@rm -f ./-lstdc++.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)
|
# evaluation network (nnue)
|
||||||
net:
|
net:
|
||||||
$(call netvariables, EvalFileDefaultNameBig)
|
@$(SHELL) ../scripts/net.sh
|
||||||
$(call fetch_network)
|
|
||||||
$(call netvariables, EvalFileDefaultNameSmall)
|
|
||||||
$(call fetch_network)
|
|
||||||
|
|
||||||
format:
|
format:
|
||||||
$(CLANG-FORMAT) -i $(SRCS) $(HEADERS) -style=file
|
$(CLANG-FORMAT) -i $(SRCS) $(HEADERS) -style=file
|
||||||
@@ -986,61 +1000,71 @@ all: $(EXE) .depend
|
|||||||
|
|
||||||
config-sanity: net
|
config-sanity: net
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Config:"
|
@echo "Config:" && \
|
||||||
@echo "debug: '$(debug)'"
|
echo "debug: '$(debug)'" && \
|
||||||
@echo "sanitize: '$(sanitize)'"
|
echo "sanitize: '$(sanitize)'" && \
|
||||||
@echo "optimize: '$(optimize)'"
|
echo "optimize: '$(optimize)'" && \
|
||||||
@echo "arch: '$(arch)'"
|
echo "arch: '$(arch)'" && \
|
||||||
@echo "bits: '$(bits)'"
|
echo "bits: '$(bits)'" && \
|
||||||
@echo "kernel: '$(KERNEL)'"
|
echo "kernel: '$(KERNEL)'" && \
|
||||||
@echo "os: '$(OS)'"
|
echo "os: '$(OS)'" && \
|
||||||
@echo "prefetch: '$(prefetch)'"
|
echo "prefetch: '$(prefetch)'" && \
|
||||||
@echo "popcnt: '$(popcnt)'"
|
echo "popcnt: '$(popcnt)'" && \
|
||||||
@echo "pext: '$(pext)'"
|
echo "pext: '$(pext)'" && \
|
||||||
@echo "sse: '$(sse)'"
|
echo "sse: '$(sse)'" && \
|
||||||
@echo "mmx: '$(mmx)'"
|
echo "mmx: '$(mmx)'" && \
|
||||||
@echo "sse2: '$(sse2)'"
|
echo "sse2: '$(sse2)'" && \
|
||||||
@echo "ssse3: '$(ssse3)'"
|
echo "ssse3: '$(ssse3)'" && \
|
||||||
@echo "sse41: '$(sse41)'"
|
echo "sse41: '$(sse41)'" && \
|
||||||
@echo "avx2: '$(avx2)'"
|
echo "avx2: '$(avx2)'" && \
|
||||||
@echo "avxvnni: '$(avxvnni)'"
|
echo "avxvnni: '$(avxvnni)'" && \
|
||||||
@echo "avx512: '$(avx512)'"
|
echo "avx512: '$(avx512)'" && \
|
||||||
@echo "vnni256: '$(vnni256)'"
|
echo "vnni256: '$(vnni256)'" && \
|
||||||
@echo "vnni512: '$(vnni512)'"
|
echo "vnni512: '$(vnni512)'" && \
|
||||||
@echo "neon: '$(neon)'"
|
echo "altivec: '$(altivec)'" && \
|
||||||
@echo "dotprod: '$(dotprod)'"
|
echo "vsx: '$(vsx)'" && \
|
||||||
@echo "arm_version: '$(arm_version)'"
|
echo "neon: '$(neon)'" && \
|
||||||
@echo "target_windows: '$(target_windows)'"
|
echo "dotprod: '$(dotprod)'" && \
|
||||||
@echo ""
|
echo "arm_version: '$(arm_version)'" && \
|
||||||
@echo "Flags:"
|
echo "lsx: '$(lsx)'" && \
|
||||||
@echo "CXX: $(CXX)"
|
echo "lasx: '$(lasx)'" && \
|
||||||
@echo "CXXFLAGS: $(CXXFLAGS)"
|
echo "target_windows: '$(target_windows)'" && \
|
||||||
@echo "LDFLAGS: $(LDFLAGS)"
|
echo "" && \
|
||||||
@echo ""
|
echo "Flags:" && \
|
||||||
@echo "Testing config sanity. If this fails, try 'make help' ..."
|
echo "CXX: $(CXX)" && \
|
||||||
@echo ""
|
echo "CXXFLAGS: $(CXXFLAGS)" && \
|
||||||
@test "$(debug)" = "yes" || test "$(debug)" = "no"
|
echo "LDFLAGS: $(LDFLAGS)" && \
|
||||||
@test "$(optimize)" = "yes" || test "$(optimize)" = "no"
|
echo "" && \
|
||||||
@test "$(SUPPORTED_ARCH)" = "true"
|
echo "Testing config sanity. If this fails, try 'make help' ..." && \
|
||||||
@test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
|
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)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "e2k" || \
|
||||||
test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" || test "$(arch)" = "riscv64" || test "$(arch)" = "loongarch64"
|
test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" || \
|
||||||
@test "$(bits)" = "32" || test "$(bits)" = "64"
|
test "$(arch)" = "riscv64" || test "$(arch)" = "loongarch64") && \
|
||||||
@test "$(prefetch)" = "yes" || test "$(prefetch)" = "no"
|
(test "$(bits)" = "32" || test "$(bits)" = "64") && \
|
||||||
@test "$(popcnt)" = "yes" || test "$(popcnt)" = "no"
|
(test "$(prefetch)" = "yes" || test "$(prefetch)" = "no") && \
|
||||||
@test "$(pext)" = "yes" || test "$(pext)" = "no"
|
(test "$(popcnt)" = "yes" || test "$(popcnt)" = "no") && \
|
||||||
@test "$(sse)" = "yes" || test "$(sse)" = "no"
|
(test "$(pext)" = "yes" || test "$(pext)" = "no") && \
|
||||||
@test "$(mmx)" = "yes" || test "$(mmx)" = "no"
|
(test "$(sse)" = "yes" || test "$(sse)" = "no") && \
|
||||||
@test "$(sse2)" = "yes" || test "$(sse2)" = "no"
|
(test "$(mmx)" = "yes" || test "$(mmx)" = "no") && \
|
||||||
@test "$(ssse3)" = "yes" || test "$(ssse3)" = "no"
|
(test "$(sse2)" = "yes" || test "$(sse2)" = "no") && \
|
||||||
@test "$(sse41)" = "yes" || test "$(sse41)" = "no"
|
(test "$(ssse3)" = "yes" || test "$(ssse3)" = "no") && \
|
||||||
@test "$(avx2)" = "yes" || test "$(avx2)" = "no"
|
(test "$(sse41)" = "yes" || test "$(sse41)" = "no") && \
|
||||||
@test "$(avx512)" = "yes" || test "$(avx512)" = "no"
|
(test "$(avx2)" = "yes" || test "$(avx2)" = "no") && \
|
||||||
@test "$(vnni256)" = "yes" || test "$(vnni256)" = "no"
|
(test "$(avx512)" = "yes" || test "$(avx512)" = "no") && \
|
||||||
@test "$(vnni512)" = "yes" || test "$(vnni512)" = "no"
|
(test "$(vnni256)" = "yes" || test "$(vnni256)" = "no") && \
|
||||||
@test "$(neon)" = "yes" || test "$(neon)" = "no"
|
(test "$(vnni512)" = "yes" || test "$(vnni512)" = "no") && \
|
||||||
@test "$(comp)" = "gcc" || test "$(comp)" = "icx" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" \
|
(test "$(altivec)" = "yes" || test "$(altivec)" = "no") && \
|
||||||
|| test "$(comp)" = "armv7a-linux-androideabi16-clang" || test "$(comp)" = "aarch64-linux-android21-clang"
|
(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)
|
$(EXE): $(OBJS)
|
||||||
+$(CXX) -o $@ $(OBJS) $(LDFLAGS)
|
+$(CXX) -o $@ $(OBJS) $(LDFLAGS)
|
||||||
|
|||||||
+350
-1
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "benchmark.h"
|
#include "benchmark.h"
|
||||||
|
#include "numa.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -91,6 +92,282 @@ const std::vector<std::string> Defaults = {
|
|||||||
};
|
};
|
||||||
// clang-format on
|
// 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
|
||||||
|
|
||||||
namespace Stockfish::Benchmark {
|
namespace Stockfish::Benchmark {
|
||||||
@@ -160,4 +437,76 @@ std::vector<std::string> setup_bench(const std::string& currentFen, std::istream
|
|||||||
return list;
|
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
|
} // namespace Stockfish
|
||||||
+11
-1
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -27,6 +27,16 @@ namespace Stockfish::Benchmark {
|
|||||||
|
|
||||||
std::vector<std::string> setup_bench(const std::string&, std::istream&);
|
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
|
} // namespace Stockfish
|
||||||
|
|
||||||
#endif // #ifndef BENCHMARK_H_INCLUDED
|
#endif // #ifndef BENCHMARK_H_INCLUDED
|
||||||
|
|||||||
+26
-20
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -34,15 +34,14 @@ Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
|||||||
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||||
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||||
|
|
||||||
Magic RookMagics[SQUARE_NB];
|
alignas(64) Magic Magics[SQUARE_NB][2];
|
||||||
Magic BishopMagics[SQUARE_NB];
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
Bitboard RookTable[0x19000]; // To store rook attacks
|
Bitboard RookTable[0x19000]; // To store rook attacks
|
||||||
Bitboard BishopTable[0x1480]; // To store bishop 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
|
// Returns the bitboard of target square for the given step
|
||||||
// from the given square. If the step is off the board, returns empty bitboard.
|
// 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)
|
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||||
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
|
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
|
||||||
|
|
||||||
init_magics(ROOK, RookTable, RookMagics);
|
init_magics(ROOK, RookTable, Magics);
|
||||||
init_magics(BISHOP, BishopTable, BishopMagics);
|
init_magics(BISHOP, BishopTable, Magics);
|
||||||
|
|
||||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
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
|
// Computes all rook and bishop attacks at startup. Magic
|
||||||
// bitboards are used to look up attacks of sliding pieces. As a reference see
|
// bitboards are used to look up attacks of sliding pieces. As a reference see
|
||||||
// www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so
|
// https://www.chessprogramming.org/Magic_Bitboards. In particular, here we use
|
||||||
// called "fancy" approach.
|
// the so called "fancy" approach.
|
||||||
void init_magics(PieceType pt, Bitboard table[], Magic magics[]) {
|
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
|
// Optimal PRNG seeds to pick the correct magics in the shortest time
|
||||||
int seeds[][RANK_NB] = {{8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020},
|
int seeds[][RANK_NB] = {{8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020},
|
||||||
{728, 10316, 55013, 32803, 12281, 15100, 16645, 255}};
|
{728, 10316, 55013, 32803, 12281, 15100, 16645, 255}};
|
||||||
|
|
||||||
Bitboard occupancy[4096], reference[4096], edges, b;
|
Bitboard occupancy[4096];
|
||||||
int epoch[4096] = {}, cnt = 0, size = 0;
|
int epoch[4096] = {}, cnt = 0;
|
||||||
|
#endif
|
||||||
|
Bitboard reference[4096];
|
||||||
|
int size = 0;
|
||||||
|
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||||
{
|
{
|
||||||
// Board edges are not considered in the relevant occupancies
|
// 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
|
// 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
|
// '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
|
// 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
|
// 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.
|
// 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.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
|
// Set the offset for the attacks table of the square. We have individual
|
||||||
// table sizes for each square with "Fancy Magic Bitboards".
|
// 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
|
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
|
||||||
// store the corresponding sliding attack bitboard in reference[].
|
// store the corresponding sliding attack bitboard in reference[].
|
||||||
b = size = 0;
|
Bitboard b = 0;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
#ifndef USE_PEXT
|
||||||
occupancy[size] = b;
|
occupancy[size] = b;
|
||||||
|
#endif
|
||||||
reference[size] = sliding_attack(pt, s, b);
|
reference[size] = sliding_attack(pt, s, b);
|
||||||
|
|
||||||
if (HasPext)
|
if (HasPext)
|
||||||
@@ -184,9 +191,7 @@ void init_magics(PieceType pt, Bitboard table[], Magic magics[]) {
|
|||||||
b = (b - m.mask) & m.mask;
|
b = (b - m.mask) & m.mask;
|
||||||
} while (b);
|
} while (b);
|
||||||
|
|
||||||
if (HasPext)
|
#ifndef USE_PEXT
|
||||||
continue;
|
|
||||||
|
|
||||||
PRNG rng(seeds[Is64Bit][rank_of(s)]);
|
PRNG rng(seeds[Is64Bit][rank_of(s)]);
|
||||||
|
|
||||||
// Find a magic for square 's' picking up an (almost) random number
|
// 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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-15
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -67,27 +68,31 @@ extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
|||||||
// Magic holds all magic bitboards relevant data for a single square
|
// Magic holds all magic bitboards relevant data for a single square
|
||||||
struct Magic {
|
struct Magic {
|
||||||
Bitboard mask;
|
Bitboard mask;
|
||||||
Bitboard magic;
|
|
||||||
Bitboard* attacks;
|
Bitboard* attacks;
|
||||||
unsigned shift;
|
#ifndef USE_PEXT
|
||||||
|
Bitboard magic;
|
||||||
|
unsigned shift;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Compute the attack's index using the 'magic bitboards' approach
|
// Compute the attack's index using the 'magic bitboards' approach
|
||||||
unsigned index(Bitboard occupied) const {
|
unsigned index(Bitboard occupied) const {
|
||||||
|
|
||||||
if (HasPext)
|
#ifdef USE_PEXT
|
||||||
return unsigned(pext(occupied, mask));
|
return unsigned(pext(occupied, mask));
|
||||||
|
#else
|
||||||
if (Is64Bit)
|
if (Is64Bit)
|
||||||
return unsigned(((occupied & mask) * magic) >> shift);
|
return unsigned(((occupied & mask) * magic) >> shift);
|
||||||
|
|
||||||
unsigned lo = unsigned(occupied) & unsigned(mask);
|
unsigned lo = unsigned(occupied) & unsigned(mask);
|
||||||
unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32);
|
unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32);
|
||||||
return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift;
|
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 Magics[SQUARE_NB][2];
|
||||||
extern Magic BishopMagics[SQUARE_NB];
|
|
||||||
|
|
||||||
constexpr Bitboard square_bb(Square s) {
|
constexpr Bitboard square_bb(Square s) {
|
||||||
assert(is_ok(s));
|
assert(is_ok(s));
|
||||||
@@ -229,9 +234,8 @@ inline Bitboard attacks_bb(Square s, Bitboard occupied) {
|
|||||||
switch (Pt)
|
switch (Pt)
|
||||||
{
|
{
|
||||||
case BISHOP :
|
case BISHOP :
|
||||||
return BishopMagics[s].attacks[BishopMagics[s].index(occupied)];
|
|
||||||
case ROOK :
|
case ROOK :
|
||||||
return RookMagics[s].attacks[RookMagics[s].index(occupied)];
|
return Magics[s][Pt - BISHOP].attacks_bb(occupied);
|
||||||
case QUEEN :
|
case QUEEN :
|
||||||
return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
|
return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
|
||||||
default :
|
default :
|
||||||
@@ -265,11 +269,10 @@ inline int popcount(Bitboard b) {
|
|||||||
|
|
||||||
#ifndef USE_POPCNT
|
#ifndef USE_POPCNT
|
||||||
|
|
||||||
union {
|
std::uint16_t indices[4];
|
||||||
Bitboard bb;
|
std::memcpy(indices, &b, sizeof(b));
|
||||||
uint16_t u[4];
|
return PopCnt16[indices[0]] + PopCnt16[indices[1]] + PopCnt16[indices[2]]
|
||||||
} v = {b};
|
+ PopCnt16[indices[3]];
|
||||||
return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]];
|
|
||||||
|
|
||||||
#elif defined(_MSC_VER)
|
#elif defined(_MSC_VER)
|
||||||
|
|
||||||
|
|||||||
+99
-65
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -47,8 +47,8 @@ namespace NN = Eval::NNUE;
|
|||||||
constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||||
constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048;
|
constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048;
|
||||||
|
|
||||||
Engine::Engine(std::string path) :
|
Engine::Engine(std::optional<std::string> path) :
|
||||||
binaryDirectory(CommandLine::get_binary_directory(path)),
|
binaryDirectory(path ? CommandLine::get_binary_directory(*path) : ""),
|
||||||
numaContext(NumaConfig::from_system()),
|
numaContext(NumaConfig::from_system()),
|
||||||
states(new std::deque<StateInfo>(1)),
|
states(new std::deque<StateInfo>(1)),
|
||||||
threads(),
|
threads(),
|
||||||
@@ -58,58 +58,84 @@ Engine::Engine(std::string path) :
|
|||||||
NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG),
|
NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG),
|
||||||
NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) {
|
NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) {
|
||||||
pos.set(StartFEN, false, &states->back());
|
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) {
|
options.add( //
|
||||||
set_numa_config_from_option(o);
|
"Debug Log File", Option("", [](const Option& o) {
|
||||||
return numa_config_information_as_string() + "\n" + thread_binding_information_as_string();
|
start_logger(o);
|
||||||
});
|
return std::nullopt;
|
||||||
|
}));
|
||||||
|
|
||||||
options["Threads"] << Option(1, 1, 1024, [this](const Option&) {
|
options.add( //
|
||||||
resize_threads();
|
"NumaPolicy", Option("auto", [this](const Option& o) {
|
||||||
return thread_binding_information_as_string();
|
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) {
|
options.add( //
|
||||||
set_tt_size(o);
|
"Threads", Option(1, 1, 1024, [this](const Option&) {
|
||||||
return std::nullopt;
|
resize_threads();
|
||||||
});
|
return thread_allocation_information_as_string();
|
||||||
|
}));
|
||||||
|
|
||||||
options["Clear Hash"] << Option([this](const Option&) {
|
options.add( //
|
||||||
search_clear();
|
"Hash", Option(16, 1, MaxHashMB, [this](const Option& o) {
|
||||||
return std::nullopt;
|
set_tt_size(o);
|
||||||
});
|
return std::nullopt;
|
||||||
options["Ponder"] << Option(false);
|
}));
|
||||||
options["MultiPV"] << Option(1, 1, MAX_MOVES);
|
|
||||||
options["Skill Level"] << Option(20, 0, 20);
|
options.add( //
|
||||||
options["Move Overhead"] << Option(10, 0, 5000);
|
"Clear Hash", Option([this](const Option&) {
|
||||||
options["nodestime"] << Option(0, 0, 10000);
|
search_clear();
|
||||||
options["UCI_Chess960"] << Option(false);
|
return std::nullopt;
|
||||||
options["UCI_LimitStrength"] << Option(false);
|
}));
|
||||||
options["UCI_Elo"] << Option(Stockfish::Search::Skill::LowestElo,
|
|
||||||
Stockfish::Search::Skill::LowestElo,
|
options.add( //
|
||||||
Stockfish::Search::Skill::HighestElo);
|
"Ponder", Option(false));
|
||||||
options["UCI_ShowWDL"] << Option(false);
|
|
||||||
options["SyzygyPath"] << Option("", [](const Option& o) {
|
options.add( //
|
||||||
Tablebases::init(o);
|
"MultiPV", Option(1, 1, MAX_MOVES));
|
||||||
return std::nullopt;
|
|
||||||
});
|
options.add("Skill Level", Option(20, 0, 20));
|
||||||
options["SyzygyProbeDepth"] << Option(1, 1, 100);
|
|
||||||
options["Syzygy50MoveRule"] << Option(true);
|
options.add("Move Overhead", Option(10, 0, 5000));
|
||||||
options["SyzygyProbeLimit"] << Option(7, 0, 7);
|
|
||||||
options["EvalFile"] << Option(EvalFileDefaultNameBig, [this](const Option& o) {
|
options.add("nodestime", Option(0, 0, 10000));
|
||||||
load_big_network(o);
|
|
||||||
return std::nullopt;
|
options.add("UCI_Chess960", Option(false));
|
||||||
});
|
|
||||||
options["EvalFileSmall"] << Option(EvalFileDefaultNameSmall, [this](const Option& o) {
|
options.add("UCI_LimitStrength", Option(false));
|
||||||
load_small_network(o);
|
|
||||||
return std::nullopt;
|
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();
|
load_networks();
|
||||||
resize_threads();
|
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) {
|
void Engine::go(Search::LimitsType& limits) {
|
||||||
assert(limits.perft == 0);
|
assert(limits.perft == 0);
|
||||||
verify_networks();
|
verify_networks();
|
||||||
limits.capSq = capSq;
|
|
||||||
|
|
||||||
threads.start_thinking(options, pos, states, limits);
|
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);
|
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::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) {
|
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));
|
states = StateListPtr(new std::deque<StateInfo>(1));
|
||||||
pos.set(fen, options["UCI_Chess960"], &states->back());
|
pos.set(fen, options["UCI_Chess960"], &states->back());
|
||||||
|
|
||||||
capSq = SQ_NONE;
|
|
||||||
for (const auto& move : moves)
|
for (const auto& move : moves)
|
||||||
{
|
{
|
||||||
auto m = UCIEngine::to_move(pos, move);
|
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();
|
states->emplace_back();
|
||||||
pos.do_move(m, states->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
|
// network related
|
||||||
|
|
||||||
void Engine::verify_networks() const {
|
void Engine::verify_networks() const {
|
||||||
networks->big.verify(options["EvalFile"]);
|
networks->big.verify(options["EvalFile"], onVerifyNetworks);
|
||||||
networks->small.verify(options["EvalFileSmall"]);
|
networks->small.verify(options["EvalFileSmall"], onVerifyNetworks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::load_networks() {
|
void Engine::load_networks() {
|
||||||
@@ -285,6 +308,8 @@ std::string Engine::visualize() const {
|
|||||||
return ss.str();
|
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 {
|
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();
|
auto counts = threads.get_bound_thread_count_by_numa_node();
|
||||||
const NumaConfig& cfg = numaContext.get_numa_config();
|
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 {
|
std::string Engine::thread_binding_information_as_string() const {
|
||||||
auto boundThreadsByNode = get_bound_thread_count_by_numa_node();
|
auto boundThreadsByNode = get_bound_thread_count_by_numa_node();
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
size_t threadsSize = threads.size();
|
|
||||||
ss << "Using " << threadsSize << (threadsSize > 1 ? " threads" : " thread");
|
|
||||||
|
|
||||||
if (boundThreadsByNode.empty())
|
if (boundThreadsByNode.empty())
|
||||||
return ss.str();
|
return ss.str();
|
||||||
|
|
||||||
ss << " with NUMA node thread binding: ";
|
|
||||||
|
|
||||||
bool isFirst = true;
|
bool isFirst = true;
|
||||||
|
|
||||||
for (auto&& [current, total] : boundThreadsByNode)
|
for (auto&& [current, total] : boundThreadsByNode)
|
||||||
@@ -332,4 +351,19 @@ std::string Engine::thread_binding_information_as_string() const {
|
|||||||
return ss.str();
|
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
|
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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -39,15 +39,13 @@
|
|||||||
|
|
||||||
namespace Stockfish {
|
namespace Stockfish {
|
||||||
|
|
||||||
enum Square : int;
|
|
||||||
|
|
||||||
class Engine {
|
class Engine {
|
||||||
public:
|
public:
|
||||||
using InfoShort = Search::InfoShort;
|
using InfoShort = Search::InfoShort;
|
||||||
using InfoFull = Search::InfoFull;
|
using InfoFull = Search::InfoFull;
|
||||||
using InfoIter = Search::InfoIteration;
|
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
|
// Cannot be movable due to components holding backreferences to fields
|
||||||
Engine(const Engine&) = delete;
|
Engine(const Engine&) = delete;
|
||||||
@@ -81,6 +79,7 @@ class Engine {
|
|||||||
void set_on_update_full(std::function<void(const InfoFull&)>&&);
|
void set_on_update_full(std::function<void(const InfoFull&)>&&);
|
||||||
void set_on_iter(std::function<void(const InfoIter&)>&&);
|
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_bestmove(std::function<void(std::string_view, std::string_view)>&&);
|
||||||
|
void set_on_verify_networks(std::function<void(std::string_view)>&&);
|
||||||
|
|
||||||
// network related
|
// network related
|
||||||
|
|
||||||
@@ -97,12 +96,15 @@ class Engine {
|
|||||||
const OptionsMap& get_options() const;
|
const OptionsMap& get_options() const;
|
||||||
OptionsMap& get_options();
|
OptionsMap& get_options();
|
||||||
|
|
||||||
|
int get_hashfull(int maxAge = 0) const;
|
||||||
|
|
||||||
std::string fen() const;
|
std::string fen() const;
|
||||||
void flip();
|
void flip();
|
||||||
std::string visualize() const;
|
std::string visualize() const;
|
||||||
std::vector<std::pair<size_t, size_t>> get_bound_thread_count_by_numa_node() 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 get_numa_config_as_string() const;
|
||||||
std::string numa_config_information_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;
|
std::string thread_binding_information_as_string() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -112,14 +114,14 @@ class Engine {
|
|||||||
|
|
||||||
Position pos;
|
Position pos;
|
||||||
StateListPtr states;
|
StateListPtr states;
|
||||||
Square capSq;
|
|
||||||
|
|
||||||
OptionsMap options;
|
OptionsMap options;
|
||||||
ThreadPool threads;
|
ThreadPool threads;
|
||||||
TranspositionTable tt;
|
TranspositionTable tt;
|
||||||
LazyNumaReplicated<Eval::NNUE::Networks> networks;
|
LazyNumaReplicated<Eval::NNUE::Networks> networks;
|
||||||
|
|
||||||
Search::SearchManager::UpdateContext updateContext;
|
Search::SearchManager::UpdateContext updateContext;
|
||||||
|
std::function<void(std::string_view)> onVerifyNetworks;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Stockfish
|
} // namespace Stockfish
|
||||||
|
|||||||
+7
-12
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -59,16 +59,14 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks,
|
|||||||
|
|
||||||
assert(!pos.checkers());
|
assert(!pos.checkers());
|
||||||
|
|
||||||
bool smallNet = use_smallnet(pos);
|
bool smallNet = use_smallnet(pos);
|
||||||
int v;
|
|
||||||
|
|
||||||
auto [psqt, positional] = smallNet ? networks.small.evaluate(pos, &caches.small)
|
auto [psqt, positional] = smallNet ? networks.small.evaluate(pos, &caches.small)
|
||||||
: networks.big.evaluate(pos, &caches.big);
|
: networks.big.evaluate(pos, &caches.big);
|
||||||
|
|
||||||
Value nnue = (125 * psqt + 131 * positional) / 128;
|
Value nnue = (125 * psqt + 131 * positional) / 128;
|
||||||
|
|
||||||
// Re-evaluate the position when higher eval accuracy is worth the time spent
|
// 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);
|
std::tie(psqt, positional) = networks.big.evaluate(pos, &caches.big);
|
||||||
nnue = (125 * psqt + 131 * positional) / 128;
|
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
|
// Blend optimism and eval with nnue complexity
|
||||||
int nnueComplexity = std::abs(psqt - positional);
|
int nnueComplexity = std::abs(psqt - positional);
|
||||||
optimism += optimism * nnueComplexity / (smallNet ? 433 : 453);
|
optimism += optimism * nnueComplexity / 468;
|
||||||
nnue -= nnue * nnueComplexity / (smallNet ? 18815 : 17864);
|
nnue -= nnue * nnueComplexity / (smallNet ? 20233 : 17879);
|
||||||
|
|
||||||
int material = (smallNet ? 553 : 532) * pos.count<PAWN>() + pos.non_pawn_material();
|
int material = 535 * pos.count<PAWN>() + pos.non_pawn_material();
|
||||||
v = (nnue * (73921 + material) + optimism * (8112 + material)) / (smallNet ? 68104 : 74715);
|
int v = (nnue * (77777 + material) + optimism * (7777 + material)) / 77777;
|
||||||
|
|
||||||
// Evaluation grain (to get more alpha-beta cuts) with randomization (for robustness)
|
|
||||||
v = (v / 16) * 16 - 1 + (pos.key() & 0x2);
|
|
||||||
|
|
||||||
// Damp down the evaluation linearly when shuffling
|
// Damp down the evaluation linearly when shuffling
|
||||||
v -= v * pos.rule50_count() / 212;
|
v -= v * pos.rule50_count() / 212;
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -33,7 +33,7 @@ namespace Eval {
|
|||||||
// for the build process (profile-build and fishtest) to work. Do not change the
|
// for the build process (profile-build and fishtest) to work. Do not change the
|
||||||
// name of the macro or the location where this macro is defined, as it is used
|
// name of the macro or the location where this macro is defined, as it is used
|
||||||
// in the Makefile/Fishtest.
|
// in the Makefile/Fishtest.
|
||||||
#define EvalFileDefaultNameBig "nn-1111cefa1111.nnue"
|
#define EvalFileDefaultNameBig "nn-1c0000000000.nnue"
|
||||||
#define EvalFileDefaultNameSmall "nn-37f18f62d772.nnue"
|
#define EvalFileDefaultNameSmall "nn-37f18f62d772.nnue"
|
||||||
|
|
||||||
namespace 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
|
||||||
+166
-58
@@ -3,8 +3,8 @@
|
|||||||
* @author Dale Weiler
|
* @author Dale Weiler
|
||||||
* @brief Utility for including binary files
|
* @brief Utility for including binary files
|
||||||
*
|
*
|
||||||
* Facilities for including binary files into the current translation unit
|
* Facilities for including binary files into the current translation unit and
|
||||||
* and making use of them externally in other translation units.
|
* making use from them externally in other translation units.
|
||||||
*/
|
*/
|
||||||
#ifndef INCBIN_HDR
|
#ifndef INCBIN_HDR
|
||||||
#define INCBIN_HDR
|
#define INCBIN_HDR
|
||||||
@@ -26,7 +26,9 @@
|
|||||||
defined(__SSSE3__) || \
|
defined(__SSSE3__) || \
|
||||||
defined(__SSE4_1__) || \
|
defined(__SSE4_1__) || \
|
||||||
defined(__SSE4_2__) || \
|
defined(__SSE4_2__) || \
|
||||||
defined(__neon__)
|
defined(__neon__) || \
|
||||||
|
defined(__ARM_NEON) || \
|
||||||
|
defined(__ALTIVEC__)
|
||||||
# define INCBIN_ALIGNMENT_INDEX 4
|
# define INCBIN_ALIGNMENT_INDEX 4
|
||||||
#elif ULONG_MAX != 0xffffffffu
|
#elif ULONG_MAX != 0xffffffffu
|
||||||
# define INCBIN_ALIGNMENT_INDEX 3
|
# define INCBIN_ALIGNMENT_INDEX 3
|
||||||
@@ -64,6 +66,9 @@
|
|||||||
X
|
X
|
||||||
#define INCBIN_INVOKE(N, ...) \
|
#define INCBIN_INVOKE(N, ...) \
|
||||||
INCBIN_EVAL(N(__VA_ARGS__))
|
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 */
|
/* Green Hills uses a different directive for including binary data */
|
||||||
#if defined(__ghs__)
|
#if defined(__ghs__)
|
||||||
@@ -117,29 +122,50 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Optionally override the linker section into which data is emitted.
|
* @brief Optionally override the linker section into which size and data is
|
||||||
|
* emitted.
|
||||||
*
|
*
|
||||||
* @warning If you use this facility, you'll have to deal with platform-specific linker output
|
* @warning If you use this facility, you might have to deal with
|
||||||
* section naming on your own
|
* 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
|
|
||||||
*/
|
*/
|
||||||
#if !defined(INCBIN_OUTPUT_SECTION)
|
#if !defined(INCBIN_OUTPUT_SECTION)
|
||||||
# if defined(__APPLE__)
|
# if defined(__APPLE__)
|
||||||
# define INCBIN_OUTPUT_SECTION ".const_data"
|
# define INCBIN_OUTPUT_SECTION ".const_data"
|
||||||
# else
|
# else
|
||||||
# define INCBIN_OUTPUT_SECTION ".rodata"
|
# define INCBIN_OUTPUT_SECTION ".rodata"
|
||||||
# endif
|
# endif
|
||||||
#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__)
|
#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_SECTION INCBIN_OUTPUT_SECTION "\n"
|
||||||
# define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
|
# define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
|
||||||
# define INCBIN_INT ".long "
|
# define INCBIN_INT ".long "
|
||||||
@@ -179,27 +205,17 @@
|
|||||||
/**
|
/**
|
||||||
* @brief Specify the prefix to use for symbol names.
|
* @brief Specify the prefix to use for symbol names.
|
||||||
*
|
*
|
||||||
* By default this is `g', producing symbols of the form:
|
* @note By default this is "g".
|
||||||
* @code
|
|
||||||
* #include "incbin.h"
|
|
||||||
* INCBIN(Foo, "foo.txt");
|
|
||||||
*
|
*
|
||||||
* // 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
|
* @code
|
||||||
* #define INCBIN_PREFIX incbin
|
* #define INCBIN_PREFIX incbin
|
||||||
* #include "incbin.h"
|
* #include "incbin.h"
|
||||||
* INCBIN(Foo, "foo.txt");
|
* INCBIN(Foo, "foo.txt");
|
||||||
*
|
*
|
||||||
* // Now you have the following symbols instead:
|
* // Now you have the following symbols instead:
|
||||||
* // const unsigned char incbinFooData[];
|
* // const unsigned char incbinFoo<data>[];
|
||||||
* // const unsigned char *const incbinFooEnd;
|
* // const unsigned char *const incbinFoo<end>;
|
||||||
* // const unsigned int incbinFooSize;
|
* // const unsigned int incbinFoo<size>;
|
||||||
* @endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
#if !defined(INCBIN_PREFIX)
|
#if !defined(INCBIN_PREFIX)
|
||||||
@@ -213,18 +229,8 @@
|
|||||||
* - INCBIN_STYLE_CAMEL "CamelCase"
|
* - INCBIN_STYLE_CAMEL "CamelCase"
|
||||||
* - INCBIN_STYLE_SNAKE "snake_case"
|
* - INCBIN_STYLE_SNAKE "snake_case"
|
||||||
*
|
*
|
||||||
* Default option is *INCBIN_STYLE_CAMEL* producing symbols of the form:
|
* @note By default this is INCBIN_STYLE_CAMEL
|
||||||
* @code
|
|
||||||
* #include "incbin.h"
|
|
||||||
* INCBIN(Foo, "foo.txt");
|
|
||||||
*
|
*
|
||||||
* // 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
|
* @code
|
||||||
* #define INCBIN_STYLE INCBIN_STYLE_SNAKE
|
* #define INCBIN_STYLE INCBIN_STYLE_SNAKE
|
||||||
* #include "incbin.h"
|
* #include "incbin.h"
|
||||||
@@ -261,8 +267,8 @@
|
|||||||
INCBIN_STRINGIZE( \
|
INCBIN_STRINGIZE( \
|
||||||
INCBIN_STYLE_IDENT(TYPE)) \
|
INCBIN_STYLE_IDENT(TYPE)) \
|
||||||
|
|
||||||
/* Generate the global labels by indirectly invoking the macro
|
/* Generate the global labels by indirectly invoking the macro with our style
|
||||||
* with our style type and concatenate the name against them. */
|
* type and concatenating the name against them. */
|
||||||
#define INCBIN_GLOBAL_LABELS(NAME, TYPE) \
|
#define INCBIN_GLOBAL_LABELS(NAME, TYPE) \
|
||||||
INCBIN_INVOKE( \
|
INCBIN_INVOKE( \
|
||||||
INCBIN_GLOBAL, \
|
INCBIN_GLOBAL, \
|
||||||
@@ -288,23 +294,38 @@
|
|||||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
* 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.
|
* "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
|
* @param NAME The name given for the binary data
|
||||||
*
|
*
|
||||||
* @code
|
* @code
|
||||||
* INCBIN_EXTERN(Foo);
|
* INCBIN_EXTERN(Foo);
|
||||||
*
|
*
|
||||||
* // Now you have the following symbols:
|
* // Now you have the following symbols:
|
||||||
* // extern const unsigned char <prefix>FooData[];
|
* // extern const unsigned char <prefix>Foo<data>[];
|
||||||
* // extern const unsigned char *const <prefix>FooEnd;
|
* // extern const unsigned char *const <prefix>Foo<end>;
|
||||||
* // extern const unsigned int <prefix>FooSize;
|
* // 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
|
* @endcode
|
||||||
*/
|
*/
|
||||||
#define INCBIN_EXTERN(NAME) \
|
#define INCBIN_EXTERN(...) \
|
||||||
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char \
|
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_CONCATENATE(INCBIN_PREFIX, NAME), \
|
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||||
INCBIN_STYLE_IDENT(DATA))[]; \
|
INCBIN_STYLE_IDENT(DATA))[]; \
|
||||||
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char *const \
|
INCBIN_EXTERNAL const INCBIN_ALIGN TYPE *const \
|
||||||
INCBIN_CONCATENATE( \
|
INCBIN_CONCATENATE( \
|
||||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||||
INCBIN_STYLE_IDENT(END)); \
|
INCBIN_STYLE_IDENT(END)); \
|
||||||
@@ -313,6 +334,29 @@
|
|||||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||||
INCBIN_STYLE_IDENT(SIZE))
|
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.
|
* @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
|
* 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.
|
* "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 NAME The name to associate with this binary data (as an identifier.)
|
||||||
* @param FILENAME The file to include (as a string literal.)
|
* @param FILENAME The file to include (as a string literal.)
|
||||||
*
|
*
|
||||||
@@ -329,9 +374,20 @@
|
|||||||
* INCBIN(Icon, "icon.png");
|
* INCBIN(Icon, "icon.png");
|
||||||
*
|
*
|
||||||
* // Now you have the following symbols:
|
* // Now you have the following symbols:
|
||||||
* // const unsigned char <prefix>IconData[];
|
* // const unsigned char <prefix>Icon<data>[];
|
||||||
* // const unsigned char *const <prefix>IconEnd;
|
* // const unsigned char *const <prefix>Icon<end>;
|
||||||
* // const unsigned int <prefix>IconSize;
|
* // 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
|
* @endcode
|
||||||
*
|
*
|
||||||
* @warning This must be used in global scope
|
* @warning This must be used in global scope
|
||||||
@@ -341,15 +397,28 @@
|
|||||||
* please @see INCBIN_EXTERN.
|
* please @see INCBIN_EXTERN.
|
||||||
*/
|
*/
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#define INCBIN(NAME, FILENAME) \
|
# define INCBIN(NAME, FILENAME) \
|
||||||
INCBIN_EXTERN(NAME)
|
INCBIN_EXTERN(NAME)
|
||||||
#else
|
#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 \
|
__asm__(INCBIN_SECTION \
|
||||||
INCBIN_GLOBAL_LABELS(NAME, DATA) \
|
INCBIN_GLOBAL_LABELS(NAME, DATA) \
|
||||||
INCBIN_ALIGN_HOST \
|
INCBIN_ALIGN_HOST \
|
||||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) ":\n" \
|
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) ":\n" \
|
||||||
INCBIN_MACRO " \"" FILENAME "\"\n" \
|
INCBIN_MACRO " \"" FILENAME "\"\n" \
|
||||||
|
TERMINATOR \
|
||||||
INCBIN_GLOBAL_LABELS(NAME, END) \
|
INCBIN_GLOBAL_LABELS(NAME, END) \
|
||||||
INCBIN_ALIGN_BYTE \
|
INCBIN_ALIGN_BYTE \
|
||||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) ":\n" \
|
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) ":\n" \
|
||||||
@@ -362,7 +431,46 @@
|
|||||||
INCBIN_ALIGN_HOST \
|
INCBIN_ALIGN_HOST \
|
||||||
".text\n" \
|
".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
|
||||||
#endif
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+32
-1
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -212,6 +212,37 @@ void* aligned_large_pages_alloc(size_t allocSize) {
|
|||||||
|
|
||||||
#endif
|
#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
|
// aligned_large_pages_free() will free the previously memory allocated
|
||||||
// by aligned_large_pages_alloc(). The effect is a nop if mem == nullptr.
|
// 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
|
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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -38,6 +38,8 @@ void std_aligned_free(void* ptr);
|
|||||||
void* aligned_large_pages_alloc(size_t size);
|
void* aligned_large_pages_alloc(size_t size);
|
||||||
void aligned_large_pages_free(void* mem);
|
void aligned_large_pages_free(void* mem);
|
||||||
|
|
||||||
|
bool has_large_pages();
|
||||||
|
|
||||||
// Frees memory which was placed there with placement new.
|
// Frees memory which was placed there with placement new.
|
||||||
// Works for both single objects and arrays of unknown bound.
|
// Works for both single objects and arrays of unknown bound.
|
||||||
template<typename T, typename FREE_FUNC>
|
template<typename T, typename FREE_FUNC>
|
||||||
|
|||||||
+38
-30
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,7 +18,9 @@
|
|||||||
|
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cassert>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@@ -38,7 +40,7 @@ namespace Stockfish {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Version number or dev.
|
// 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
|
// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
|
||||||
// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
|
// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
|
||||||
@@ -122,7 +124,7 @@ class Logger {
|
|||||||
//
|
//
|
||||||
// For releases (non-dev builds) we only include the version number:
|
// For releases (non-dev builds) we only include the version number:
|
||||||
// Stockfish version
|
// Stockfish version
|
||||||
std::string engine_info(bool to_uci) {
|
std::string engine_version_info() {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Stockfish " << version << std::setfill('0');
|
ss << "Stockfish " << version << std::setfill('0');
|
||||||
|
|
||||||
@@ -151,11 +153,14 @@ std::string engine_info(bool to_uci) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ss << (to_uci ? "\nid author " : " by ") << "the Stockfish developers (see AUTHORS file)";
|
|
||||||
|
|
||||||
return ss.str();
|
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
|
// Returns a string trying to describe the compiler we use
|
||||||
std::string compiler_info() {
|
std::string compiler_info() {
|
||||||
@@ -284,7 +289,10 @@ template<size_t N>
|
|||||||
struct DebugInfo {
|
struct DebugInfo {
|
||||||
std::atomic<int64_t> data[N] = {0};
|
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> {
|
struct DebugExtremes: public DebugInfo<3> {
|
||||||
@@ -294,54 +302,54 @@ struct DebugExtremes: public DebugInfo<3> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
DebugInfo<2> hit[MaxDebugSlots];
|
std::array<DebugInfo<2>, MaxDebugSlots> hit;
|
||||||
DebugInfo<2> mean[MaxDebugSlots];
|
std::array<DebugInfo<2>, MaxDebugSlots> mean;
|
||||||
DebugInfo<3> stdev[MaxDebugSlots];
|
std::array<DebugInfo<3>, MaxDebugSlots> stdev;
|
||||||
DebugInfo<6> correl[MaxDebugSlots];
|
std::array<DebugInfo<6>, MaxDebugSlots> correl;
|
||||||
DebugExtremes extremes[MaxDebugSlots];
|
std::array<DebugExtremes, MaxDebugSlots> extremes;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void dbg_hit_on(bool cond, int slot) {
|
void dbg_hit_on(bool cond, int slot) {
|
||||||
|
|
||||||
++hit[slot][0];
|
++hit.at(slot)[0];
|
||||||
if (cond)
|
if (cond)
|
||||||
++hit[slot][1];
|
++hit.at(slot)[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbg_mean_of(int64_t value, int slot) {
|
void dbg_mean_of(int64_t value, int slot) {
|
||||||
|
|
||||||
++mean[slot][0];
|
++mean.at(slot)[0];
|
||||||
mean[slot][1] += value;
|
mean.at(slot)[1] += value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbg_stdev_of(int64_t value, int slot) {
|
void dbg_stdev_of(int64_t value, int slot) {
|
||||||
|
|
||||||
++stdev[slot][0];
|
++stdev.at(slot)[0];
|
||||||
stdev[slot][1] += value;
|
stdev.at(slot)[1] += value;
|
||||||
stdev[slot][2] += value * value;
|
stdev.at(slot)[2] += value * value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbg_extremes_of(int64_t value, int slot) {
|
void dbg_extremes_of(int64_t value, int slot) {
|
||||||
++extremes[slot][0];
|
++extremes.at(slot)[0];
|
||||||
|
|
||||||
int64_t current_max = extremes[slot][1].load();
|
int64_t current_max = extremes.at(slot)[1].load();
|
||||||
while (current_max < value && !extremes[slot][1].compare_exchange_weak(current_max, value))
|
while (current_max < value && !extremes.at(slot)[1].compare_exchange_weak(current_max, value))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
int64_t current_min = extremes[slot][2].load();
|
int64_t current_min = extremes.at(slot)[2].load();
|
||||||
while (current_min > value && !extremes[slot][2].compare_exchange_weak(current_min, value))
|
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) {
|
void dbg_correl_of(int64_t value1, int64_t value2, int slot) {
|
||||||
|
|
||||||
++correl[slot][0];
|
++correl.at(slot)[0];
|
||||||
correl[slot][1] += value1;
|
correl.at(slot)[1] += value1;
|
||||||
correl[slot][2] += value1 * value1;
|
correl.at(slot)[2] += value1 * value1;
|
||||||
correl[slot][3] += value2;
|
correl.at(slot)[3] += value2;
|
||||||
correl[slot][4] += value2 * value2;
|
correl.at(slot)[4] += value2 * value2;
|
||||||
correl[slot][5] += value1 * value2;
|
correl.at(slot)[5] += value1 * value2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbg_print() {
|
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());
|
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); });
|
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
|
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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
#define MISC_H_INCLUDED
|
#define MISC_H_INCLUDED
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define stringify2(x) #x
|
#define stringify2(x) #x
|
||||||
@@ -35,6 +37,7 @@
|
|||||||
|
|
||||||
namespace Stockfish {
|
namespace Stockfish {
|
||||||
|
|
||||||
|
std::string engine_version_info();
|
||||||
std::string engine_info(bool to_uci = false);
|
std::string engine_info(bool to_uci = false);
|
||||||
std::string compiler_info();
|
std::string compiler_info();
|
||||||
|
|
||||||
@@ -79,8 +82,8 @@ inline TimePoint now() {
|
|||||||
.count();
|
.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::vector<std::string> split(const std::string& s, const std::string& delimiter) {
|
inline std::vector<std::string_view> split(std::string_view s, std::string_view delimiter) {
|
||||||
std::vector<std::string> res;
|
std::vector<std::string_view> res;
|
||||||
|
|
||||||
if (s.empty())
|
if (s.empty())
|
||||||
return res;
|
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);
|
void remove_whitespace(std::string& s);
|
||||||
bool is_whitespace(const std::string& s);
|
bool is_whitespace(std::string_view s);
|
||||||
|
|
||||||
enum SyncCout {
|
enum SyncCout {
|
||||||
IO_LOCK,
|
IO_LOCK,
|
||||||
@@ -117,11 +120,8 @@ void sync_cout_start();
|
|||||||
void sync_cout_end();
|
void sync_cout_end();
|
||||||
|
|
||||||
// True if and only if the binary is compiled on a little-endian machine
|
// True if and only if the binary is compiled on a little-endian machine
|
||||||
static inline const union {
|
static inline const std::uint16_t Le = 1;
|
||||||
uint32_t i;
|
static inline const bool IsLittleEndian = *reinterpret_cast<const char*>(&Le) == 1;
|
||||||
char c[4];
|
|
||||||
} Le = {0x01020304};
|
|
||||||
static inline const bool IsLittleEndian = (Le.c[0] == 4);
|
|
||||||
|
|
||||||
|
|
||||||
template<typename T, std::size_t MaxSize>
|
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
|
// xorshift64star Pseudo-Random Number Generator
|
||||||
// This class is based on original code written and dedicated
|
// This class is based on original code written and dedicated
|
||||||
// to the public domain by Sebastiano Vigna (2014).
|
// to the public domain by Sebastiano Vigna (2014).
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+33
-31
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,11 +18,11 @@
|
|||||||
|
|
||||||
#include "movepick.h"
|
#include "movepick.h"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <utility>
|
#include <limits>
|
||||||
|
|
||||||
#include "bitboard.h"
|
#include "bitboard.h"
|
||||||
|
#include "misc.h"
|
||||||
#include "position.h"
|
#include "position.h"
|
||||||
|
|
||||||
namespace Stockfish {
|
namespace Stockfish {
|
||||||
@@ -82,16 +82,20 @@ MovePicker::MovePicker(const Position& p,
|
|||||||
Move ttm,
|
Move ttm,
|
||||||
Depth d,
|
Depth d,
|
||||||
const ButterflyHistory* mh,
|
const ButterflyHistory* mh,
|
||||||
|
const LowPlyHistory* lph,
|
||||||
const CapturePieceToHistory* cph,
|
const CapturePieceToHistory* cph,
|
||||||
const PieceToHistory** ch,
|
const PieceToHistory** ch,
|
||||||
const PawnHistory* ph) :
|
const PawnHistory* ph,
|
||||||
|
int pl) :
|
||||||
pos(p),
|
pos(p),
|
||||||
mainHistory(mh),
|
mainHistory(mh),
|
||||||
|
lowPlyHistory(lph),
|
||||||
captureHistory(cph),
|
captureHistory(cph),
|
||||||
continuationHistory(ch),
|
continuationHistory(ch),
|
||||||
pawnHistory(ph),
|
pawnHistory(ph),
|
||||||
ttMove(ttm),
|
ttMove(ttm),
|
||||||
depth(d) {
|
depth(d),
|
||||||
|
ply(pl) {
|
||||||
|
|
||||||
if (pos.checkers())
|
if (pos.checkers())
|
||||||
stage = EVASION_TT + !(ttm && pos.pseudo_legal(ttm));
|
stage = EVASION_TT + !(ttm && pos.pseudo_legal(ttm));
|
||||||
@@ -152,12 +156,13 @@ void MovePicker::score() {
|
|||||||
Square to = m.to_sq();
|
Square to = m.to_sq();
|
||||||
|
|
||||||
// histories
|
// 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 * (*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[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[3])[pc][to];
|
||||||
|
m.value += (*continuationHistory[4])[pc][to] / 3;
|
||||||
m.value += (*continuationHistory[5])[pc][to];
|
m.value += (*continuationHistory[5])[pc][to];
|
||||||
|
|
||||||
// bonus for checks
|
// bonus for checks
|
||||||
@@ -171,16 +176,18 @@ void MovePicker::score() {
|
|||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
// malus for putting piece en prise
|
// malus for putting piece en prise
|
||||||
m.value -= (pt == QUEEN ? bool(to & threatenedByRook) * 49000
|
m.value -= (pt == QUEEN ? bool(to & threatenedByRook) * 49000
|
||||||
: pt == ROOK ? bool(to & threatenedByMinor) * 24335
|
: pt == ROOK && bool(to & threatenedByMinor) ? 24335
|
||||||
: bool(to & threatenedByPawn) * 14900);
|
: 0);
|
||||||
|
|
||||||
|
if (ply < LOW_PLY_HISTORY_SIZE)
|
||||||
|
m.value += 8 * (*lowPlyHistory)[ply][m.from_to()] / (1 + 2 * ply);
|
||||||
}
|
}
|
||||||
|
|
||||||
else // Type == EVASIONS
|
else // Type == EVASIONS
|
||||||
{
|
{
|
||||||
if (pos.capture_stage(m))
|
if (pos.capture_stage(m))
|
||||||
m.value =
|
m.value = PieceValue[pos.piece_on(m.to_sq())] + (1 << 28);
|
||||||
PieceValue[pos.piece_on(m.to_sq())] - type_of(pos.moved_piece(m)) + (1 << 28);
|
|
||||||
else
|
else
|
||||||
m.value = (*mainHistory)[pos.side_to_move()][m.from_to()]
|
m.value = (*mainHistory)[pos.side_to_move()][m.from_to()]
|
||||||
+ (*continuationHistory[0])[pos.moved_piece(m)][m.to_sq()]
|
+ (*continuationHistory[0])[pos.moved_piece(m)][m.to_sq()]
|
||||||
@@ -190,26 +197,20 @@ void MovePicker::score() {
|
|||||||
|
|
||||||
// Returns the next move satisfying a predicate function.
|
// Returns the next move satisfying a predicate function.
|
||||||
// This never returns the TT move, as it was emitted before.
|
// 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) {
|
Move MovePicker::select(Pred filter) {
|
||||||
|
|
||||||
while (cur < endMoves)
|
for (; cur < endMoves; ++cur)
|
||||||
{
|
|
||||||
if constexpr (T == Best)
|
|
||||||
std::swap(*cur, *std::max_element(cur, endMoves));
|
|
||||||
|
|
||||||
if (*cur != ttMove && filter())
|
if (*cur != ttMove && filter())
|
||||||
return *cur++;
|
return *cur++;
|
||||||
|
|
||||||
cur++;
|
|
||||||
}
|
|
||||||
return Move::none();
|
return Move::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the most important method of the MovePicker class. We emit one
|
// 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,
|
// 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.
|
// 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; };
|
auto quiet_threshold = [](Depth d) { return -3560 * d; };
|
||||||
|
|
||||||
@@ -236,7 +237,7 @@ top:
|
|||||||
goto top;
|
goto top;
|
||||||
|
|
||||||
case GOOD_CAPTURE :
|
case GOOD_CAPTURE :
|
||||||
if (select<Next>([&]() {
|
if (select([&]() {
|
||||||
// Move losing capture to endBadCaptures to be tried later
|
// Move losing capture to endBadCaptures to be tried later
|
||||||
return pos.see_ge(*cur, -cur->value / 18) ? true
|
return pos.see_ge(*cur, -cur->value / 18) ? true
|
||||||
: (*endBadCaptures++ = *cur, false);
|
: (*endBadCaptures++ = *cur, false);
|
||||||
@@ -260,7 +261,7 @@ top:
|
|||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
|
|
||||||
case GOOD_QUIET :
|
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))
|
if ((cur - 1)->value > -7998 || (cur - 1)->value <= quiet_threshold(depth))
|
||||||
return *(cur - 1);
|
return *(cur - 1);
|
||||||
@@ -277,7 +278,7 @@ top:
|
|||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
|
|
||||||
case BAD_CAPTURE :
|
case BAD_CAPTURE :
|
||||||
if (select<Next>([]() { return true; }))
|
if (select([]() { return true; }))
|
||||||
return *(cur - 1);
|
return *(cur - 1);
|
||||||
|
|
||||||
// Prepare the pointers to loop over the bad quiets
|
// Prepare the pointers to loop over the bad quiets
|
||||||
@@ -289,7 +290,7 @@ top:
|
|||||||
|
|
||||||
case BAD_QUIET :
|
case BAD_QUIET :
|
||||||
if (!skipQuiets)
|
if (!skipQuiets)
|
||||||
return select<Next>([]() { return true; });
|
return select([]() { return true; });
|
||||||
|
|
||||||
return Move::none();
|
return Move::none();
|
||||||
|
|
||||||
@@ -298,21 +299,22 @@ top:
|
|||||||
endMoves = generate<EVASIONS>(pos, cur);
|
endMoves = generate<EVASIONS>(pos, cur);
|
||||||
|
|
||||||
score<EVASIONS>();
|
score<EVASIONS>();
|
||||||
|
partial_insertion_sort(cur, endMoves, std::numeric_limits<int>::min());
|
||||||
++stage;
|
++stage;
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
|
|
||||||
case EVASION :
|
case EVASION :
|
||||||
return select<Best>([]() { return true; });
|
case QCAPTURE :
|
||||||
|
return select([]() { return true; });
|
||||||
|
|
||||||
case PROBCUT :
|
case PROBCUT :
|
||||||
return select<Next>([&]() { return pos.see_ge(*cur, threshold); });
|
return select([&]() { return pos.see_ge(*cur, threshold); });
|
||||||
|
|
||||||
case QCAPTURE :
|
|
||||||
return select<Next>([]() { return true; });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(false);
|
assert(false);
|
||||||
return Move::none(); // Silence warning
|
return Move::none(); // Silence warning
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MovePicker::skip_quiet_moves() { skipQuiets = true; }
|
||||||
|
|
||||||
} // namespace Stockfish
|
} // namespace Stockfish
|
||||||
|
|||||||
+12
-121
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,123 +19,13 @@
|
|||||||
#ifndef MOVEPICK_H_INCLUDED
|
#ifndef MOVEPICK_H_INCLUDED
|
||||||
#define MOVEPICK_H_INCLUDED
|
#define MOVEPICK_H_INCLUDED
|
||||||
|
|
||||||
#include <algorithm>
|
#include "history.h"
|
||||||
#include <array>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <limits>
|
|
||||||
#include <type_traits> // IWYU pragma: keep
|
|
||||||
|
|
||||||
#include "movegen.h"
|
#include "movegen.h"
|
||||||
#include "position.h"
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
namespace Stockfish {
|
namespace Stockfish {
|
||||||
|
|
||||||
constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2
|
class Position;
|
||||||
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>;
|
|
||||||
|
|
||||||
// The MovePicker class is used to pick one pseudo-legal move at a time from the
|
// 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
|
// current position. The most important method is next_move(), which emits one
|
||||||
@@ -145,11 +35,6 @@ using CorrectionHistory =
|
|||||||
// a cut-off first.
|
// a cut-off first.
|
||||||
class MovePicker {
|
class MovePicker {
|
||||||
|
|
||||||
enum PickType {
|
|
||||||
Next,
|
|
||||||
Best
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MovePicker(const MovePicker&) = delete;
|
MovePicker(const MovePicker&) = delete;
|
||||||
MovePicker& operator=(const MovePicker&) = delete;
|
MovePicker& operator=(const MovePicker&) = delete;
|
||||||
@@ -157,14 +42,17 @@ class MovePicker {
|
|||||||
Move,
|
Move,
|
||||||
Depth,
|
Depth,
|
||||||
const ButterflyHistory*,
|
const ButterflyHistory*,
|
||||||
|
const LowPlyHistory*,
|
||||||
const CapturePieceToHistory*,
|
const CapturePieceToHistory*,
|
||||||
const PieceToHistory**,
|
const PieceToHistory**,
|
||||||
const PawnHistory*);
|
const PawnHistory*,
|
||||||
|
int);
|
||||||
MovePicker(const Position&, Move, int, const CapturePieceToHistory*);
|
MovePicker(const Position&, Move, int, const CapturePieceToHistory*);
|
||||||
Move next_move(bool skipQuiets = false);
|
Move next_move();
|
||||||
|
void skip_quiet_moves();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<PickType T, typename Pred>
|
template<typename Pred>
|
||||||
Move select(Pred);
|
Move select(Pred);
|
||||||
template<GenType>
|
template<GenType>
|
||||||
void score();
|
void score();
|
||||||
@@ -173,6 +61,7 @@ class MovePicker {
|
|||||||
|
|
||||||
const Position& pos;
|
const Position& pos;
|
||||||
const ButterflyHistory* mainHistory;
|
const ButterflyHistory* mainHistory;
|
||||||
|
const LowPlyHistory* lowPlyHistory;
|
||||||
const CapturePieceToHistory* captureHistory;
|
const CapturePieceToHistory* captureHistory;
|
||||||
const PieceToHistory** continuationHistory;
|
const PieceToHistory** continuationHistory;
|
||||||
const PawnHistory* pawnHistory;
|
const PawnHistory* pawnHistory;
|
||||||
@@ -181,6 +70,8 @@ class MovePicker {
|
|||||||
int stage;
|
int stage;
|
||||||
int threshold;
|
int threshold;
|
||||||
Depth depth;
|
Depth depth;
|
||||||
|
int ply;
|
||||||
|
bool skipQuiets = false;
|
||||||
ExtMove moves[MAX_MOVES];
|
ExtMove moves[MAX_MOVES];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -38,17 +38,41 @@
|
|||||||
namespace Stockfish::Eval::NNUE::Layers {
|
namespace Stockfish::Eval::NNUE::Layers {
|
||||||
|
|
||||||
#if (USE_SSSE3 | (USE_NEON >= 8))
|
#if (USE_SSSE3 | (USE_NEON >= 8))
|
||||||
alignas(CacheLineSize) static inline const
|
static constexpr int lsb_index64[64] = {
|
||||||
std::array<std::array<std::uint16_t, 8>, 256> lookup_indices = []() {
|
0, 47, 1, 56, 48, 27, 2, 60, 57, 49, 41, 37, 28, 16, 3, 61, 54, 58, 35, 52, 50, 42,
|
||||||
std::array<std::array<std::uint16_t, 8>, 256> v{};
|
21, 44, 38, 32, 29, 23, 17, 11, 4, 62, 46, 55, 26, 59, 40, 36, 15, 53, 34, 51, 20, 43,
|
||||||
for (unsigned i = 0; i < 256; ++i)
|
31, 22, 10, 45, 25, 39, 14, 33, 19, 30, 9, 24, 13, 18, 8, 12, 7, 6, 5, 63};
|
||||||
{
|
|
||||||
std::uint64_t j = i, k = 0;
|
constexpr int constexpr_lsb(uint64_t bb) {
|
||||||
while (j)
|
assert(bb != 0);
|
||||||
v[i][k++] = pop_lsb(j);
|
constexpr uint64_t debruijn64 = 0x03F79D71B4CB0A89ULL;
|
||||||
}
|
return lsb_index64[((bb ^ (bb - 1)) * debruijn64) >> 58];
|
||||||
return v;
|
}
|
||||||
}();
|
|
||||||
|
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
|
// Find indices of nonzero numbers in an int32_t array
|
||||||
template<const IndexType InputDimensions>
|
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;
|
using vec128_t = __m128i;
|
||||||
#define vec128_zero _mm_setzero_si128()
|
#define vec128_zero _mm_setzero_si128()
|
||||||
#define vec128_set_16(a) _mm_set1_epi16(a)
|
#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_storeu(a, b) _mm_storeu_si128(a, b)
|
||||||
#define vec128_add(a, b) _mm_add_epi16(a, b)
|
#define vec128_add(a, b) _mm_add_epi16(a, b)
|
||||||
#elif defined(USE_NEON)
|
#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)
|
for (IndexType j = 0; j < OutputsPerChunk; ++j)
|
||||||
{
|
{
|
||||||
const auto lookup = (nnz >> (j * 8)) & 0xFF;
|
const unsigned lookup = (nnz >> (j * 8)) & 0xFF;
|
||||||
const auto offsets =
|
const vec128_t offsets =
|
||||||
vec128_load(reinterpret_cast<const vec128_t*>(&lookup_indices[lookup]));
|
vec128_load(reinterpret_cast<const vec128_t*>(&Lookup.offset_indices[lookup]));
|
||||||
vec128_storeu(reinterpret_cast<vec128_t*>(out + count), vec128_add(base, offsets));
|
vec128_storeu(reinterpret_cast<vec128_t*>(out + count), vec128_add(base, offsets));
|
||||||
count += popcount(lookup);
|
count += popcount(lookup);
|
||||||
base = vec128_add(base, increment);
|
base = vec128_add(base, increment);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+36
-24
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -26,8 +26,10 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "../evaluate.h"
|
#define INCBIN_SILENCE_BITCODE_WARNING
|
||||||
#include "../incbin/incbin.h"
|
#include "../incbin/incbin.h"
|
||||||
|
|
||||||
|
#include "../evaluate.h"
|
||||||
#include "../memory.h"
|
#include "../memory.h"
|
||||||
#include "../misc.h"
|
#include "../misc.h"
|
||||||
#include "../position.h"
|
#include "../position.h"
|
||||||
@@ -36,7 +38,6 @@
|
|||||||
#include "nnue_common.h"
|
#include "nnue_common.h"
|
||||||
#include "nnue_misc.h"
|
#include "nnue_misc.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
// Macro to embed the default efficiently updatable neural network (NNUE) file
|
// Macro to embed the default efficiently updatable neural network (NNUE) file
|
||||||
// data in the engine binary (using incbin.h, by Dale Weiler).
|
// data in the engine binary (using incbin.h, by Dale Weiler).
|
||||||
// This macro invocation will declare the following three variables
|
// 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;
|
const unsigned int gEmbeddedNNUESmallSize = 1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
struct EmbeddedNNUE {
|
struct EmbeddedNNUE {
|
||||||
EmbeddedNNUE(const unsigned char* embeddedData,
|
EmbeddedNNUE(const unsigned char* embeddedData,
|
||||||
const unsigned char* embeddedEnd,
|
const unsigned char* embeddedEnd,
|
||||||
@@ -98,7 +101,7 @@ bool read_parameters(std::istream& stream, T& reference) {
|
|||||||
|
|
||||||
// Write evaluation function parameters
|
// Write evaluation function parameters
|
||||||
template<typename T>
|
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());
|
write_little_endian<std::uint32_t>(stream, T::get_hash_value());
|
||||||
return reference.write_parameters(stream);
|
return reference.write_parameters(stream);
|
||||||
@@ -234,35 +237,44 @@ Network<Arch, Transformer>::evaluate(const Position& pos
|
|||||||
|
|
||||||
|
|
||||||
template<typename Arch, typename Transformer>
|
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())
|
if (evalfilePath.empty())
|
||||||
evalfilePath = evalFile.defaultName;
|
evalfilePath = evalFile.defaultName;
|
||||||
|
|
||||||
if (evalFile.current != evalfilePath)
|
if (evalFile.current != evalfilePath)
|
||||||
{
|
{
|
||||||
std::string msg1 =
|
if (f)
|
||||||
"Network evaluation parameters compatible with the engine must be available.";
|
{
|
||||||
std::string msg2 = "The network file " + evalfilePath + " was not loaded successfully.";
|
std::string msg1 =
|
||||||
std::string msg3 = "The UCI option EvalFile might need to specify the full path, "
|
"Network evaluation parameters compatible with the engine must be available.";
|
||||||
"including the directory name, to the network file.";
|
std::string msg2 = "The network file " + evalfilePath + " was not loaded successfully.";
|
||||||
std::string msg4 = "The default net can be downloaded from: "
|
std::string msg3 = "The UCI option EvalFile might need to specify the full path, "
|
||||||
"https://tests.stockfishchess.org/api/nn/"
|
"including the directory name, to the network file.";
|
||||||
+ evalFile.defaultName;
|
std::string msg4 = "The default net can be downloaded from: "
|
||||||
std::string msg5 = "The engine will be terminated now.";
|
"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);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size = sizeof(*featureTransformer) + sizeof(Arch) * LayerStacks;
|
if (f)
|
||||||
sync_cout << "info string NNUE evaluation using " << evalfilePath << " ("
|
{
|
||||||
<< size / (1024 * 1024) << "MiB, (" << featureTransformer->InputDimensions << ", "
|
size_t size = sizeof(*featureTransformer) + sizeof(Arch) * LayerStacks;
|
||||||
<< network[0].TransformedFeatureDimensions << ", " << network[0].FC_0_OUTPUTS << ", "
|
f("info string NNUE evaluation using " + evalfilePath + " ("
|
||||||
<< network[0].FC_1_OUTPUTS << ", 1))" << sync_endl;
|
+ 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
|
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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,9 +20,11 @@
|
|||||||
#define NETWORK_H_INCLUDED
|
#define NETWORK_H_INCLUDED
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@@ -68,7 +70,7 @@ class Network {
|
|||||||
void hint_common_access(const Position& pos,
|
void hint_common_access(const Position& pos,
|
||||||
AccumulatorCaches::Cache<FTDimensions>* cache) const;
|
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,
|
NnueEvalTrace trace_evaluate(const Position& pos,
|
||||||
AccumulatorCaches::Cache<FTDimensions>* cache) const;
|
AccumulatorCaches::Cache<FTDimensions>* cache) const;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -80,11 +80,6 @@ struct AccumulatorCaches {
|
|||||||
entry.clear(network.featureTransformer->biases);
|
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<Entry, COLOR_NB>& operator[](Square sq) { return entries[sq]; }
|
||||||
|
|
||||||
std::array<std::array<Entry, COLOR_NB>, SQUARE_NB> entries;
|
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
|
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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+292
-381
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "../position.h"
|
#include "../position.h"
|
||||||
@@ -145,11 +146,51 @@ using psqt_vec_t = int32x4_t;
|
|||||||
|
|
||||||
#endif
|
#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
|
#ifdef VECTOR
|
||||||
|
|
||||||
// Compute optimal SIMD register count for feature transformer accumulation.
|
|
||||||
|
|
||||||
// We use __m* types as template arguments, which causes GCC to emit warnings
|
// 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
|
// about losing some attribute information. This is irrelevant to us as we
|
||||||
// only take their size, so the following pragma are harmless.
|
// 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"
|
#pragma GCC diagnostic ignored "-Wignored-attributes"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<typename SIMDRegisterType, typename LaneType, int NumLanes, int MaxRegisters>
|
template<typename SIMDRegisterType, typename LaneType, int NumLanes, int MaxRegisters>
|
||||||
static constexpr int BestRegisterCount() {
|
static constexpr int BestRegisterCount() {
|
||||||
#define RegisterSize sizeof(SIMDRegisterType)
|
constexpr std::size_t RegisterSize = sizeof(SIMDRegisterType);
|
||||||
#define LaneSize sizeof(LaneType)
|
constexpr std::size_t LaneSize = sizeof(LaneType);
|
||||||
|
|
||||||
static_assert(RegisterSize >= LaneSize);
|
static_assert(RegisterSize >= LaneSize);
|
||||||
static_assert(MaxRegisters <= NumRegistersSIMD);
|
static_assert(MaxRegisters <= NumRegistersSIMD);
|
||||||
static_assert(MaxRegisters > 0);
|
static_assert(MaxRegisters > 0);
|
||||||
static_assert(NumRegistersSIMD > 0);
|
static_assert(NumRegistersSIMD > 0);
|
||||||
static_assert(RegisterSize % LaneSize == 0);
|
static_assert(RegisterSize % LaneSize == 0);
|
||||||
static_assert((NumLanes * LaneSize) % RegisterSize == 0);
|
static_assert((NumLanes * LaneSize) % RegisterSize == 0);
|
||||||
|
|
||||||
const int ideal = (NumLanes * LaneSize) / RegisterSize;
|
const int ideal = (NumLanes * LaneSize) / RegisterSize;
|
||||||
if (ideal <= MaxRegisters)
|
if (ideal <= MaxRegisters)
|
||||||
return ideal;
|
return ideal;
|
||||||
|
|
||||||
// Look for the largest divisor of the ideal register count that is smaller than MaxRegisters
|
// Look for the largest divisor of the ideal register count that is smaller than MaxRegisters
|
||||||
for (int divisor = MaxRegisters; divisor > 1; --divisor)
|
for (int divisor = MaxRegisters; divisor > 1; --divisor)
|
||||||
if (ideal % divisor == 0)
|
if (ideal % divisor == 0)
|
||||||
return divisor;
|
return divisor;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__)
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#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
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Input feature converter
|
// Input feature converter
|
||||||
@@ -196,17 +251,7 @@ class FeatureTransformer {
|
|||||||
static constexpr IndexType HalfDimensions = TransformedFeatureDimensions;
|
static constexpr IndexType HalfDimensions = TransformedFeatureDimensions;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#ifdef VECTOR
|
using Tiling = SIMDTiling<TransformedFeatureDimensions, HalfDimensions>;
|
||||||
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
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Output type
|
// Output type
|
||||||
@@ -219,76 +264,54 @@ class FeatureTransformer {
|
|||||||
// Size of forward propagation buffer
|
// Size of forward propagation buffer
|
||||||
static constexpr std::size_t BufferSize = OutputDimensions * sizeof(OutputType);
|
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
|
// Hash value embedded in the evaluation file
|
||||||
static constexpr std::uint32_t get_hash_value() {
|
static constexpr std::uint32_t get_hash_value() {
|
||||||
return FeatureSet::HashValue ^ (OutputDimensions * 2);
|
return FeatureSet::HashValue ^ (OutputDimensions * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr void order_packs([[maybe_unused]] uint64_t* v) {
|
void permute_weights() {
|
||||||
#if defined(USE_AVX512) // _mm512_packs_epi16 ordering
|
permute<16>(biases, PackusEpi16Order);
|
||||||
uint64_t tmp0 = v[2], tmp1 = v[3];
|
permute<16>(weights, PackusEpi16Order);
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr void inverse_order_packs([[maybe_unused]] uint64_t* v) {
|
void unpermute_weights() {
|
||||||
#if defined(USE_AVX512) // Inverse _mm512_packs_epi16 ordering
|
permute<16>(biases, InversePackusEpi16Order);
|
||||||
uint64_t tmp0 = v[2], tmp1 = v[3];
|
permute<16>(weights, InversePackusEpi16Order);
|
||||||
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 permute_weights([[maybe_unused]] void (*order_fn)(uint64_t*)) const {
|
inline void scale_weights(bool read) {
|
||||||
#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]);
|
|
||||||
|
|
||||||
for (IndexType j = 0; j < InputDimensions; ++j)
|
for (IndexType j = 0; j < InputDimensions; ++j)
|
||||||
{
|
{
|
||||||
uint64_t* w =
|
WeightType* w = &weights[j * HalfDimensions];
|
||||||
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]);
|
|
||||||
for (IndexType i = 0; i < HalfDimensions; ++i)
|
for (IndexType i = 0; i < HalfDimensions; ++i)
|
||||||
w[i] = read ? w[i] * 2 : w[i] / 2;
|
w[i] = read ? w[i] * 2 : w[i] / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
BiasType* b = const_cast<BiasType*>(biases);
|
|
||||||
for (IndexType i = 0; i < HalfDimensions; ++i)
|
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
|
// Read network parameters
|
||||||
@@ -298,22 +321,22 @@ class FeatureTransformer {
|
|||||||
read_leb_128<WeightType>(stream, weights, HalfDimensions * InputDimensions);
|
read_leb_128<WeightType>(stream, weights, HalfDimensions * InputDimensions);
|
||||||
read_leb_128<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
|
read_leb_128<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
|
||||||
|
|
||||||
permute_weights(inverse_order_packs);
|
permute_weights();
|
||||||
scale_weights(true);
|
scale_weights(true);
|
||||||
return !stream.fail();
|
return !stream.fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write network parameters
|
// 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);
|
scale_weights(false);
|
||||||
|
|
||||||
write_leb_128<BiasType>(stream, biases, HalfDimensions);
|
write_leb_128<BiasType>(stream, biases, HalfDimensions);
|
||||||
write_leb_128<WeightType>(stream, weights, HalfDimensions * InputDimensions);
|
write_leb_128<WeightType>(stream, weights, HalfDimensions * InputDimensions);
|
||||||
write_leb_128<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
|
write_leb_128<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
|
||||||
|
|
||||||
permute_weights(inverse_order_packs);
|
permute_weights();
|
||||||
scale_weights(true);
|
scale_weights(true);
|
||||||
return !stream.fail();
|
return !stream.fail();
|
||||||
}
|
}
|
||||||
@@ -447,17 +470,16 @@ class FeatureTransformer {
|
|||||||
|
|
||||||
void hint_common_access(const Position& pos,
|
void hint_common_access(const Position& pos,
|
||||||
AccumulatorCaches::Cache<HalfDimensions>* cache) const {
|
AccumulatorCaches::Cache<HalfDimensions>* cache) const {
|
||||||
hint_common_access_for_perspective<WHITE>(pos, cache);
|
update_accumulator<WHITE>(pos, cache);
|
||||||
hint_common_access_for_perspective<BLACK>(pos, cache);
|
update_accumulator<BLACK>(pos, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<Color Perspective>
|
template<Color Perspective>
|
||||||
[[nodiscard]] std::pair<StateInfo*, StateInfo*>
|
StateInfo* try_find_computed_accumulator(const Position& pos) const {
|
||||||
try_find_computed_accumulator(const Position& pos) const {
|
|
||||||
// Look for a usable accumulator of an earlier position. We keep track
|
// Look for a usable accumulator of an earlier position. We keep track
|
||||||
// of the estimated gain in terms of features to be added/subtracted.
|
// of the estimated gain in terms of features to be added/subtracted.
|
||||||
StateInfo *st = pos.state(), *next = nullptr;
|
StateInfo* st = pos.state();
|
||||||
int gain = FeatureSet::refresh_cost(pos);
|
int gain = FeatureSet::refresh_cost(pos);
|
||||||
while (st->previous && !(st->*accPtr).computed[Perspective])
|
while (st->previous && !(st->*accPtr).computed[Perspective])
|
||||||
{
|
{
|
||||||
@@ -466,239 +488,170 @@ class FeatureTransformer {
|
|||||||
if (FeatureSet::requires_refresh(st, Perspective)
|
if (FeatureSet::requires_refresh(st, Perspective)
|
||||||
|| (gain -= FeatureSet::update_cost(st) + 1) < 0)
|
|| (gain -= FeatureSet::update_cost(st) + 1) < 0)
|
||||||
break;
|
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.
|
// Given a computed accumulator, computes the accumulator of the next position.
|
||||||
// All states must be sequential, that is states_to_update[i] must
|
template<Color Perspective>
|
||||||
// either be reachable by repeatedly applying ->previous from
|
void update_accumulator_incremental(const Position& pos, StateInfo* computed) const {
|
||||||
// states_to_update[i+1], and computed_st must be reachable by
|
assert((computed->*accPtr).computed[Perspective]);
|
||||||
// repeatedly applying ->previous on states_to_update[0].
|
assert(computed->next != nullptr);
|
||||||
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;
|
|
||||||
}());
|
|
||||||
|
|
||||||
#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);
|
const Square ksq = pos.square<KING>(Perspective);
|
||||||
|
|
||||||
// The size must be enough to contain the largest possible update.
|
// The size must be enough to contain the largest possible update.
|
||||||
// That might depend on the feature set and generally relies on the
|
// That might depend on the feature set and generally relies on the
|
||||||
// feature set's update cost calculation to be correct and never allow
|
// feature set's update cost calculation to be correct and never allow
|
||||||
// updates with more added/removed features than MaxActiveDimensions.
|
// 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;
|
std::memcpy((next->*accPtr).accumulation[Perspective],
|
||||||
|
(computed->*accPtr).accumulation[Perspective],
|
||||||
const StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1];
|
HalfDimensions * sizeof(BiasType));
|
||||||
|
std::memcpy((next->*accPtr).psqtAccumulation[Perspective],
|
||||||
for (StateInfo* st2 = states_to_update[i]; st2 != end_state; st2 = st2->previous)
|
(computed->*accPtr).psqtAccumulation[Perspective],
|
||||||
FeatureSet::append_changed_indices<Perspective>(ksq, st2->dirtyPiece, removed[i],
|
PSQTBuckets * sizeof(PSQTWeightType));
|
||||||
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]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
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
|
for (IndexType i = 0; i < HalfDimensions * sizeof(WeightType) / sizeof(vec_t); ++i)
|
||||||
auto accTileIn = reinterpret_cast<const vec_t*>(
|
accOut[i] = vec_add_16(vec_sub_16(accIn[i], columnR0[i]), columnA0[i]);
|
||||||
&(st->*accPtr).accumulation[Perspective][j * TileHeight]);
|
}
|
||||||
for (IndexType k = 0; k < NumRegs; ++k)
|
else if (added.size() == 1)
|
||||||
acc[k] = vec_load(&accTileIn[k]);
|
{
|
||||||
|
const IndexType offsetR1 = HalfDimensions * removed[1];
|
||||||
|
auto* columnR1 = reinterpret_cast<const vec_t*>(&weights[offsetR1]);
|
||||||
|
|
||||||
for (IndexType i = 0; i < N; ++i)
|
for (IndexType i = 0; i < HalfDimensions * sizeof(WeightType) / sizeof(vec_t); ++i)
|
||||||
{
|
accOut[i] = vec_sub_16(vec_add_16(accIn[i], columnA0[i]),
|
||||||
// Difference calculation for the deactivated features
|
vec_add_16(columnR0[i], columnR1[i]));
|
||||||
for (const auto index : removed[i])
|
}
|
||||||
{
|
else
|
||||||
const IndexType offset = HalfDimensions * index + j * TileHeight;
|
{
|
||||||
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
|
const IndexType offsetA1 = HalfDimensions * added[1];
|
||||||
for (IndexType k = 0; k < NumRegs; ++k)
|
auto* columnA1 = reinterpret_cast<const vec_t*>(&weights[offsetA1]);
|
||||||
acc[k] = vec_sub_16(acc[k], column[k]);
|
const IndexType offsetR1 = HalfDimensions * removed[1];
|
||||||
}
|
auto* columnR1 = reinterpret_cast<const vec_t*>(&weights[offsetR1]);
|
||||||
|
|
||||||
// Difference calculation for the activated features
|
for (IndexType i = 0; i < HalfDimensions * sizeof(WeightType) / sizeof(vec_t); ++i)
|
||||||
for (const auto index : added[i])
|
accOut[i] =
|
||||||
{
|
vec_add_16(accIn[i], vec_sub_16(vec_add_16(columnA0[i], columnA1[i]),
|
||||||
const IndexType offset = HalfDimensions * index + j * TileHeight;
|
vec_add_16(columnR0[i], columnR1[i])));
|
||||||
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 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
|
for (std::size_t i = 0;
|
||||||
auto accTilePsqtIn = reinterpret_cast<const psqt_vec_t*>(
|
i < PSQTBuckets * sizeof(PSQTWeightType) / sizeof(psqt_vec_t); ++i)
|
||||||
&(st->*accPtr).psqtAccumulation[Perspective][j * PsqtTileHeight]);
|
accPsqtOut[i] = vec_add_psqt_32(vec_sub_psqt_32(accPsqtIn[i], columnPsqtR0[i]),
|
||||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
columnPsqtA0[i]);
|
||||||
psqt[k] = vec_load_psqt(&accTilePsqtIn[k]);
|
}
|
||||||
|
else if (added.size() == 1)
|
||||||
for (IndexType i = 0; i < N; ++i)
|
{
|
||||||
{
|
const IndexType offsetPsqtR1 = PSQTBuckets * removed[1];
|
||||||
// Difference calculation for the deactivated features
|
auto* columnPsqtR1 =
|
||||||
for (const auto index : removed[i])
|
reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtR1]);
|
||||||
{
|
|
||||||
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
|
for (std::size_t i = 0;
|
||||||
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
i < PSQTBuckets * sizeof(PSQTWeightType) / sizeof(psqt_vec_t); ++i)
|
||||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
accPsqtOut[i] =
|
||||||
psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]);
|
vec_sub_psqt_32(vec_add_psqt_32(accPsqtIn[i], columnPsqtA0[i]),
|
||||||
}
|
vec_add_psqt_32(columnPsqtR0[i], columnPsqtR1[i]));
|
||||||
|
}
|
||||||
// Difference calculation for the activated features
|
else
|
||||||
for (const auto index : added[i])
|
{
|
||||||
{
|
const IndexType offsetPsqtA1 = PSQTBuckets * added[1];
|
||||||
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
|
auto* columnPsqtA1 =
|
||||||
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtA1]);
|
||||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
const IndexType offsetPsqtR1 = PSQTBuckets * removed[1];
|
||||||
psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
|
auto* columnPsqtR1 =
|
||||||
}
|
reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offsetPsqtR1]);
|
||||||
|
|
||||||
// Store accumulator
|
for (std::size_t i = 0;
|
||||||
auto accTilePsqtOut = reinterpret_cast<psqt_vec_t*>(
|
i < PSQTBuckets * sizeof(PSQTWeightType) / sizeof(psqt_vec_t); ++i)
|
||||||
&(states_to_update[i]->*accPtr)
|
accPsqtOut[i] = vec_add_psqt_32(
|
||||||
.psqtAccumulation[Perspective][j * PsqtTileHeight]);
|
accPsqtIn[i],
|
||||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
vec_sub_psqt_32(vec_add_psqt_32(columnPsqtA0[i], columnPsqtA1[i]),
|
||||||
vec_store_psqt(&accTilePsqtOut[k], psqt[k]);
|
vec_add_psqt_32(columnPsqtR0[i], columnPsqtR1[i])));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
for (IndexType i = 0; i < N; ++i)
|
std::memcpy((next->*accPtr).accumulation[Perspective],
|
||||||
{
|
(computed->*accPtr).accumulation[Perspective],
|
||||||
std::memcpy((states_to_update[i]->*accPtr).accumulation[Perspective],
|
HalfDimensions * sizeof(BiasType));
|
||||||
(st->*accPtr).accumulation[Perspective], HalfDimensions * sizeof(BiasType));
|
std::memcpy((next->*accPtr).psqtAccumulation[Perspective],
|
||||||
|
(computed->*accPtr).psqtAccumulation[Perspective],
|
||||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
PSQTBuckets * sizeof(PSQTWeightType));
|
||||||
(states_to_update[i]->*accPtr).psqtAccumulation[Perspective][k] =
|
|
||||||
(st->*accPtr).psqtAccumulation[Perspective][k];
|
|
||||||
|
|
||||||
st = states_to_update[i];
|
|
||||||
|
|
||||||
// Difference calculation for the deactivated features
|
// Difference calculation for the deactivated features
|
||||||
for (const auto index : removed[i])
|
for (const auto index : removed)
|
||||||
{
|
{
|
||||||
const IndexType offset = HalfDimensions * index;
|
const IndexType offset = HalfDimensions * index;
|
||||||
for (IndexType j = 0; j < HalfDimensions; ++j)
|
for (IndexType i = 0; i < HalfDimensions; ++i)
|
||||||
(st->*accPtr).accumulation[Perspective][j] -= weights[offset + j];
|
(next->*accPtr).accumulation[Perspective][i] -= weights[offset + i];
|
||||||
|
|
||||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
for (std::size_t i = 0; i < PSQTBuckets; ++i)
|
||||||
(st->*accPtr).psqtAccumulation[Perspective][k] -=
|
(next->*accPtr).psqtAccumulation[Perspective][i] -=
|
||||||
psqtWeights[index * PSQTBuckets + k];
|
psqtWeights[index * PSQTBuckets + i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Difference calculation for the activated features
|
// Difference calculation for the activated features
|
||||||
for (const auto index : added[i])
|
for (const auto index : added)
|
||||||
{
|
{
|
||||||
const IndexType offset = HalfDimensions * index;
|
const IndexType offset = HalfDimensions * index;
|
||||||
for (IndexType j = 0; j < HalfDimensions; ++j)
|
for (IndexType i = 0; i < HalfDimensions; ++i)
|
||||||
(st->*accPtr).accumulation[Perspective][j] += weights[offset + j];
|
(next->*accPtr).accumulation[Perspective][i] += weights[offset + i];
|
||||||
|
|
||||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
for (std::size_t i = 0; i < PSQTBuckets; ++i)
|
||||||
(st->*accPtr).psqtAccumulation[Perspective][k] +=
|
(next->*accPtr).psqtAccumulation[Perspective][i] +=
|
||||||
psqtWeights[index * PSQTBuckets + k];
|
psqtWeights[index * PSQTBuckets + i];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
(next->*accPtr).computed[Perspective] = true;
|
||||||
|
|
||||||
|
if (next != pos.state())
|
||||||
|
update_accumulator_incremental<Perspective>(pos, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<Color Perspective>
|
template<Color Perspective>
|
||||||
void update_accumulator_refresh_cache(const Position& pos,
|
void update_accumulator_refresh_cache(const Position& pos,
|
||||||
AccumulatorCaches::Cache<HalfDimensions>* cache) const {
|
AccumulatorCaches::Cache<HalfDimensions>* cache) const {
|
||||||
@@ -735,88 +688,88 @@ class FeatureTransformer {
|
|||||||
accumulator.computed[Perspective] = true;
|
accumulator.computed[Perspective] = true;
|
||||||
|
|
||||||
#ifdef VECTOR
|
#ifdef VECTOR
|
||||||
vec_t acc[NumRegs];
|
vec_t acc[Tiling::NumRegs];
|
||||||
psqt_vec_t psqt[NumPsqtRegs];
|
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 =
|
auto* accTile = reinterpret_cast<vec_t*>(
|
||||||
reinterpret_cast<vec_t*>(&accumulator.accumulation[Perspective][j * TileHeight]);
|
&accumulator.accumulation[Perspective][j * Tiling::TileHeight]);
|
||||||
auto entryTile = reinterpret_cast<vec_t*>(&entry.accumulation[j * 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];
|
acc[k] = entryTile[k];
|
||||||
|
|
||||||
int i = 0;
|
std::size_t i = 0;
|
||||||
for (; i < int(std::min(removed.size(), added.size())); ++i)
|
for (; i < std::min(removed.size(), added.size()); ++i)
|
||||||
{
|
{
|
||||||
IndexType indexR = removed[i];
|
IndexType indexR = removed[i];
|
||||||
const IndexType offsetR = HalfDimensions * indexR + j * TileHeight;
|
const IndexType offsetR = HalfDimensions * indexR + j * Tiling::TileHeight;
|
||||||
auto columnR = reinterpret_cast<const vec_t*>(&weights[offsetR]);
|
auto* columnR = reinterpret_cast<const vec_t*>(&weights[offsetR]);
|
||||||
IndexType indexA = added[i];
|
IndexType indexA = added[i];
|
||||||
const IndexType offsetA = HalfDimensions * indexA + j * TileHeight;
|
const IndexType offsetA = HalfDimensions * indexA + j * Tiling::TileHeight;
|
||||||
auto columnA = reinterpret_cast<const vec_t*>(&weights[offsetA]);
|
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]));
|
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];
|
IndexType index = removed[i];
|
||||||
const IndexType offset = HalfDimensions * index + j * TileHeight;
|
const IndexType offset = HalfDimensions * index + j * Tiling::TileHeight;
|
||||||
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
|
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]);
|
acc[k] = vec_sub_16(acc[k], column[k]);
|
||||||
}
|
}
|
||||||
for (; i < int(added.size()); ++i)
|
for (; i < added.size(); ++i)
|
||||||
{
|
{
|
||||||
IndexType index = added[i];
|
IndexType index = added[i];
|
||||||
const IndexType offset = HalfDimensions * index + j * TileHeight;
|
const IndexType offset = HalfDimensions * index + j * Tiling::TileHeight;
|
||||||
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
|
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]);
|
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]);
|
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]);
|
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*>(
|
auto* accTilePsqt = reinterpret_cast<psqt_vec_t*>(
|
||||||
&accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]);
|
&accumulator.psqtAccumulation[Perspective][j * Tiling::PsqtTileHeight]);
|
||||||
auto entryTilePsqt =
|
auto* entryTilePsqt =
|
||||||
reinterpret_cast<psqt_vec_t*>(&entry.psqtAccumulation[j * PsqtTileHeight]);
|
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];
|
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];
|
IndexType index = removed[i];
|
||||||
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
|
const IndexType offset = PSQTBuckets * index + j * Tiling::PsqtTileHeight;
|
||||||
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
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]);
|
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];
|
IndexType index = added[i];
|
||||||
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
|
const IndexType offset = PSQTBuckets * index + j * Tiling::PsqtTileHeight;
|
||||||
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
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]);
|
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]);
|
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]);
|
vec_store_psqt(&accTilePsqt[k], psqt[k]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -858,60 +811,18 @@ class FeatureTransformer {
|
|||||||
entry.byTypeBB[pt] = pos.pieces(pt);
|
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>
|
template<Color Perspective>
|
||||||
void update_accumulator(const Position& pos,
|
void update_accumulator(const Position& pos,
|
||||||
AccumulatorCaches::Cache<HalfDimensions>* cache) const {
|
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->*accPtr).computed[Perspective] && oldest != pos.state())
|
||||||
|
// Start from the oldest computed accumulator, update all the
|
||||||
if ((oldest_st->*accPtr).computed[Perspective])
|
// accumulators up to the current position.
|
||||||
{
|
update_accumulator_incremental<Perspective>(pos, oldest);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
update_accumulator_refresh_cache<Perspective>(pos, cache);
|
update_accumulator_refresh_cache<Perspective>(pos, cache);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -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] = '+';
|
board[y][x] = board[y][x + 8] = board[y + 3][x + 8] = board[y + 3][x] = '+';
|
||||||
if (pc != NO_PIECE)
|
if (pc != NO_PIECE)
|
||||||
board[y + 1][x + 4] = PieceToChar[pc];
|
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);
|
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
|
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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+7
-6
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -653,7 +654,7 @@ class NumaConfig {
|
|||||||
NumaIndex n = 0;
|
NumaIndex n = 0;
|
||||||
for (auto&& nodeStr : split(s, ":"))
|
for (auto&& nodeStr : split(s, ":"))
|
||||||
{
|
{
|
||||||
auto indices = indices_from_shortened_string(nodeStr);
|
auto indices = indices_from_shortened_string(std::string(nodeStr));
|
||||||
if (!indices.empty())
|
if (!indices.empty())
|
||||||
{
|
{
|
||||||
for (auto idx : indices)
|
for (auto idx : indices)
|
||||||
@@ -1015,7 +1016,7 @@ class NumaConfig {
|
|||||||
if (s.empty())
|
if (s.empty())
|
||||||
return indices;
|
return indices;
|
||||||
|
|
||||||
for (const std::string& ss : split(s, ","))
|
for (const auto& ss : split(s, ","))
|
||||||
{
|
{
|
||||||
if (ss.empty())
|
if (ss.empty())
|
||||||
continue;
|
continue;
|
||||||
@@ -1023,13 +1024,13 @@ class NumaConfig {
|
|||||||
auto parts = split(ss, "-");
|
auto parts = split(ss, "-");
|
||||||
if (parts.size() == 1)
|
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);
|
indices.emplace_back(c);
|
||||||
}
|
}
|
||||||
else if (parts.size() == 2)
|
else if (parts.size() == 2)
|
||||||
{
|
{
|
||||||
const CpuIndex cfirst = CpuIndex{str_to_size_t(parts[0])};
|
const CpuIndex cfirst = CpuIndex{str_to_size_t(std::string(parts[0]))};
|
||||||
const CpuIndex clast = CpuIndex{str_to_size_t(parts[1])};
|
const CpuIndex clast = CpuIndex{str_to_size_t(std::string(parts[1]))};
|
||||||
for (size_t c = cfirst; c <= clast; ++c)
|
for (size_t c = cfirst; c <= clast; ++c)
|
||||||
{
|
{
|
||||||
indices.emplace_back(c);
|
indices.emplace_back(c);
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+89
-56
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -119,6 +119,9 @@ void Position::init() {
|
|||||||
for (Piece pc : Pieces)
|
for (Piece pc : Pieces)
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||||
Zobrist::psq[pc][s] = rng.rand<Key>();
|
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)
|
for (File f = FILE_A; f <= FILE_H; ++f)
|
||||||
Zobrist::enpassant[f] = rng.rand<Key>();
|
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
|
// The function is only used when a new position is set up
|
||||||
void Position::set_state() const {
|
void Position::set_state() const {
|
||||||
|
|
||||||
st->key = st->materialKey = 0;
|
st->key = st->materialKey = 0;
|
||||||
st->pawnKey = Zobrist::noPawns;
|
st->minorPieceKey = 0;
|
||||||
|
st->nonPawnKey[WHITE] = st->nonPawnKey[BLACK] = 0;
|
||||||
|
st->pawnKey = Zobrist::noPawns;
|
||||||
st->nonPawnMaterial[WHITE] = st->nonPawnMaterial[BLACK] = VALUE_ZERO;
|
st->nonPawnMaterial[WHITE] = st->nonPawnMaterial[BLACK] = VALUE_ZERO;
|
||||||
st->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);
|
st->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);
|
||||||
|
|
||||||
@@ -350,8 +355,18 @@ void Position::set_state() const {
|
|||||||
if (type_of(pc) == PAWN)
|
if (type_of(pc) == PAWN)
|
||||||
st->pawnKey ^= Zobrist::psq[pc][s];
|
st->pawnKey ^= Zobrist::psq[pc][s];
|
||||||
|
|
||||||
else if (type_of(pc) != KING)
|
else
|
||||||
st->nonPawnMaterial[color_of(pc)] += PieceValue[pc];
|
{
|
||||||
|
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)
|
if (st->epSquare != SQ_NONE)
|
||||||
@@ -364,7 +379,7 @@ void Position::set_state() const {
|
|||||||
|
|
||||||
for (Piece pc : Pieces)
|
for (Piece pc : Pieces)
|
||||||
for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
|
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.
|
// Slider attacks use the occupied bitboard to indicate occupancy.
|
||||||
Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
|
Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
|
||||||
|
|
||||||
return (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN))
|
return (attacks_bb<ROOK>(s, occupied) & pieces(ROOK, QUEEN))
|
||||||
| (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN))
|
|
||||||
| (attacks_bb<KNIGHT>(s) & pieces(KNIGHT))
|
|
||||||
| (attacks_bb<ROOK>(s, occupied) & pieces(ROOK, QUEEN))
|
|
||||||
| (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, 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
|
// Tests whether a pseudo-legal move is legal
|
||||||
bool Position::legal(Move m) const {
|
bool Position::legal(Move m) const {
|
||||||
@@ -521,7 +545,7 @@ bool Position::legal(Move m) const {
|
|||||||
Direction step = to > from ? WEST : EAST;
|
Direction step = to > from ? WEST : EAST;
|
||||||
|
|
||||||
for (Square s = to; s != from; s += step)
|
for (Square s = to; s != from; s += step)
|
||||||
if (attackers_to(s) & pieces(~us))
|
if (attackers_to_exist(s, pieces(), ~us))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// In case of Chess960, verify if the Rook blocks some checks.
|
// 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
|
// If the moving piece is a king, check whether the destination square is
|
||||||
// attacked by the opponent.
|
// attacked by the opponent.
|
||||||
if (type_of(piece_on(from)) == KING)
|
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
|
// 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.
|
// 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
|
// 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.
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -659,7 +683,12 @@ bool Position::gives_check(Move m) const {
|
|||||||
// Makes a move, and saves all information necessary
|
// Makes a move, and saves all information necessary
|
||||||
// to a StateInfo object. The move is assumed to be legal. Pseudo-legal
|
// to a StateInfo object. The move is assumed to be legal. Pseudo-legal
|
||||||
// moves should be filtered out before this function is called.
|
// 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(m.is_ok());
|
||||||
assert(&newSt != st);
|
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.
|
// our state pointer to point to the new (ready to be updated) state.
|
||||||
std::memcpy(&newSt, st, offsetof(StateInfo, key));
|
std::memcpy(&newSt, st, offsetof(StateInfo, key));
|
||||||
newSt.previous = st;
|
newSt.previous = st;
|
||||||
|
st->next = &newSt;
|
||||||
st = &newSt;
|
st = &newSt;
|
||||||
|
|
||||||
// Increment ply counters. In particular, rule50 will be reset to zero later on
|
// 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);
|
do_castling<true>(us, from, to, rfrom, rto);
|
||||||
|
|
||||||
k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
|
k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
|
||||||
|
st->nonPawnKey[us] ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
|
||||||
captured = NO_PIECE;
|
captured = NO_PIECE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -731,7 +762,13 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
|||||||
st->pawnKey ^= Zobrist::psq[captured][capsq];
|
st->pawnKey ^= Zobrist::psq[captured][capsq];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
st->nonPawnMaterial[them] -= PieceValue[captured];
|
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.dirty_num = 2; // 1 piece moved, 1 piece captured
|
||||||
dp.piece[1] = captured;
|
dp.piece[1] = captured;
|
||||||
@@ -742,7 +779,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
|||||||
remove_piece(capsq);
|
remove_piece(capsq);
|
||||||
|
|
||||||
k ^= Zobrist::psq[captured][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
|
// Reset rule 50 counter
|
||||||
st->rule50 = 0;
|
st->rule50 = 0;
|
||||||
@@ -789,7 +826,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
|||||||
|
|
||||||
else if (m.type_of() == PROMOTION)
|
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(relative_rank(us, to) == RANK_8);
|
||||||
assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
|
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++;
|
dp.dirty_num++;
|
||||||
|
|
||||||
// Update hash keys
|
// Update hash keys
|
||||||
k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to];
|
// Zobrist::psq[pc][to] is zero, so we don't need to clear it
|
||||||
st->pawnKey ^= Zobrist::psq[pc][to];
|
k ^= Zobrist::psq[promotion][to];
|
||||||
st->materialKey ^=
|
st->materialKey ^= Zobrist::psq[promotion][8 + pieceCount[promotion] - 1]
|
||||||
Zobrist::psq[promotion][pieceCount[promotion] - 1] ^ Zobrist::psq[pc][pieceCount[pc]];
|
^ Zobrist::psq[pc][8 + pieceCount[pc]];
|
||||||
|
|
||||||
|
if (promotionType <= BISHOP)
|
||||||
|
st->minorPieceKey ^= Zobrist::psq[promotion][to];
|
||||||
|
|
||||||
// Update material
|
// Update material
|
||||||
st->nonPawnMaterial[us] += PieceValue[promotion];
|
st->nonPawnMaterial[us] += PieceValue[promotion];
|
||||||
@@ -821,11 +862,21 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
|||||||
st->rule50 = 0;
|
st->rule50 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set capture piece
|
else
|
||||||
st->capturedPiece = captured;
|
{
|
||||||
|
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
|
// Update the key with the final value
|
||||||
st->key = k;
|
st->key = k;
|
||||||
|
if (tt)
|
||||||
|
prefetch(tt->first_entry(key()));
|
||||||
|
|
||||||
|
// Set capture piece
|
||||||
|
st->capturedPiece = captured;
|
||||||
|
|
||||||
// Calculate checkers bitboard (if move gives check)
|
// Calculate checkers bitboard (if move gives check)
|
||||||
st->checkersBB = givesCheck ? attackers_to(square<KING>(them)) & pieces(us) : 0;
|
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
|
// Used to do a "null move": it flips
|
||||||
// the side to move without executing any move on the board.
|
// 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(!checkers());
|
||||||
assert(&newSt != st);
|
assert(&newSt != st);
|
||||||
@@ -963,13 +1014,9 @@ void Position::do_null_move(StateInfo& newSt, TranspositionTable& tt) {
|
|||||||
std::memcpy(&newSt, st, offsetof(StateInfo, accumulatorBig));
|
std::memcpy(&newSt, st, offsetof(StateInfo, accumulatorBig));
|
||||||
|
|
||||||
newSt.previous = st;
|
newSt.previous = st;
|
||||||
|
st->next = &newSt;
|
||||||
st = &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)
|
if (st->epSquare != SQ_NONE)
|
||||||
{
|
{
|
||||||
st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
|
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->key ^= Zobrist::side;
|
||||||
++st->rule50;
|
|
||||||
prefetch(tt.first_entry(key()));
|
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;
|
st->pliesFromNull = 0;
|
||||||
|
|
||||||
sideToMove = ~sideToMove;
|
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)
|
// Tests if the SEE (Static Exchange Evaluation)
|
||||||
// value of move is greater or equal to the given threshold. We'll use an
|
// 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.
|
// 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)))
|
else if ((bb = stmAttackers & pieces(QUEEN)))
|
||||||
{
|
{
|
||||||
if ((swap = QueenValue - swap) < res)
|
swap = QueenValue - swap;
|
||||||
break;
|
// 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);
|
occupied ^= least_significant_square_bb(bb);
|
||||||
|
|
||||||
attackers |= (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN))
|
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()))
|
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Return a draw score if a position repeats once earlier but strictly
|
return is_repetition(ply);
|
||||||
// after the root, or repeats twice before or at the root.
|
|
||||||
return st->repetition && st->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
|
// Tests whether there has been at least one repetition
|
||||||
// of positions since the last capture or pawn move.
|
// of positions since the last capture or pawn move.
|
||||||
@@ -1253,7 +1286,7 @@ bool Position::pos_is_ok() const {
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (pieceCount[W_KING] != 1 || pieceCount[B_KING] != 1
|
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");
|
assert(0 && "pos_is_ok: Kings");
|
||||||
|
|
||||||
if ((pieces(PAWN) & (Rank1BB | Rank8BB)) || pieceCount[W_PAWN] > 8 || pieceCount[B_PAWN] > 8)
|
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
|
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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -43,6 +43,8 @@ struct StateInfo {
|
|||||||
// Copied when making a move
|
// Copied when making a move
|
||||||
Key materialKey;
|
Key materialKey;
|
||||||
Key pawnKey;
|
Key pawnKey;
|
||||||
|
Key minorPieceKey;
|
||||||
|
Key nonPawnKey[COLOR_NB];
|
||||||
Value nonPawnMaterial[COLOR_NB];
|
Value nonPawnMaterial[COLOR_NB];
|
||||||
int castlingRights;
|
int castlingRights;
|
||||||
int rule50;
|
int rule50;
|
||||||
@@ -53,6 +55,7 @@ struct StateInfo {
|
|||||||
Key key;
|
Key key;
|
||||||
Bitboard checkersBB;
|
Bitboard checkersBB;
|
||||||
StateInfo* previous;
|
StateInfo* previous;
|
||||||
|
StateInfo* next;
|
||||||
Bitboard blockersForKing[COLOR_NB];
|
Bitboard blockersForKing[COLOR_NB];
|
||||||
Bitboard pinners[COLOR_NB];
|
Bitboard pinners[COLOR_NB];
|
||||||
Bitboard checkSquares[PIECE_TYPE_NB];
|
Bitboard checkSquares[PIECE_TYPE_NB];
|
||||||
@@ -122,6 +125,7 @@ class Position {
|
|||||||
// Attacks to/from a given square
|
// Attacks to/from a given square
|
||||||
Bitboard attackers_to(Square s) const;
|
Bitboard attackers_to(Square s) const;
|
||||||
Bitboard attackers_to(Square s, Bitboard occupied) const;
|
Bitboard attackers_to(Square s, Bitboard occupied) const;
|
||||||
|
bool attackers_to_exist(Square s, Bitboard occupied, Color c) const;
|
||||||
void update_slider_blockers(Color c) const;
|
void update_slider_blockers(Color c) const;
|
||||||
template<PieceType Pt>
|
template<PieceType Pt>
|
||||||
Bitboard attacks_by(Color c) const;
|
Bitboard attacks_by(Color c) const;
|
||||||
@@ -136,10 +140,10 @@ class Position {
|
|||||||
Piece captured_piece() const;
|
Piece captured_piece() const;
|
||||||
|
|
||||||
// Doing and undoing moves
|
// Doing and undoing moves
|
||||||
void do_move(Move m, StateInfo& newSt);
|
void do_move(Move m, StateInfo& newSt, const TranspositionTable* tt);
|
||||||
void do_move(Move m, StateInfo& newSt, bool givesCheck);
|
void do_move(Move m, StateInfo& newSt, bool givesCheck, const TranspositionTable* tt);
|
||||||
void undo_move(Move m);
|
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();
|
void undo_null_move();
|
||||||
|
|
||||||
// Static Exchange Evaluation
|
// Static Exchange Evaluation
|
||||||
@@ -147,15 +151,17 @@ class Position {
|
|||||||
|
|
||||||
// Accessing hash keys
|
// Accessing hash keys
|
||||||
Key key() const;
|
Key key() const;
|
||||||
Key key_after(Move m) const;
|
|
||||||
Key material_key() const;
|
Key material_key() const;
|
||||||
Key pawn_key() const;
|
Key pawn_key() const;
|
||||||
|
Key minor_piece_key() const;
|
||||||
|
Key non_pawn_key(Color c) const;
|
||||||
|
|
||||||
// Other properties of the position
|
// Other properties of the position
|
||||||
Color side_to_move() const;
|
Color side_to_move() const;
|
||||||
int game_ply() const;
|
int game_ply() const;
|
||||||
bool is_chess960() const;
|
bool is_chess960() const;
|
||||||
bool is_draw(int ply) const;
|
bool is_draw(int ply) const;
|
||||||
|
bool is_repetition(int ply) const;
|
||||||
bool upcoming_repetition(int ply) const;
|
bool upcoming_repetition(int ply) const;
|
||||||
bool has_repeated() const;
|
bool has_repeated() const;
|
||||||
int rule50_count() 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::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(Color c) const { return st->nonPawnMaterial[c]; }
|
||||||
|
|
||||||
inline Value Position::non_pawn_material() const {
|
inline Value Position::non_pawn_material() const {
|
||||||
@@ -355,7 +365,9 @@ inline void Position::move_piece(Square from, Square to) {
|
|||||||
board[to] = pc;
|
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; }
|
inline StateInfo* Position::state() const { return st; }
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -29,7 +29,7 @@ namespace Stockfish {
|
|||||||
Score::Score(Value v, const Position& pos) {
|
Score::Score(Value v, const Position& pos) {
|
||||||
assert(-VALUE_INFINITE < v && v < VALUE_INFINITE);
|
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)};
|
score = InternalUnits{UCIEngine::to_cp(v, pos)};
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+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
|
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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -31,8 +31,8 @@
|
|||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "history.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "movepick.h"
|
|
||||||
#include "nnue/network.h"
|
#include "nnue/network.h"
|
||||||
#include "nnue/nnue_accumulator.h"
|
#include "nnue/nnue_accumulator.h"
|
||||||
#include "numa.h"
|
#include "numa.h"
|
||||||
@@ -61,18 +61,21 @@ namespace Search {
|
|||||||
// shallower and deeper in the tree during the search. Each search thread has
|
// shallower and deeper in the tree during the search. Each search thread has
|
||||||
// its own array of Stack objects, indexed by the current ply.
|
// its own array of Stack objects, indexed by the current ply.
|
||||||
struct Stack {
|
struct Stack {
|
||||||
Move* pv;
|
Move* pv;
|
||||||
PieceToHistory* continuationHistory;
|
PieceToHistory* continuationHistory;
|
||||||
int ply;
|
CorrectionHistory<PieceTo>* continuationCorrectionHistory;
|
||||||
Move currentMove;
|
int ply;
|
||||||
Move excludedMove;
|
Move currentMove;
|
||||||
Value staticEval;
|
Move excludedMove;
|
||||||
int statScore;
|
Value staticEval;
|
||||||
int moveCount;
|
int statScore;
|
||||||
bool inCheck;
|
int moveCount;
|
||||||
bool ttPv;
|
bool inCheck;
|
||||||
bool ttHit;
|
bool ttPv;
|
||||||
int cutoffCnt;
|
bool ttHit;
|
||||||
|
int cutoffCnt;
|
||||||
|
int reduction;
|
||||||
|
bool isTTMove;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -90,15 +93,16 @@ struct RootMove {
|
|||||||
return m.score != score ? m.score < score : m.previousScore < previousScore;
|
return m.score != score ? m.score < score : m.previousScore < previousScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t effort = 0;
|
uint64_t effort = 0;
|
||||||
Value score = -VALUE_INFINITE;
|
Value score = -VALUE_INFINITE;
|
||||||
Value previousScore = -VALUE_INFINITE;
|
Value previousScore = -VALUE_INFINITE;
|
||||||
Value averageScore = -VALUE_INFINITE;
|
Value averageScore = -VALUE_INFINITE;
|
||||||
Value uciScore = -VALUE_INFINITE;
|
Value meanSquaredScore = -VALUE_INFINITE * VALUE_INFINITE;
|
||||||
bool scoreLowerbound = false;
|
Value uciScore = -VALUE_INFINITE;
|
||||||
bool scoreUpperbound = false;
|
bool scoreLowerbound = false;
|
||||||
int selDepth = 0;
|
bool scoreUpperbound = false;
|
||||||
int tbRank = 0;
|
int selDepth = 0;
|
||||||
|
int tbRank = 0;
|
||||||
Value tbScore;
|
Value tbScore;
|
||||||
std::vector<Move> pv;
|
std::vector<Move> pv;
|
||||||
};
|
};
|
||||||
@@ -124,7 +128,6 @@ struct LimitsType {
|
|||||||
int movestogo, depth, mate, perft, infinite;
|
int movestogo, depth, mate, perft, infinite;
|
||||||
uint64_t nodes;
|
uint64_t nodes;
|
||||||
bool ponderMode;
|
bool ponderMode;
|
||||||
Square capSq;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -277,11 +280,17 @@ class Worker {
|
|||||||
void ensure_network_replicated();
|
void ensure_network_replicated();
|
||||||
|
|
||||||
// Public because they need to be updatable by the stats
|
// Public because they need to be updatable by the stats
|
||||||
ButterflyHistory mainHistory;
|
ButterflyHistory mainHistory;
|
||||||
|
LowPlyHistory lowPlyHistory;
|
||||||
|
|
||||||
CapturePieceToHistory captureHistory;
|
CapturePieceToHistory captureHistory;
|
||||||
ContinuationHistory continuationHistory[2][2];
|
ContinuationHistory continuationHistory[2][2];
|
||||||
PawnHistory pawnHistory;
|
PawnHistory pawnHistory;
|
||||||
CorrectionHistory correctionHistory;
|
|
||||||
|
CorrectionHistory<Pawn> pawnCorrectionHistory;
|
||||||
|
CorrectionHistory<Minor> minorPieceCorrectionHistory;
|
||||||
|
CorrectionHistory<NonPawn> nonPawnCorrectionHistory[COLOR_NB];
|
||||||
|
CorrectionHistory<Continuation> continuationCorrectionHistory;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void iterative_deepening();
|
void iterative_deepening();
|
||||||
@@ -305,6 +314,8 @@ class Worker {
|
|||||||
TimePoint elapsed() const;
|
TimePoint elapsed() const;
|
||||||
TimePoint elapsed_time() const;
|
TimePoint elapsed_time() const;
|
||||||
|
|
||||||
|
Value evaluate(const Position&);
|
||||||
|
|
||||||
LimitsType limits;
|
LimitsType limits;
|
||||||
|
|
||||||
size_t pvIdx, pvLast;
|
size_t pvIdx, pvLast;
|
||||||
@@ -342,6 +353,11 @@ class Worker {
|
|||||||
friend class SearchManager;
|
friend class SearchManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ConthistBonus {
|
||||||
|
int index;
|
||||||
|
int weight;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace Search
|
} // namespace Search
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -1620,7 +1620,7 @@ bool Tablebases::root_probe(Position& pos,
|
|||||||
WDLScore wdl = -probe_wdl(pos, &result);
|
WDLScore wdl = -probe_wdl(pos, &result);
|
||||||
dtz = dtz_before_zeroing(wdl);
|
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,
|
// 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,
|
// 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
|
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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+6
-6
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -329,13 +329,13 @@ Thread* ThreadPool::get_best_thread() const {
|
|||||||
const auto bestThreadMoveVote = votes[bestThreadPV[0]];
|
const auto bestThreadMoveVote = votes[bestThreadPV[0]];
|
||||||
const auto newThreadMoveVote = votes[newThreadPV[0]];
|
const auto newThreadMoveVote = votes[newThreadPV[0]];
|
||||||
|
|
||||||
const bool bestThreadInProvenWin = bestThreadScore >= VALUE_TB_WIN_IN_MAX_PLY;
|
const bool bestThreadInProvenWin = is_win(bestThreadScore);
|
||||||
const bool newThreadInProvenWin = newThreadScore >= VALUE_TB_WIN_IN_MAX_PLY;
|
const bool newThreadInProvenWin = is_win(newThreadScore);
|
||||||
|
|
||||||
const bool bestThreadInProvenLoss =
|
const bool bestThreadInProvenLoss =
|
||||||
bestThreadScore != -VALUE_INFINITE && bestThreadScore <= VALUE_TB_LOSS_IN_MAX_PLY;
|
bestThreadScore != -VALUE_INFINITE && is_loss(bestThreadScore);
|
||||||
const bool newThreadInProvenLoss =
|
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
|
// We make sure not to pick a thread with truncated principal variation
|
||||||
const bool betterVotingValue =
|
const bool betterVotingValue =
|
||||||
@@ -355,7 +355,7 @@ Thread* ThreadPool::get_best_thread() const {
|
|||||||
bestThread = th.get();
|
bestThread = th.get();
|
||||||
}
|
}
|
||||||
else if (newThreadInProvenWin || newThreadInProvenLoss
|
else if (newThreadInProvenWin || newThreadInProvenLoss
|
||||||
|| (newThreadScore > VALUE_TB_LOSS_IN_MAX_PLY
|
|| (!is_loss(newThreadScore)
|
||||||
&& (newThreadMoveVote > bestThreadMoveVote
|
&& (newThreadMoveVote > bestThreadMoveVote
|
||||||
|| (newThreadMoveVote == bestThreadMoveVote && betterVotingValue))))
|
|| (newThreadMoveVote == bestThreadMoveVote && betterVotingValue))))
|
||||||
bestThread = th.get();
|
bestThread = th.get();
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+19
-16
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -87,18 +87,20 @@ void TimeManagement::init(Search::LimitsType& limits,
|
|||||||
const TimePoint scaledTime = limits.time[us] / scaleFactor;
|
const TimePoint scaledTime = limits.time[us] / scaleFactor;
|
||||||
const TimePoint scaledInc = limits.inc[us] / scaleFactor;
|
const TimePoint scaledInc = limits.inc[us] / scaleFactor;
|
||||||
|
|
||||||
// Maximum move horizon of 50 moves
|
// Maximum move horizon
|
||||||
int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50;
|
int centiMTG = limits.movestogo ? std::min(limits.movestogo * 100, 5000) : 5051;
|
||||||
|
|
||||||
// If less than one second, gradually reduce mtg
|
// 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
|
// 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)
|
TimePoint timeLeft =
|
||||||
- moveOverhead * (2 + mtg));
|
std::max(TimePoint(1),
|
||||||
|
limits.time[us]
|
||||||
|
+ (limits.inc[us] * (centiMTG - 100) - moveOverhead * (200 + centiMTG)) / 100);
|
||||||
|
|
||||||
// x basetime (+ z increment)
|
// x basetime (+ z increment)
|
||||||
// If there is a healthy increment, timeLeft can exceed the actual available
|
// 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
|
// Extra time according to timeLeft
|
||||||
if (originalTimeAdjust < 0)
|
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.
|
// Calculate time constants based on current time left.
|
||||||
double logTimeInSec = std::log10(scaledTime / 1000.0);
|
double logTimeInSec = std::log10(scaledTime / 1000.0);
|
||||||
double optConstant = std::min(0.00308 + 0.000319 * logTimeInSec, 0.00506);
|
double optConstant = std::min(0.0032116 + 0.000321123 * logTimeInSec, 0.00508017);
|
||||||
double maxConstant = std::max(3.39 + 3.01 * logTimeInSec, 2.93);
|
double maxConstant = std::max(3.3977 + 3.03950 * logTimeInSec, 2.94761);
|
||||||
|
|
||||||
optScale = std::min(0.0122 + std::pow(ply + 2.95, 0.462) * optConstant,
|
optScale = std::min(0.0121431 + std::pow(ply + 2.94693, 0.461073) * optConstant,
|
||||||
0.213 * limits.time[us] / timeLeft)
|
0.213035 * limits.time[us] / timeLeft)
|
||||||
* originalTimeAdjust;
|
* 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)
|
// x moves in y seconds (+ z increment)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
optScale = std::min((0.88 + ply / 116.4) / mtg, 0.88 * limits.time[us] / timeLeft);
|
optScale =
|
||||||
maxScale = std::min(6.3, 1.5 + 0.11 * mtg);
|
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
|
// Limit the maximum possible time for this move
|
||||||
optimumTime = TimePoint(optScale * timeLeft);
|
optimumTime = TimePoint(optScale * timeLeft);
|
||||||
maximumTime =
|
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"])
|
if (options["Ponder"])
|
||||||
optimumTime += optimumTime / 4;
|
optimumTime += optimumTime / 4;
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+8
-6
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -193,13 +193,13 @@ void TranspositionTable::clear(ThreadPool& threads) {
|
|||||||
// Returns an approximation of the hashtable
|
// Returns an approximation of the hashtable
|
||||||
// occupation during a search. The hash is x permill full, as per UCI protocol.
|
// occupation during a search. The hash is x permill full, as per UCI protocol.
|
||||||
// Only counts entries which match the current generation.
|
// Only counts entries which match the current generation.
|
||||||
int TranspositionTable::hashfull() const {
|
int TranspositionTable::hashfull(int maxAge) const {
|
||||||
|
int maxAgeInternal = maxAge << GENERATION_BITS;
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
for (int i = 0; i < 1000; ++i)
|
for (int i = 0; i < 1000; ++i)
|
||||||
for (int j = 0; j < ClusterSize; ++j)
|
for (int j = 0; j < ClusterSize; ++j)
|
||||||
cnt += table[i].entry[j].is_occupied()
|
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;
|
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)
|
> tte[i].depth8 - tte[i].relative_age(generation8) * 2)
|
||||||
replace = &tte[i];
|
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
|
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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -51,6 +51,18 @@ struct TTData {
|
|||||||
Depth depth;
|
Depth depth;
|
||||||
Bound bound;
|
Bound bound;
|
||||||
bool is_pv;
|
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 resize(size_t mbSize, ThreadPool& threads); // Set TT size
|
||||||
void clear(ThreadPool& threads); // Re-initialize memory, multithreaded
|
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
|
const; // Approximate what fraction of entries (permille) have been written to during this root search
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -55,7 +55,7 @@ void Tune::make_option(OptionsMap* opts, const string& n, int v, const SetRange&
|
|||||||
if (TuneResults.count(n))
|
if (TuneResults.count(n))
|
||||||
v = TuneResults[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]);
|
LastOption = &((*opts)[n]);
|
||||||
|
|
||||||
// Print formatted parameters, ready to be copy-pasted in Fishtest
|
// 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
|
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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
+16
-1
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -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_WIN_IN_MAX_PLY = VALUE_TB - MAX_PLY;
|
||||||
constexpr Value VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_TB_WIN_IN_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
|
// In the code, we make the assumption that these values
|
||||||
// are such that non_pawn_material() can be used to uniquely
|
// are such that non_pawn_material() can be used to uniquely
|
||||||
// identify the material on the board.
|
// identify the material on the board.
|
||||||
|
|||||||
+177
-3
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <iterator>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
|
|
||||||
#include "benchmark.h"
|
#include "benchmark.h"
|
||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
|
#include "memory.h"
|
||||||
#include "movegen.h"
|
#include "movegen.h"
|
||||||
#include "position.h"
|
#include "position.h"
|
||||||
#include "score.h"
|
#include "score.h"
|
||||||
@@ -39,6 +41,8 @@
|
|||||||
|
|
||||||
namespace Stockfish {
|
namespace Stockfish {
|
||||||
|
|
||||||
|
constexpr auto BenchmarkCommand = "speedtest";
|
||||||
|
|
||||||
constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||||
template<typename... Ts>
|
template<typename... Ts>
|
||||||
struct overload: Ts... {
|
struct overload: Ts... {
|
||||||
@@ -48,7 +52,7 @@ struct overload: Ts... {
|
|||||||
template<typename... Ts>
|
template<typename... Ts>
|
||||||
overload(Ts...) -> overload<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();
|
sync_cout_start();
|
||||||
for (auto& line : split(str, "\n"))
|
for (auto& line : split(str, "\n"))
|
||||||
{
|
{
|
||||||
@@ -69,11 +73,16 @@ UCIEngine::UCIEngine(int argc, char** argv) :
|
|||||||
print_info_string(*str);
|
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_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_no_moves([](const auto& i) { on_update_no_moves(i); });
|
||||||
engine.set_on_update_full(
|
engine.set_on_update_full(
|
||||||
[this](const auto& i) { on_update_full(i, engine.get_options()["UCI_ShowWDL"]); });
|
[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_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() {
|
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
|
// 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.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);
|
go(is);
|
||||||
}
|
}
|
||||||
else if (token == "position")
|
else if (token == "position")
|
||||||
@@ -133,6 +142,8 @@ void UCIEngine::loop() {
|
|||||||
engine.flip();
|
engine.flip();
|
||||||
else if (token == "bench")
|
else if (token == "bench")
|
||||||
bench(is);
|
bench(is);
|
||||||
|
else if (token == BenchmarkCommand)
|
||||||
|
benchmark(is);
|
||||||
else if (token == "d")
|
else if (token == "d")
|
||||||
sync_cout << engine.visualize() << sync_endl;
|
sync_cout << engine.visualize() << sync_endl;
|
||||||
else if (token == "eval")
|
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"]); });
|
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) {
|
void UCIEngine::setoption(std::istringstream& is) {
|
||||||
engine.wait_for_search_finished();
|
engine.wait_for_search_finished();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -58,10 +58,11 @@ class UCIEngine {
|
|||||||
Engine engine;
|
Engine engine;
|
||||||
CommandLine cli;
|
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 go(std::istringstream& is);
|
||||||
void bench(std::istream& args);
|
void bench(std::istream& args);
|
||||||
|
void benchmark(std::istream& args);
|
||||||
void position(std::istringstream& is);
|
void position(std::istringstream& is);
|
||||||
void setoption(std::istringstream& is);
|
void setoption(std::istringstream& is);
|
||||||
std::uint64_t perft(const Search::LimitsType&);
|
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_update_full(const Engine::InfoFull& info, bool showWDL);
|
||||||
static void on_iter(const Engine::InfoIter& info);
|
static void on_iter(const Engine::InfoIter& info);
|
||||||
static void on_bestmove(std::string_view bestmove, std::string_view ponder);
|
static void on_bestmove(std::string_view bestmove, std::string_view ponder);
|
||||||
|
|
||||||
|
void init_search_update_listeners();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Stockfish
|
} // namespace Stockfish
|
||||||
|
|||||||
+22
-20
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -57,17 +58,31 @@ void OptionsMap::setoption(std::istringstream& is) {
|
|||||||
sync_cout << "No such option: " << name << sync_endl;
|
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);
|
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))
|
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); }
|
std::size_t OptionsMap::count(const std::string& name) const { return options_map.count(name); }
|
||||||
|
|
||||||
Option::Option(const OptionsMap* map) :
|
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); }
|
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
|
// Updates currentValue and triggers on_change() action. It's up to
|
||||||
// the GUI to check for option's limits, but we could receive the new value
|
// the GUI to check for option's limits, but we could receive the new value
|
||||||
// from the user by console window, so let's check the bounds anyway.
|
// from the user by console window, so let's check the bounds anyway.
|
||||||
@@ -161,7 +163,7 @@ Option& Option::operator=(const std::string& v) {
|
|||||||
std::string token;
|
std::string token;
|
||||||
std::istringstream ss(defaultValue);
|
std::istringstream ss(defaultValue);
|
||||||
while (ss >> token)
|
while (ss >> token)
|
||||||
comboMap[token] << Option();
|
comboMap.add(token, Option());
|
||||||
if (!comboMap.count(v) || v == "var")
|
if (!comboMap.count(v) || v == "var")
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||||
Copyright (C) 2004-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
|
Stockfish is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -54,12 +54,13 @@ class Option {
|
|||||||
|
|
||||||
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
|
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
|
||||||
|
|
||||||
|
int operator<<(const Option&) = delete;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class OptionsMap;
|
friend class OptionsMap;
|
||||||
friend class Engine;
|
friend class Engine;
|
||||||
friend class Tune;
|
friend class Tune;
|
||||||
|
|
||||||
void operator<<(const Option&);
|
|
||||||
|
|
||||||
std::string defaultValue, currentValue, type;
|
std::string defaultValue, currentValue, type;
|
||||||
int min, max;
|
int min, max;
|
||||||
@@ -82,8 +83,9 @@ class OptionsMap {
|
|||||||
|
|
||||||
void setoption(std::istringstream&);
|
void setoption(std::istringstream&);
|
||||||
|
|
||||||
Option operator[](const std::string&) const;
|
const Option& operator[](const std::string&) const;
|
||||||
Option& operator[](const std::string&);
|
|
||||||
|
void add(const std::string&, const Option& option);
|
||||||
|
|
||||||
std::size_t count(const std::string&) const;
|
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
|
#!/bin/bash
|
||||||
# verify perft numbers (positions from www.chessprogramming.org/Perft_Results)
|
# verify perft numbers (positions from https://www.chessprogramming.org/Perft_Results)
|
||||||
|
|
||||||
error()
|
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