Merge commit '315f8ba4bf7d846b35f984d5e6040c14a512d9b9' into cluster

This is the last commit before there are more conflicts.
This commit is contained in:
Steinar H. Gunderson
2025-12-25 20:12:54 +01:00
25 changed files with 475 additions and 395 deletions
+26 -26
View File
@@ -8,11 +8,11 @@
"comp": "gcc", "comp": "gcc",
"shell": "bash", "shell": "bash",
"archive_ext": "tar", "archive_ext": "tar",
"sde": "/home/runner/work/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.27.0-2023-09-13-lin/sde -future --" "sde": "/home/runner/work/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.33.0-2024-01-07-lin/sde -future --"
}, },
{ {
"name": "MacOS 13 Apple Clang", "name": "macOS 15 Apple Clang",
"os": "macos-13", "os": "macos-15-intel",
"simple_name": "macos", "simple_name": "macos",
"compiler": "clang++", "compiler": "clang++",
"comp": "clang", "comp": "clang",
@@ -20,7 +20,7 @@
"archive_ext": "tar" "archive_ext": "tar"
}, },
{ {
"name": "MacOS 14 Apple Clang M1", "name": "macOS 14 Apple Clang M1",
"os": "macos-14", "os": "macos-14",
"simple_name": "macos-m1", "simple_name": "macos-m1",
"compiler": "clang++", "compiler": "clang++",
@@ -38,7 +38,7 @@
"msys_env": "x86_64-gcc", "msys_env": "x86_64-gcc",
"shell": "msys2 {0}", "shell": "msys2 {0}",
"ext": ".exe", "ext": ".exe",
"sde": "/d/a/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.27.0-2023-09-13-win/sde.exe -future --", "sde": "/d/a/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.33.0-2024-01-07-win/sde.exe -future --",
"archive_ext": "zip" "archive_ext": "zip"
}, },
{ {
@@ -61,8 +61,8 @@
"x86-64-bmi2", "x86-64-bmi2",
"x86-64-avxvnni", "x86-64-avxvnni",
"x86-64-avx512", "x86-64-avx512",
"x86-64-vnni256",
"x86-64-vnni512", "x86-64-vnni512",
"x86-64-avx512icl",
"apple-silicon", "apple-silicon",
"armv8", "armv8",
"armv8-dotprod" "armv8-dotprod"
@@ -105,13 +105,13 @@
} }
}, },
{ {
"binaries": "x86-64-vnni256", "binaries": "x86-64-vnni512",
"config": { "config": {
"os": "macos-14" "os": "macos-14"
} }
}, },
{ {
"binaries": "x86-64-vnni512", "binaries": "x86-64-avx512icl",
"config": { "config": {
"os": "macos-14" "os": "macos-14"
} }
@@ -119,25 +119,25 @@
{ {
"binaries": "x86-64-avxvnni", "binaries": "x86-64-avxvnni",
"config": { "config": {
"os": "macos-13" "os": "macos-15-intel"
} }
}, },
{ {
"binaries": "x86-64-avx512", "binaries": "x86-64-avx512",
"config": { "config": {
"os": "macos-13" "os": "macos-15-intel"
}
},
{
"binaries": "x86-64-vnni256",
"config": {
"os": "macos-13"
} }
}, },
{ {
"binaries": "x86-64-vnni512", "binaries": "x86-64-vnni512",
"config": { "config": {
"os": "macos-13" "os": "macos-15-intel"
}
},
{
"binaries": "x86-64-avx512icl",
"config": {
"os": "macos-15-intel"
} }
}, },
{ {
@@ -176,18 +176,18 @@
"os": "windows-11-arm" "os": "windows-11-arm"
} }
}, },
{
"binaries": "x86-64-vnni256",
"config": {
"os": "windows-11-arm"
}
},
{ {
"binaries": "x86-64-vnni512", "binaries": "x86-64-vnni512",
"config": { "config": {
"os": "windows-11-arm" "os": "windows-11-arm"
} }
}, },
{
"binaries": "x86-64-avx512icl",
"config": {
"os": "windows-11-arm"
}
},
{ {
"binaries": "apple-silicon", "binaries": "apple-silicon",
"config": { "config": {
@@ -215,7 +215,7 @@
{ {
"binaries": "apple-silicon", "binaries": "apple-silicon",
"config": { "config": {
"os": "macos-13" "os": "macos-15-intel"
} }
}, },
{ {
@@ -239,7 +239,7 @@
{ {
"binaries": "armv8", "binaries": "armv8",
"config": { "config": {
"os": "macos-13" "os": "macos-15-intel"
} }
}, },
{ {
@@ -269,7 +269,7 @@
{ {
"binaries": "armv8-dotprod", "binaries": "armv8-dotprod",
"config": { "config": {
"os": "macos-13" "os": "macos-15-intel"
} }
}, },
{ {
+2 -2
View File
@@ -43,10 +43,10 @@ jobs:
- name: Download SDE package - name: Download SDE package
if: runner.os == 'Linux' || runner.os == 'Windows' if: runner.os == 'Linux' || runner.os == 'Windows'
uses: petarpetrovt/setup-sde@91a1a03434384e064706634125a15f7446d2aafb # @v2.3 uses: petarpetrovt/setup-sde@f0fa5971dc275704531e94264dd23250c442aa41 # @v2.4
with: with:
environmentVariableName: SDE_DIR environmentVariableName: SDE_DIR
sdeVersion: 9.27.0 sdeVersion: 9.33.0
- name: Download the used network from the fishtest framework - name: Download the used network from the fishtest framework
run: make net run: make net
+30 -11
View File
@@ -24,7 +24,7 @@ jobs:
with: with:
repository: vondele/matetrack repository: vondele/matetrack
path: matetrack path: matetrack
ref: 4f8a80860ed8f3607f05a9195df8b40203bdc360 ref: 2d96fa3373f90edb032b7ea7468473fb9e6f0343
persist-credentials: false persist-credentials: false
- name: matetrack install deps - name: matetrack install deps
@@ -47,25 +47,44 @@ jobs:
wget --no-verbose -r -nH --cut-dirs=2 --no-parent --reject="index.html*" -e robots=off https://tablebase.lichess.ovh/tables/standard/3-4-5-wdl/ wget --no-verbose -r -nH --cut-dirs=2 --no-parent --reject="index.html*" -e robots=off https://tablebase.lichess.ovh/tables/standard/3-4-5-wdl/
wget --no-verbose -r -nH --cut-dirs=2 --no-parent --reject="index.html*" -e robots=off https://tablebase.lichess.ovh/tables/standard/3-4-5-dtz/ wget --no-verbose -r -nH --cut-dirs=2 --no-parent --reject="index.html*" -e robots=off https://tablebase.lichess.ovh/tables/standard/3-4-5-dtz/
- name: Run matetrack - name: Run matetrack th1
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 matecheckout1.out
! grep "issues were detected" matecheckout.out > /dev/null ! grep "issues were detected" matecheckout1.out > /dev/null
- name: Run matetrack with --syzygy50MoveRule false - name: Run matetrack th4
working-directory: matetrack
run: |
python matecheck.py --syzygyPath 3-4-5-wdl/:3-4-5-dtz/ --engine /home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish --epdFile mates2000.epd --nodes 100000 --threads 4 | tee matecheckout4.out
! grep "issues were detected" matecheckout4.out > /dev/null
- name: Run matetrack th1 with --syzygy50MoveRule false
working-directory: matetrack working-directory: matetrack
run: | run: |
grep 5men cursed.epd > cursed5.epd 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 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 matecheckcursed1.out
! grep "issues were detected" matecheckcursed.out > /dev/null ! grep "issues were detected" matecheckcursed1.out > /dev/null
- name: Verify mate and TB win count for matecheckcursed.out - name: Run matetrack th4 with --syzygy50MoveRule false
working-directory: matetrack working-directory: matetrack
run: | run: |
mates=$(grep "Found mates:" matecheckcursed.out | awk '{print $3}') grep 5men cursed.epd > cursed5.epd
tbwins=$(grep "Found TB wins:" matecheckcursed.out | awk '{print $4}') 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 --threads 4 --syzygy50MoveRule false | tee matecheckcursed4.out
! grep "issues were detected" matecheckcursed4.out > /dev/null
- name: Verify mate and TB win count for matecheckcursed[14].out
working-directory: matetrack
run: |
mates=$(grep "Found mates:" matecheckcursed1.out | awk '{print $3}')
tbwins=$(grep "Found TB wins:" matecheckcursed1.out | awk '{print $4}')
if [ $(($mates + $tbwins)) -ne 32 ]; then if [ $(($mates + $tbwins)) -ne 32 ]; then
echo "Sum of mates and TB wins is not 32 in matecheckcursed.out" >&2 echo "Sum of mates and TB wins is not 32 in matecheckcursed1.out" >&2
exit 1
fi
mates=$(grep "Found mates:" matecheckcursed4.out | awk '{print $3}')
tbwins=$(grep "Found TB wins:" matecheckcursed4.out | awk '{print $4}')
if [ $(($mates + $tbwins)) -ne 32 ]; then
echo "Sum of mates and TB wins is not 32 in matecheckcursed4.out" >&2
exit 1 exit 1
fi fi
+1 -1
View File
@@ -42,7 +42,7 @@ jobs:
- name: Run with glibcxx assertions - name: Run with glibcxx assertions
make_option: "" make_option: ""
cxx_extra_flags: -D_GLIBCXX_ASSERTIONS cxx_extra_flags: -D_GLIBCXX_ASSERTIONS
instrumented_option: non instrumented_option: none
defaults: defaults:
run: run:
working-directory: src working-directory: src
+5 -5
View File
@@ -56,21 +56,21 @@ jobs:
base_image: "ppc64le/alpine:latest" base_image: "ppc64le/alpine:latest"
platform: linux/ppc64le platform: linux/ppc64le
shell: bash shell: bash
- name: MacOS 13 Apple Clang - name: macOS 15 Apple Clang
os: macos-13 os: macos-15-intel
compiler: clang++ compiler: clang++
comp: clang comp: clang
run_64bit_tests: true run_64bit_tests: true
shell: bash shell: bash
- name: MacOS 14 Apple Clang M1 - name: macOS 14 Apple Clang M1
os: macos-14 os: macos-14
compiler: clang++ compiler: clang++
comp: clang comp: clang
run_64bit_tests: false run_64bit_tests: false
run_m1_tests: true run_m1_tests: true
shell: bash shell: bash
- name: MacOS 13 GCC 11 - name: macOS 15 GCC 11
os: macos-13 os: macos-15-intel
compiler: g++-11 compiler: g++-11
comp: gcc comp: gcc
run_64bit_tests: true run_64bit_tests: true
+4 -14
View File
@@ -12,20 +12,17 @@ on:
jobs: jobs:
Artifacts: Artifacts:
name: ${{ matrix.config.name }} ${{ matrix.binaries }} name: ${{ matrix.config.name }} ${{ matrix.binaries }}
runs-on: ${{ matrix.config.os }} runs-on: ubuntu-latest
env: env:
COMPCXX: ${{ matrix.config.compiler }}
COMP: ${{ matrix.config.comp }}
EXT: ${{ matrix.config.ext }} EXT: ${{ matrix.config.ext }}
NAME: ${{ matrix.config.simple_name }} NAME: ${{ matrix.config.simple_name }}
BINARY: ${{ matrix.binaries }} BINARY: ${{ matrix.binaries }}
SDE: ${{ matrix.config.sde }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: ${{ fromJson(inputs.matrix) }} matrix: ${{ fromJson(inputs.matrix) }}
defaults: defaults:
run: run:
shell: ${{ matrix.config.shell }} shell: bash
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
@@ -37,13 +34,6 @@ jobs:
name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }}
path: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} path: ${{ matrix.config.simple_name }} ${{ matrix.binaries }}
- name: Setup msys and install required packages
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.config.msys_sys }}
install: mingw-w64-${{ matrix.config.msys_env }} make git zip
- name: Create Package - name: Create Package
run: | run: |
mkdir stockfish mkdir stockfish
@@ -69,13 +59,13 @@ jobs:
cp CONTRIBUTING.md ../stockfish/ cp CONTRIBUTING.md ../stockfish/
- name: Create tar - name: Create tar
if: runner.os != 'Windows' if: ${{ !startsWith(matrix.config.os, 'windows') }}
run: | run: |
chmod +x ./stockfish/stockfish-$NAME-$BINARY$EXT chmod +x ./stockfish/stockfish-$NAME-$BINARY$EXT
tar -cvf stockfish-$NAME-$BINARY.tar stockfish tar -cvf stockfish-$NAME-$BINARY.tar stockfish
- name: Create zip - name: Create zip
if: runner.os == 'Windows' if: ${{ startsWith(matrix.config.os, 'windows') }}
run: | run: |
zip -r stockfish-$NAME-$BINARY.zip stockfish zip -r stockfish-$NAME-$BINARY.zip stockfish
+11 -1
View File
@@ -31,6 +31,8 @@ Andy Duplain
Antoine Champion (antoinechampion) Antoine Champion (antoinechampion)
Aram Tumanian (atumanian) Aram Tumanian (atumanian)
Arjun Temurnikar Arjun Temurnikar
Aron Petkovski (fury)
Arseniy Surkov (codedeliveryservice)
Artem Solopiy (EntityFX) Artem Solopiy (EntityFX)
Auguste Pop Auguste Pop
Balazs Szilagyi Balazs Szilagyi
@@ -56,10 +58,11 @@ Ciekce
clefrks clefrks
Clemens L. (rn5f107s2) Clemens L. (rn5f107s2)
Cody Ho (aesrentai) Cody Ho (aesrentai)
CSTENTOR
Dale Weiler (graphitemaster) Dale Weiler (graphitemaster)
Daniel Axtens (daxtens) Daniel Axtens (daxtens)
Daniel Dugovic (ddugovic) Daniel Dugovic (ddugovic)
Daniel Monroe (Ergodice) Daniel Monroe (daniel-monroe)
Daniel Samek (DanSamek) Daniel Samek (DanSamek)
Dan Schmidt (dfannius) Dan Schmidt (dfannius)
Dariusz Orzechowski (dorzechowski) Dariusz Orzechowski (dorzechowski)
@@ -123,6 +126,7 @@ Jonathan McDermid (jonathanmcdermid)
Joost VandeVondele (vondele) Joost VandeVondele (vondele)
Joseph Ellis (jhellis3) Joseph Ellis (jhellis3)
Joseph R. Prostko Joseph R. Prostko
Jost Triller (tsoj)
Jörg Oster (joergoster) Jörg Oster (joergoster)
Julian Willemer (NightlyKing) Julian Willemer (NightlyKing)
jundery jundery
@@ -130,6 +134,7 @@ Justin Blanchard (UncombedCoconut)
Kelly Wilson Kelly Wilson
Ken Takusagawa Ken Takusagawa
Kenneth Lee (kennethlee33) Kenneth Lee (kennethlee33)
kevlu8
Kian E (KJE-98) Kian E (KJE-98)
kinderchocolate kinderchocolate
Kiran Panditrao (Krgp) Kiran Panditrao (Krgp)
@@ -178,6 +183,7 @@ Nathan Rugg (nmrugg)
Nguyen Pham (nguyenpham) Nguyen Pham (nguyenpham)
Nicklas Persson (NicklasPersson) Nicklas Persson (NicklasPersson)
Nick Pelling (nickpelling) Nick Pelling (nickpelling)
Nicolas Duhamel (nikloskoda)
Niklas Fiekas (niklasf) Niklas Fiekas (niklasf)
Nikolay Kostov (NikolayIT) Nikolay Kostov (NikolayIT)
Norman Schmidt (FireFather) Norman Schmidt (FireFather)
@@ -211,6 +217,7 @@ Ronald de Man (syzygy1, syzygy)
Ron Britvich (Britvich) Ron Britvich (Britvich)
rqs rqs
Rui Coelho (ruicoelhopedro) Rui Coelho (ruicoelhopedro)
rustam-cpp
Ryan Schmitt Ryan Schmitt
Ryan Takker Ryan Takker
Sami Kiminki (skiminki) Sami Kiminki (skiminki)
@@ -230,12 +237,15 @@ Stefano Di Martino (StefanoD)
Steinar Gunderson (sesse) Steinar Gunderson (sesse)
Stéphane Nicolet (snicolet) Stéphane Nicolet (snicolet)
Stephen Touset (stouset) Stephen Touset (stouset)
Stockfisher69
Styx (styxdoto)
Syine Mineta (MinetaS) Syine Mineta (MinetaS)
Taras Vuk (TarasVuk) Taras Vuk (TarasVuk)
Thanar2 Thanar2
thaspel thaspel
theo77186 theo77186
TierynnB TierynnB
Timothy Herchen (anematode)
Ting-Hsuan Huang (fffelix-huang) Ting-Hsuan Huang (fffelix-huang)
Tobias Steinmann Tobias Steinmann
Tomasz Sobczyk (Sopel97) Tomasz Sobczyk (Sopel97)
+6 -4
View File
@@ -31,7 +31,7 @@ set_arch_loongarch64() {
if check_flags 'lasx'; then if check_flags 'lasx'; then
true_arch='loongarch64-lasx' true_arch='loongarch64-lasx'
elif check_flags 'lsx'; then elif check_flags 'lsx'; then
true_arch='lonngarch64-lsx' true_arch='loongarch64-lsx'
else else
true_arch='loongarch64' true_arch='loongarch64'
fi fi
@@ -42,9 +42,11 @@ set_arch_x86_64() {
if check_flags 'avx512f' 'avx512cd' 'avx512vl' 'avx512dq' 'avx512bw' 'avx512ifma' 'avx512vbmi' 'avx512vbmi2' 'avx512vpopcntdq' 'avx512bitalg' 'avx512vnni' 'vpclmulqdq' 'gfni' 'vaes'; then if check_flags 'avx512f' 'avx512cd' 'avx512vl' 'avx512dq' 'avx512bw' 'avx512ifma' 'avx512vbmi' 'avx512vbmi2' 'avx512vpopcntdq' 'avx512bitalg' 'avx512vnni' 'vpclmulqdq' 'gfni' 'vaes'; then
true_arch='x86-64-avx512icl' true_arch='x86-64-avx512icl'
elif check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then elif check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then
true_arch='x86-64-vnni256' true_arch='x86-64-vnni512'
elif check_flags 'avx512f' 'avx512bw'; then elif check_flags 'avx512f' 'avx512bw'; then
true_arch='x86-64-avx512' true_arch='x86-64-avx512'
elif check_flags 'avxvnni'; then
true_arch='x86-64-avxvnni'
elif [ -z "${znver_1_2+1}" ] && check_flags 'bmi2'; then elif [ -z "${znver_1_2+1}" ] && check_flags 'bmi2'; then
true_arch='x86-64-bmi2' true_arch='x86-64-bmi2'
elif check_flags 'avx2'; then elif check_flags 'avx2'; then
@@ -57,7 +59,7 @@ set_arch_x86_64() {
} }
set_arch_ppc_64() { set_arch_ppc_64() {
if $(grep -q -w "altivec" /proc/cpuinfo); then if grep -q -w "altivec" /proc/cpuinfo; then
power=$(grep -oP -m 1 'cpu\t+: POWER\K\d+' /proc/cpuinfo) power=$(grep -oP -m 1 'cpu\t+: POWER\K\d+' /proc/cpuinfo)
if [ "0$power" -gt 7 ]; then if [ "0$power" -gt 7 ]; then
# VSX started with POWER8 # VSX started with POWER8
@@ -83,7 +85,7 @@ case $uname_s in
'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 '_.')
set_arch_x86_64 set_arch_x86_64
if [ "$true_arch" = 'x86-64-vnni256' ] || [ "$true_arch" = 'x86-64-avx512' ]; then if [ "$true_arch" = 'x86-64-avx512' ]; then
file_arch='x86-64-bmi2' file_arch='x86-64-bmi2'
fi fi
;; ;;
+8 -27
View File
@@ -98,7 +98,6 @@ VPATH = syzygy:nnue:nnue/features
# avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2 # avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2
# avxvnni = yes/no --- -mavxvnni --- Use Intel Vector Neural Network Instructions AVX # avxvnni = yes/no --- -mavxvnni --- Use Intel Vector Neural Network Instructions AVX
# 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
# vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512 # vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512
# avx512icl = yes/no --- ... multiple ... --- Use All AVX-512 features available on both Intel Ice Lake and AMD Zen 4 # avx512icl = yes/no --- ... multiple ... --- Use All AVX-512 features available on both Intel Ice Lake and AMD Zen 4
# altivec = yes/no --- -maltivec --- Use PowerPC Altivec SIMD extension # altivec = yes/no --- -maltivec --- Use PowerPC Altivec SIMD extension
@@ -130,7 +129,7 @@ endif
# explicitly check for the list of supported architectures (as listed with make help), # explicitly check for the list of supported architectures (as listed with make help),
# the user can override with `make ARCH=x86-64-avx512icl SUPPORTED_ARCH=true` # the user can override with `make ARCH=x86-64-avx512icl SUPPORTED_ARCH=true`
ifeq ($(ARCH), $(filter $(ARCH), \ ifeq ($(ARCH), $(filter $(ARCH), \
x86-64-avx512icl x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni \ x86-64-avx512icl x86-64-vnni512 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-bmi2 x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \
x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-64-altivec ppc-64-vsx 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 \ armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64 \
@@ -155,7 +154,6 @@ sse41 = no
avx2 = no avx2 = no
avxvnni = no avxvnni = no
avx512 = no avx512 = no
vnni256 = no
vnni512 = no vnni512 = no
avx512icl = no avx512icl = no
altivec = no altivec = no
@@ -272,17 +270,6 @@ ifeq ($(findstring -avx512,$(ARCH)),-avx512)
avx512 = yes avx512 = yes
endif endif
ifeq ($(findstring -vnni256,$(ARCH)),-vnni256)
popcnt = yes
sse = yes
sse2 = yes
ssse3 = yes
sse41 = yes
avx2 = yes
pext = yes
vnni256 = yes
endif
ifeq ($(findstring -vnni512,$(ARCH)),-vnni512) ifeq ($(findstring -vnni512,$(ARCH)),-vnni512)
popcnt = yes popcnt = yes
sse = yes sse = yes
@@ -593,6 +580,10 @@ endif
# Locate the version in the same directory as the compiler used, # Locate the version in the same directory as the compiler used,
# with fallback to a generic one if it can't be located # with fallback to a generic one if it can't be located
LLVM_PROFDATA := $(dir $(realpath $(shell which $(CXX) 2> /dev/null)))llvm-profdata LLVM_PROFDATA := $(dir $(realpath $(shell which $(CXX) 2> /dev/null)))llvm-profdata
# for icx
ifeq ($(wildcard $(LLVM_PROFDATA)),)
LLVM_PROFDATA := $(dir $(realpath $(shell which $(CXX) 2> /dev/null)))/compiler/llvm-profdata
endif
ifeq ($(wildcard $(LLVM_PROFDATA)),) ifeq ($(wildcard $(LLVM_PROFDATA)),)
LLVM_PROFDATA := llvm-profdata LLVM_PROFDATA := llvm-profdata
endif endif
@@ -723,17 +714,10 @@ ifeq ($(avx512),yes)
endif endif
endif endif
ifeq ($(vnni256),yes)
CXXFLAGS += -DUSE_VNNI
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -mavx512f -mavx512bw -mavx512vnni -mavx512dq -mavx512vl -mprefer-vector-width=256
endif
endif
ifeq ($(vnni512),yes) ifeq ($(vnni512),yes)
CXXFLAGS += -DUSE_VNNI CXXFLAGS += -DUSE_VNNI
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
CXXFLAGS += -mavx512f -mavx512bw -mavx512vnni -mavx512dq -mavx512vl -mprefer-vector-width=512 CXXFLAGS += -mavx512f -mavx512bw -mavx512vnni -mavx512dq -mavx512vl
endif endif
endif endif
@@ -911,9 +895,8 @@ help:
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-avx512icl > x86 64-bit with minimum avx512 support of Intel Ice Lane or AMD Zen 4" && \ echo "x86-64-avx512icl > x86 64-bit with minimum avx512 support of Intel Ice Lake or AMD Zen 4" && \
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-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" && \
@@ -1058,7 +1041,6 @@ config-sanity: net
echo "avx2: '$(avx2)'" && \ echo "avx2: '$(avx2)'" && \
echo "avxvnni: '$(avxvnni)'" && \ echo "avxvnni: '$(avxvnni)'" && \
echo "avx512: '$(avx512)'" && \ echo "avx512: '$(avx512)'" && \
echo "vnni256: '$(vnni256)'" && \
echo "vnni512: '$(vnni512)'" && \ echo "vnni512: '$(vnni512)'" && \
echo "avx512icl: '$(avx512icl)'" && \ echo "avx512icl: '$(avx512icl)'" && \
echo "altivec: '$(altivec)'" && \ echo "altivec: '$(altivec)'" && \
@@ -1096,7 +1078,6 @@ config-sanity: net
(test "$(sse41)" = "yes" || test "$(sse41)" = "no") && \ (test "$(sse41)" = "yes" || test "$(sse41)" = "no") && \
(test "$(avx2)" = "yes" || test "$(avx2)" = "no") && \ (test "$(avx2)" = "yes" || test "$(avx2)" = "no") && \
(test "$(avx512)" = "yes" || test "$(avx512)" = "no") && \ (test "$(avx512)" = "yes" || test "$(avx512)" = "no") && \
(test "$(vnni256)" = "yes" || test "$(vnni256)" = "no") && \
(test "$(vnni512)" = "yes" || test "$(vnni512)" = "no") && \ (test "$(vnni512)" = "yes" || test "$(vnni512)" = "no") && \
(test "$(avx512icl)" = "yes" || test "$(avx512icl)" = "no") && \ (test "$(avx512icl)" = "yes" || test "$(avx512icl)" = "no") && \
(test "$(altivec)" = "yes" || test "$(altivec)" = "no") && \ (test "$(altivec)" = "yes" || test "$(altivec)" = "no") && \
@@ -1150,7 +1131,7 @@ icx-profile-make:
all all
icx-profile-use: icx-profile-use:
$(XCRUN) llvm-profdata merge -output=stockfish.profdata *.profraw $(XCRUN) $(LLVM_PROFDATA) merge -output=stockfish.profdata *.profraw
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \
EXTRACXXFLAGS='-fprofile-instr-use=stockfish.profdata' \ EXTRACXXFLAGS='-fprofile-instr-use=stockfish.profdata' \
EXTRALDFLAGS='-fprofile-use ' \ EXTRALDFLAGS='-fprofile-use ' \
-5
View File
@@ -179,11 +179,6 @@ inline Bitboard between_bb(Square s1, Square s2) {
return BetweenBB[s1][s2]; return BetweenBB[s1][s2];
} }
// Returns true if the squares s1, s2 and s3 are aligned either on a
// straight or on a diagonal line.
inline bool aligned(Square s1, Square s2, Square s3) { return line_bb(s1, s2) & s3; }
// distance() functions return the distance between x and y, defined as the // distance() functions return the distance between x and y, defined as the
// number of steps for a king in x to reach y. // number of steps for a king in x to reach y.
+7 -10
View File
@@ -33,7 +33,7 @@
namespace Stockfish { namespace Stockfish {
constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 constexpr int PAWN_HISTORY_SIZE = 8192; // has to be a power of 2
constexpr int CORRECTION_HISTORY_SIZE = 32768; // 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 CORRECTION_HISTORY_LIMIT = 1024;
constexpr int LOW_PLY_HISTORY_SIZE = 5; constexpr int LOW_PLY_HISTORY_SIZE = 5;
@@ -44,14 +44,12 @@ static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0,
static_assert((CORRECTION_HISTORY_SIZE & (CORRECTION_HISTORY_SIZE - 1)) == 0, static_assert((CORRECTION_HISTORY_SIZE & (CORRECTION_HISTORY_SIZE - 1)) == 0,
"CORRECTION_HISTORY_SIZE has to be a power of 2"); "CORRECTION_HISTORY_SIZE has to be a power of 2");
enum PawnHistoryType { inline int pawn_history_index(const Position& pos) {
Normal, return pos.pawn_key() & (PAWN_HISTORY_SIZE - 1);
Correction }
};
template<PawnHistoryType T = Normal> inline int pawn_correction_history_index(const Position& pos) {
inline int pawn_structure_index(const Position& pos) { return pos.pawn_key() & (CORRECTION_HISTORY_SIZE - 1);
return pos.pawn_key() & ((T == Normal ? PAWN_HISTORY_SIZE : CORRECTION_HISTORY_SIZE) - 1);
} }
inline int minor_piece_index(const Position& pos) { inline int minor_piece_index(const Position& pos) {
@@ -103,7 +101,7 @@ using Stats = MultiArray<StatsEntry<T, D>, Sizes...>;
// ButterflyHistory records how often quiet moves have been successful or unsuccessful // ButterflyHistory records how often quiet moves have been successful or unsuccessful
// during the current search, and is used for reduction and move ordering decisions. // 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, // 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) // see https://www.chessprogramming.org/Butterfly_Boards
using ButterflyHistory = Stats<std::int16_t, 7183, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)>; 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 // LowPlyHistory is adressed by play and move's from and to squares, used
@@ -120,7 +118,6 @@ using PieceToHistory = Stats<std::int16_t, 30000, PIECE_NB, SQUARE_NB>;
// ContinuationHistory is the combined history of a given pair of moves, usually // ContinuationHistory is the combined history of a given pair of moves, usually
// the current one given a previous one. The nested history table is based on // the current one given a previous one. The nested history table is based on
// PieceToHistory instead of ButterflyBoards. // PieceToHistory instead of ButterflyBoards.
// (~63 elo)
using ContinuationHistory = MultiArray<PieceToHistory, PIECE_NB, SQUARE_NB>; using ContinuationHistory = MultiArray<PieceToHistory, PIECE_NB, SQUARE_NB>;
// PawnHistory is addressed by the pawn structure and a move's [piece][to] // PawnHistory is addressed by the pawn structure and a move's [piece][to]
+1
View File
@@ -37,6 +37,7 @@ namespace {
#if defined(USE_AVX512ICL) #if defined(USE_AVX512ICL)
inline Move* write_moves(Move* moveList, uint32_t mask, __m512i vector) { inline Move* write_moves(Move* moveList, uint32_t mask, __m512i vector) {
// Avoid _mm512_mask_compressstoreu_epi16() as it's 256 uOps on Zen4
_mm512_storeu_si512(reinterpret_cast<__m512i*>(moveList), _mm512_storeu_si512(reinterpret_cast<__m512i*>(moveList),
_mm512_maskz_compress_epi16(mask, vector)); _mm512_maskz_compress_epi16(mask, vector));
return moveList + popcount(mask); return moveList + popcount(mask);
+10 -25
View File
@@ -115,8 +115,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, int th, const CapturePieceTo
threshold(th) { threshold(th) {
assert(!pos.checkers()); assert(!pos.checkers());
stage = PROBCUT_TT stage = PROBCUT_TT + !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm));
+ !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm) && pos.see_ge(ttm, threshold));
} }
// Assigns a numerical value to each move in a list, used for sorting. // Assigns a numerical value to each move in a list, used for sorting.
@@ -129,13 +128,15 @@ ExtMove* MovePicker::score(MoveList<Type>& ml) {
Color us = pos.side_to_move(); Color us = pos.side_to_move();
[[maybe_unused]] Bitboard threatByLesser[QUEEN + 1]; [[maybe_unused]] Bitboard threatByLesser[KING + 1];
if constexpr (Type == QUIETS) if constexpr (Type == QUIETS)
{ {
threatByLesser[PAWN] = 0;
threatByLesser[KNIGHT] = threatByLesser[BISHOP] = pos.attacks_by<PAWN>(~us); threatByLesser[KNIGHT] = threatByLesser[BISHOP] = pos.attacks_by<PAWN>(~us);
threatByLesser[ROOK] = threatByLesser[ROOK] =
pos.attacks_by<KNIGHT>(~us) | pos.attacks_by<BISHOP>(~us) | threatByLesser[KNIGHT]; pos.attacks_by<KNIGHT>(~us) | pos.attacks_by<BISHOP>(~us) | threatByLesser[KNIGHT];
threatByLesser[QUEEN] = pos.attacks_by<ROOK>(~us) | threatByLesser[ROOK]; threatByLesser[QUEEN] = pos.attacks_by<ROOK>(~us) | threatByLesser[ROOK];
threatByLesser[KING] = pos.attacks_by<QUEEN>(~us) | threatByLesser[QUEEN];
} }
ExtMove* it = cur; ExtMove* it = cur;
@@ -158,7 +159,7 @@ ExtMove* MovePicker::score(MoveList<Type>& ml) {
{ {
// histories // histories
m.value = 2 * (*mainHistory)[us][m.from_to()]; m.value = 2 * (*mainHistory)[us][m.from_to()];
m.value += 2 * (*pawnHistory)[pawn_structure_index(pos)][pc][to]; m.value += 2 * (*pawnHistory)[pawn_history_index(pos)][pc][to];
m.value += (*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]; m.value += (*continuationHistory[2])[pc][to];
@@ -170,12 +171,10 @@ ExtMove* MovePicker::score(MoveList<Type>& ml) {
// penalty for moving to a square threatened by a lesser piece // penalty for moving to a square threatened by a lesser piece
// or bonus for escaping an attack by a lesser piece. // or bonus for escaping an attack by a lesser piece.
if (KNIGHT <= pt && pt <= QUEEN) static constexpr int bonus[KING + 1] = {0, 0, 144, 144, 256, 517, 10000};
{ int v = threatByLesser[pt] & to ? -95 : 100 * bool(threatByLesser[pt] & from);
static constexpr int bonus[QUEEN + 1] = {0, 0, 144, 144, 256, 517}; m.value += bonus[pt] * v;
int v = threatByLesser[pt] & to ? -95 : 100 * bool(threatByLesser[pt] & from);
m.value += bonus[pt] * v;
}
if (ply < LOW_PLY_HISTORY_SIZE) if (ply < LOW_PLY_HISTORY_SIZE)
m.value += 8 * (*lowPlyHistory)[ply][m.from_to()] / (1 + ply); m.value += 8 * (*lowPlyHistory)[ply][m.from_to()] / (1 + ply);
@@ -189,7 +188,7 @@ ExtMove* MovePicker::score(MoveList<Type>& ml) {
{ {
m.value = (*mainHistory)[us][m.from_to()] + (*continuationHistory[0])[pc][to]; m.value = (*mainHistory)[us][m.from_to()] + (*continuationHistory[0])[pc][to];
if (ply < LOW_PLY_HISTORY_SIZE) if (ply < LOW_PLY_HISTORY_SIZE)
m.value += 2 * (*lowPlyHistory)[ply][m.from_to()] / (1 + ply); m.value += (*lowPlyHistory)[ply][m.from_to()];
} }
} }
} }
@@ -316,18 +315,4 @@ top:
void MovePicker::skip_quiet_moves() { skipQuiets = true; } void MovePicker::skip_quiet_moves() { skipQuiets = true; }
// this function must be called after all quiet moves and captures have been generated
bool MovePicker::can_move_king_or_pawn() const {
// SEE negative captures shouldn't be returned in GOOD_CAPTURE stage
assert(stage > GOOD_CAPTURE && stage != EVASION_INIT);
for (const ExtMove* m = moves; m < endGenerated; ++m)
{
PieceType movedPieceType = type_of(pos.moved_piece(*m));
if ((movedPieceType == PAWN || movedPieceType == KING) && pos.legal(*m))
return true;
}
return false;
}
} // namespace Stockfish } // namespace Stockfish
-1
View File
@@ -50,7 +50,6 @@ class MovePicker {
MovePicker(const Position&, Move, int, const CapturePieceToHistory*); MovePicker(const Position&, Move, int, const CapturePieceToHistory*);
Move next_move(); Move next_move();
void skip_quiet_moves(); void skip_quiet_moves();
bool can_move_king_or_pawn() const;
private: private:
template<typename Pred> template<typename Pred>
+91 -14
View File
@@ -22,6 +22,7 @@
#define NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED #define NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED
#include <algorithm> #include <algorithm>
#include <cstddef>
#include <cstdint> #include <cstdint>
#include <iostream> #include <iostream>
@@ -82,7 +83,37 @@ void find_nnz(const std::int32_t* RESTRICT input,
std::uint16_t* RESTRICT out, std::uint16_t* RESTRICT out,
IndexType& count_out) { IndexType& count_out) {
#ifdef USE_AVX512 #if defined(USE_AVX512ICL)
constexpr IndexType SimdWidthIn = 16; // 512 bits / 32 bits
constexpr IndexType SimdWidthOut = 32; // 512 bits / 16 bits
constexpr IndexType NumChunks = InputDimensions / SimdWidthOut;
const __m512i increment = _mm512_set1_epi16(SimdWidthOut);
__m512i base = _mm512_set_epi16( // Same permute order as _mm512_packus_epi32()
31, 30, 29, 28, 15, 14, 13, 12, 27, 26, 25, 24, 11, 10, 9, 8, 23, 22, 21, 20, 7, 6, 5, 4, 19,
18, 17, 16, 3, 2, 1, 0);
IndexType count = 0;
for (IndexType i = 0; i < NumChunks; ++i)
{
const __m512i inputV0 = _mm512_load_si512(input + i * 2 * SimdWidthIn);
const __m512i inputV1 = _mm512_load_si512(input + i * 2 * SimdWidthIn + SimdWidthIn);
// Get a bitmask and gather non zero indices
const __m512i inputV01 = _mm512_packus_epi32(inputV0, inputV1);
const __mmask32 nnzMask = _mm512_test_epi16_mask(inputV01, inputV01);
// Avoid _mm512_mask_compressstoreu_epi16() as it's 256 uOps on Zen4
__m512i nnz = _mm512_maskz_compress_epi16(nnzMask, base);
_mm512_storeu_si512(out + count, nnz);
count += popcount(nnzMask);
base = _mm512_add_epi16(base, increment);
}
count_out = count;
#elif defined(USE_AVX512)
constexpr IndexType SimdWidth = 16; // 512 bits / 32 bits constexpr IndexType SimdWidth = 16; // 512 bits / 32 bits
constexpr IndexType NumChunks = InputDimensions / SimdWidth; constexpr IndexType NumChunks = InputDimensions / SimdWidth;
const __m512i increment = _mm512_set1_epi32(SimdWidth); const __m512i increment = _mm512_set1_epi32(SimdWidth);
@@ -217,11 +248,13 @@ class AffineTransformSparseInput {
#if defined(USE_AVX512) #if defined(USE_AVX512)
using invec_t = __m512i; using invec_t = __m512i;
using outvec_t = __m512i; using outvec_t = __m512i;
#define vec_add_32 _mm512_add_epi32
#define vec_set_32 _mm512_set1_epi32 #define vec_set_32 _mm512_set1_epi32
#define vec_add_dpbusd_32 SIMD::m512_add_dpbusd_epi32 #define vec_add_dpbusd_32 SIMD::m512_add_dpbusd_epi32
#elif defined(USE_AVX2) #elif defined(USE_AVX2)
using invec_t = __m256i; using invec_t = __m256i;
using outvec_t = __m256i; using outvec_t = __m256i;
#define vec_add_32 _mm256_add_epi32
#define vec_set_32 _mm256_set1_epi32 #define vec_set_32 _mm256_set1_epi32
#define vec_add_dpbusd_32 SIMD::m256_add_dpbusd_epi32 #define vec_add_dpbusd_32 SIMD::m256_add_dpbusd_epi32
#elif defined(USE_SSSE3) #elif defined(USE_SSSE3)
@@ -240,12 +273,19 @@ class AffineTransformSparseInput {
#define vec_set_32(a) vreinterpretq_s8_u32(vdupq_n_u32(a)) #define vec_set_32(a) vreinterpretq_s8_u32(vdupq_n_u32(a))
#define vec_add_dpbusd_32 SIMD::neon_m128_add_dpbusd_epi32 #define vec_add_dpbusd_32 SIMD::neon_m128_add_dpbusd_epi32
#endif #endif
static constexpr IndexType OutputSimdWidth = sizeof(outvec_t) / sizeof(OutputType); constexpr IndexType OutputSimdWidth = sizeof(outvec_t) / sizeof(OutputType);
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 8) / ChunkSize; constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 8) / ChunkSize;
constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; constexpr IndexType NumAccums = OutputDimensions / OutputSimdWidth;
std::uint16_t nnz[NumChunks]; // If we're using high-latency dot product instructions, split the accumulators
IndexType count; // to create 3 separate dependency chains and merge at the end
constexpr IndexType NumRegs =
#if defined(USE_VNNI)
3 * NumAccums;
#else
NumAccums;
#endif
std::uint16_t nnz[NumChunks];
IndexType count;
const auto input32 = reinterpret_cast<const std::int32_t*>(input); const auto input32 = reinterpret_cast<const std::int32_t*>(input);
@@ -254,24 +294,61 @@ class AffineTransformSparseInput {
const outvec_t* biasvec = reinterpret_cast<const outvec_t*>(biases); const outvec_t* biasvec = reinterpret_cast<const outvec_t*>(biases);
outvec_t acc[NumRegs]; outvec_t acc[NumRegs];
for (IndexType k = 0; k < NumRegs; ++k) for (IndexType k = 0; k < NumAccums; ++k)
acc[k] = biasvec[k]; acc[k] = biasvec[k];
for (IndexType j = 0; j < count; ++j) const auto* start = nnz;
const auto* end = nnz + count;
// convince GCC to not do weird pointer arithmetic in the following loop
const std::int8_t* weights_cp = weights;
#if defined(USE_VNNI)
for (IndexType k = NumAccums; k < NumRegs; ++k)
acc[k] = vec_zero();
while (start < end - 2)
{ {
const auto i = nnz[j]; const std::ptrdiff_t i0 = *start++;
const invec_t in = vec_set_32(input32[i]); const std::ptrdiff_t i1 = *start++;
const auto col = const std::ptrdiff_t i2 = *start++;
reinterpret_cast<const invec_t*>(&weights[i * OutputDimensions * ChunkSize]); const invec_t in0 = vec_set_32(input32[i0]);
for (IndexType k = 0; k < NumRegs; ++k) const invec_t in1 = vec_set_32(input32[i1]);
const invec_t in2 = vec_set_32(input32[i2]);
const auto col0 =
reinterpret_cast<const invec_t*>(&weights_cp[i0 * OutputDimensions * ChunkSize]);
const auto col1 =
reinterpret_cast<const invec_t*>(&weights_cp[i1 * OutputDimensions * ChunkSize]);
const auto col2 =
reinterpret_cast<const invec_t*>(&weights_cp[i2 * OutputDimensions * ChunkSize]);
for (IndexType k = 0; k < NumAccums; ++k)
{
vec_add_dpbusd_32(acc[k], in0, col0[k]);
vec_add_dpbusd_32(acc[k + NumAccums], in1, col1[k]);
vec_add_dpbusd_32(acc[k + 2 * NumAccums], in2, col2[k]);
}
}
for (IndexType k = 0; k < NumAccums; ++k)
acc[k] = vec_add_32(vec_add_32(acc[k], acc[k + NumAccums]), acc[k + 2 * NumAccums]);
#endif
while (start < end)
{
const std::ptrdiff_t i = *start++;
const invec_t in = vec_set_32(input32[i]);
const auto col =
reinterpret_cast<const invec_t*>(&weights_cp[i * OutputDimensions * ChunkSize]);
for (IndexType k = 0; k < NumAccums; ++k)
vec_add_dpbusd_32(acc[k], in, col[k]); vec_add_dpbusd_32(acc[k], in, col[k]);
} }
outvec_t* outptr = reinterpret_cast<outvec_t*>(output); outvec_t* outptr = reinterpret_cast<outvec_t*>(output);
for (IndexType k = 0; k < NumRegs; ++k) for (IndexType k = 0; k < NumAccums; ++k)
outptr[k] = acc[k]; outptr[k] = acc[k];
#undef vec_set_32 #undef vec_set_32
#undef vec_add_dpbusd_32 #undef vec_add_dpbusd_32
#ifdef vec_add_32
#undef vec_add_32
#endif
#else #else
// Use dense implementation for the other architectures. // Use dense implementation for the other architectures.
affine_transform_non_ssse3<InputDimensions, PaddedInputDimensions, OutputDimensions>( affine_transform_non_ssse3<InputDimensions, PaddedInputDimensions, OutputDimensions>(
+3 -3
View File
@@ -57,9 +57,9 @@ uint64_t perft(Position& pos, Depth depth) {
} }
inline uint64_t perft(const std::string& fen, Depth depth, bool isChess960) { inline uint64_t perft(const std::string& fen, Depth depth, bool isChess960) {
StateListPtr states(new std::deque<StateInfo>(1)); StateInfo st;
Position p; Position p;
p.set(fen, isChess960, &states->back()); p.set(fen, isChess960, &st);
return perft<true>(p, depth); return perft<true>(p, depth);
} }
+65 -16
View File
@@ -79,7 +79,7 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) {
for (Bitboard b = pos.checkers(); b;) for (Bitboard b = pos.checkers(); b;)
os << UCIEngine::square(pop_lsb(b)) << " "; os << UCIEngine::square(pop_lsb(b)) << " ";
if (int(Tablebases::MaxCardinality) >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) if (Tablebases::MaxCardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING))
{ {
StateInfo st; StateInfo st;
@@ -717,6 +717,8 @@ DirtyPiece Position::do_move(Move m,
Piece pc = piece_on(from); Piece pc = piece_on(from);
Piece captured = m.type_of() == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to); Piece captured = m.type_of() == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to);
bool checkEP = false;
DirtyPiece dp; DirtyPiece dp;
dp.pc = pc; dp.pc = pc;
dp.from = from; dp.from = from;
@@ -804,21 +806,14 @@ DirtyPiece Position::do_move(Move m,
// Move the piece. The tricky Chess960 castling is handled earlier // Move the piece. The tricky Chess960 castling is handled earlier
if (m.type_of() != CASTLING) if (m.type_of() != CASTLING)
{
move_piece(from, to); move_piece(from, to);
}
// If the moving piece is a pawn do some special extra work // If the moving piece is a pawn do some special extra work
if (type_of(pc) == PAWN) if (type_of(pc) == PAWN)
{ {
// Set en passant square if the moved pawn can be captured // Check later if the en passant square needs to be set
if ((int(to) ^ int(from)) == 16 if ((int(to) ^ int(from)) == 16)
&& (attacks_bb<PAWN>(to - pawn_push(us), us) & pieces(them, PAWN))) checkEP = true;
{
st->epSquare = to - pawn_push(us);
k ^= Zobrist::enpassant[file_of(st->epSquare)];
}
else if (m.type_of() == PROMOTION) else if (m.type_of() == PROMOTION)
{ {
@@ -863,11 +858,6 @@ DirtyPiece Position::do_move(Move m,
st->minorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; st->minorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
} }
// Update the key with the final value
st->key = k;
if (tt)
prefetch(tt->first_entry(key()));
// Set capture piece // Set capture piece
st->capturedPiece = captured; st->capturedPiece = captured;
@@ -879,6 +869,63 @@ DirtyPiece Position::do_move(Move m,
// Update king attacks used for fast check detection // Update king attacks used for fast check detection
set_check_info(); set_check_info();
// Accurate e.p. info is needed for correct zobrist key generation and 3-fold checking
while (checkEP)
{
auto updateEpSquare = [&] {
st->epSquare = to - pawn_push(us);
k ^= Zobrist::enpassant[file_of(st->epSquare)];
};
Bitboard pawns = attacks_bb<PAWN>(to - pawn_push(us), us) & pieces(them, PAWN);
// If there are no pawns attacking the ep square, ep is not possible
if (!pawns)
break;
// If there are checkers other than the to be captured pawn, ep is never legal
if (checkers() & ~square_bb(to))
break;
if (more_than_one(pawns))
{
// If there are two pawns potentially being abled to capture and at least one
// is not pinned, ep is legal as there are no horizontal exposed checks
if (!more_than_one(blockers_for_king(them) & pawns))
{
updateEpSquare();
break;
}
// If there is no pawn on our king's file, and thus both pawns are pinned
// by bishops, ep is not legal as the king square must be in front of the to square.
// And because the ep square and the king are not on a common diagonal, either ep capture
// would expose the king to a check from one of the bishops
if (!(file_bb(square<KING>(them)) & pawns))
break;
// Otherwise remove the pawn on the king file, as an ep capture by it can never be legal and the
// check below relies on there only being one pawn
pawns &= ~file_bb(square<KING>(them));
}
Square ksq = square<KING>(them);
Square capsq = to;
Bitboard occupied = (pieces() ^ lsb(pawns) ^ capsq) | (to - pawn_push(us));
// If our king is not attacked after making the move, ep is legal.
if (!(attacks_bb<ROOK>(ksq, occupied) & pieces(us, QUEEN, ROOK))
&& !(attacks_bb<BISHOP>(ksq, occupied) & pieces(us, QUEEN, BISHOP)))
updateEpSquare();
break;
}
// Update the key with the final value
st->key = k;
if (tt)
prefetch(tt->first_entry(key()));
// Calculate the repetition info. It is the ply distance from the previous // Calculate the repetition info. It is the ply distance from the previous
// occurrence of the same position, negative in the 3-fold case, or zero // occurrence of the same position, negative in the 3-fold case, or zero
// if the position was not repeated. // if the position was not repeated.
@@ -1058,6 +1105,8 @@ bool Position::see_ge(Move m, int threshold) const {
Square from = m.from_sq(), to = m.to_sq(); Square from = m.from_sq(), to = m.to_sq();
assert(piece_on(from) != NO_PIECE);
int swap = PieceValue[piece_on(to)] - threshold; int swap = PieceValue[piece_on(to)] - threshold;
if (swap < 0) if (swap < 0)
return false; return false;
+6 -13
View File
@@ -103,10 +103,9 @@ class Position {
Square square(Color c) const; Square square(Color c) const;
// Castling // Castling
CastlingRights castling_rights(Color c) const; bool can_castle(CastlingRights cr) const;
bool can_castle(CastlingRights cr) const; bool castling_impeded(CastlingRights cr) const;
bool castling_impeded(CastlingRights cr) const; Square castling_rook_square(CastlingRights cr) const;
Square castling_rook_square(CastlingRights cr) const;
// Checking // Checking
Bitboard checkers() const; Bitboard checkers() const;
@@ -184,8 +183,7 @@ class Position {
Square& rfrom, Square& rfrom,
Square& rto, Square& rto,
DirtyPiece* const dp = nullptr); DirtyPiece* const dp = nullptr);
template<bool AfterMove> Key adjust_key50(Key k) const;
Key adjust_key50(Key k) const;
// Data members // Data members
Piece board[SQUARE_NB]; Piece board[SQUARE_NB];
@@ -248,10 +246,6 @@ inline Square Position::ep_square() const { return st->epSquare; }
inline bool Position::can_castle(CastlingRights cr) const { return st->castlingRights & cr; } inline bool Position::can_castle(CastlingRights cr) const { return st->castlingRights & cr; }
inline CastlingRights Position::castling_rights(Color c) const {
return c & CastlingRights(st->castlingRights);
}
inline bool Position::castling_impeded(CastlingRights cr) const { inline bool Position::castling_impeded(CastlingRights cr) const {
assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO); assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO);
return pieces() & castlingPath[cr]; return pieces() & castlingPath[cr];
@@ -288,11 +282,10 @@ inline Bitboard Position::pinners(Color c) const { return st->pinners[c]; }
inline Bitboard Position::check_squares(PieceType pt) const { return st->checkSquares[pt]; } inline Bitboard Position::check_squares(PieceType pt) const { return st->checkSquares[pt]; }
inline Key Position::key() const { return adjust_key50<false>(st->key); } inline Key Position::key() const { return adjust_key50(st->key); }
template<bool AfterMove>
inline Key Position::adjust_key50(Key k) const { inline Key Position::adjust_key50(Key k) const {
return st->rule50 < 14 - AfterMove ? k : k ^ make_key((st->rule50 - (14 - AfterMove)) / 8); return st->rule50 < 14 ? k : k ^ make_key((st->rule50 - 14) / 8);
} }
inline Key Position::pawn_key() const { return st->pawnKey; } inline Key Position::pawn_key() const { return st->pawnKey; }
+178 -205
View File
@@ -73,18 +73,22 @@ using SearchedList = ValueList<Move, SEARCHEDLIST_CAPACITY>;
// so changing them or adding conditions that are similar requires // so changing them or adding conditions that are similar requires
// tests at these types of time controls. // tests at these types of time controls.
// (*Scaler) All tuned parameters at time controls shorter than
// optimized for require verifications at longer time controls
int correction_value(const Worker& w, const Position& pos, const Stack* const ss) { int correction_value(const Worker& w, const Position& pos, const Stack* const ss) {
const Color us = pos.side_to_move(); const Color us = pos.side_to_move();
const auto m = (ss - 1)->currentMove; const auto m = (ss - 1)->currentMove;
const auto pcv = w.pawnCorrectionHistory[pawn_structure_index<Correction>(pos)][us]; const auto pcv = w.pawnCorrectionHistory[pawn_correction_history_index(pos)][us];
const auto micv = w.minorPieceCorrectionHistory[minor_piece_index(pos)][us]; const auto micv = w.minorPieceCorrectionHistory[minor_piece_index(pos)][us];
const auto wnpcv = w.nonPawnCorrectionHistory[non_pawn_index<WHITE>(pos)][WHITE][us]; const auto wnpcv = w.nonPawnCorrectionHistory[non_pawn_index<WHITE>(pos)][WHITE][us];
const auto bnpcv = w.nonPawnCorrectionHistory[non_pawn_index<BLACK>(pos)][BLACK][us]; const auto bnpcv = w.nonPawnCorrectionHistory[non_pawn_index<BLACK>(pos)][BLACK][us];
const auto cntcv = const auto cntcv =
m.is_ok() ? (*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()] m.is_ok() ? (*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()]
: 0; + (*(ss - 4)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()]
: 8;
return 7696 * pcv + 7689 * micv + 9708 * (wnpcv + bnpcv) + 6978 * cntcv; return 9536 * pcv + 8494 * micv + 10132 * (wnpcv + bnpcv) + 7156 * cntcv;
} }
// Add correctionHistory value to raw staticEval and guarantee evaluation // Add correctionHistory value to raw staticEval and guarantee evaluation
@@ -100,19 +104,22 @@ void update_correction_history(const Position& pos,
const Move m = (ss - 1)->currentMove; const Move m = (ss - 1)->currentMove;
const Color us = pos.side_to_move(); const Color us = pos.side_to_move();
static constexpr int nonPawnWeight = 172; constexpr int nonPawnWeight = 165;
workerThread.pawnCorrectionHistory[pawn_structure_index<Correction>(pos)][us] workerThread.pawnCorrectionHistory[pawn_correction_history_index(pos)][us] << bonus;
<< bonus * 111 / 128; workerThread.minorPieceCorrectionHistory[minor_piece_index(pos)][us] << bonus * 145 / 128;
workerThread.minorPieceCorrectionHistory[minor_piece_index(pos)][us] << bonus * 151 / 128;
workerThread.nonPawnCorrectionHistory[non_pawn_index<WHITE>(pos)][WHITE][us] workerThread.nonPawnCorrectionHistory[non_pawn_index<WHITE>(pos)][WHITE][us]
<< bonus * nonPawnWeight / 128; << bonus * nonPawnWeight / 128;
workerThread.nonPawnCorrectionHistory[non_pawn_index<BLACK>(pos)][BLACK][us] workerThread.nonPawnCorrectionHistory[non_pawn_index<BLACK>(pos)][BLACK][us]
<< bonus * nonPawnWeight / 128; << bonus * nonPawnWeight / 128;
if (m.is_ok()) if (m.is_ok())
(*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()] {
<< bonus * 141 / 128; const Square to = m.to_sq();
const Piece pc = pos.piece_on(m.to_sq());
(*(ss - 2)->continuationCorrectionHistory)[pc][to] << bonus * 137 / 128;
(*(ss - 4)->continuationCorrectionHistory)[pc][to] << bonus * 64 / 128;
}
} }
// Add a small random component to draw evaluations to avoid 3-fold blindness // Add a small random component to draw evaluations to avoid 3-fold blindness
@@ -131,8 +138,7 @@ void update_all_stats(const Position& pos,
SearchedList& quietsSearched, SearchedList& quietsSearched,
SearchedList& capturesSearched, SearchedList& capturesSearched,
Depth depth, Depth depth,
Move TTMove, Move TTMove);
int moveCount);
} // namespace } // namespace
@@ -330,7 +336,7 @@ void Search::Worker::iterative_deepening() {
int searchAgainCounter = 0; int searchAgainCounter = 0;
lowPlyHistory.fill(86); lowPlyHistory.fill(97);
// Iterative deepening loop until requested to stop or the target depth is reached // Iterative deepening loop until requested to stop or the target depth is reached
while (++rootDepth < MAX_PLY && !threads.stop while (++rootDepth < MAX_PLY && !threads.stop
@@ -366,7 +372,7 @@ void Search::Worker::iterative_deepening() {
selDepth = 0; selDepth = 0;
// Reset aspiration window starting size // Reset aspiration window starting size
delta = 5 + std::abs(rootMoves[pvIdx].meanSquaredScore) / 11134; delta = 5 + threadIdx % 8 + std::abs(rootMoves[pvIdx].meanSquaredScore) / 9000;
Value avg = rootMoves[pvIdx].averageScore; Value avg = rootMoves[pvIdx].averageScore;
alpha = std::max(avg - delta, -VALUE_INFINITE); alpha = std::max(avg - delta, -VALUE_INFINITE);
beta = std::min(avg + delta, VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE);
@@ -416,7 +422,7 @@ void Search::Worker::iterative_deepening() {
// otherwise exit the loop. // otherwise exit the loop.
if (bestValue <= alpha) if (bestValue <= alpha)
{ {
beta = (alpha + beta) / 2; beta = alpha;
alpha = std::max(bestValue - delta, -VALUE_INFINITE); alpha = std::max(bestValue - delta, -VALUE_INFINITE);
failedHighCnt = 0; failedHighCnt = 0;
@@ -425,7 +431,8 @@ void Search::Worker::iterative_deepening() {
} }
else if (bestValue >= beta) else if (bestValue >= beta)
{ {
beta = std::min(bestValue + delta, VALUE_INFINITE); alpha = std::max(beta - delta, alpha);
beta = std::min(bestValue + delta, VALUE_INFINITE);
++failedHighCnt; ++failedHighCnt;
} }
else else
@@ -507,29 +514,29 @@ void Search::Worker::iterative_deepening() {
rootMoves[0].effort * 100000 / std::max(size_t(1), size_t(nodes)); rootMoves[0].effort * 100000 / std::max(size_t(1), size_t(nodes));
double fallingEval = double fallingEval =
(11.396 + 2.035 * (mainThread->bestPreviousAverageScore - bestValue) (11.325 + 2.115 * (mainThread->bestPreviousAverageScore - bestValue)
+ 0.968 * (mainThread->iterValue[iterIdx] - bestValue)) + 0.987 * (mainThread->iterValue[iterIdx] - bestValue))
/ 100.0; / 100.0;
fallingEval = std::clamp(fallingEval, 0.5786, 1.6752); fallingEval = std::clamp(fallingEval, 0.5688, 1.5698);
// If the bestMove is stable over several iterations, reduce time accordingly // If the bestMove is stable over several iterations, reduce time accordingly
double k = 0.527; double k = 0.5189;
double center = lastBestMoveDepth + 11; double center = lastBestMoveDepth + 11.57;
timeReduction = 0.8 + 0.84 / (1.077 + std::exp(-k * (completedDepth - center))); timeReduction = 0.723 + 0.79 / (1.104 + std::exp(-k * (completedDepth - center)));
double reduction = double reduction =
(1.4540 + mainThread->previousTimeReduction) / (2.1593 * timeReduction); (1.455 + mainThread->previousTimeReduction) / (2.2375 * timeReduction);
double bestMoveInstability = 0.9929 + 1.8519 * totBestMoveChanges / threads.size(); double bestMoveInstability = 1.04 + 1.8956 * totBestMoveChanges / threads.size();
double totalTime = double totalTime =
mainThread->tm.optimum() * fallingEval * reduction * bestMoveInstability; mainThread->tm.optimum() * fallingEval * reduction * bestMoveInstability;
// Cap used time in case of a single legal move for a better viewer experience // Cap used time in case of a single legal move for a better viewer experience
if (rootMoves.size() == 1) if (rootMoves.size() == 1)
totalTime = std::min(500.0, totalTime); totalTime = std::min(502.0, totalTime);
auto elapsedTime = elapsed(); auto elapsedTime = elapsed();
if (completedDepth >= 10 && nodesEffort >= 97056 && elapsedTime > totalTime * 0.6540 if (completedDepth >= 10 && nodesEffort >= 92425 && elapsedTime > totalTime * 0.666
&& !mainThread->ponder) && !mainThread->ponder)
threads.stop = true; threads.stop = true;
@@ -544,7 +551,7 @@ void Search::Worker::iterative_deepening() {
threads.stop = true; threads.stop = true;
} }
else else
threads.increaseDepth = mainThread->ponder || elapsedTime <= totalTime * 0.5138; threads.increaseDepth = mainThread->ponder || elapsedTime <= totalTime * 0.503;
} }
mainThread->iterValue[iterIdx] = bestValue; mainThread->iterValue[iterIdx] = bestValue;
@@ -564,14 +571,22 @@ void Search::Worker::iterative_deepening() {
} }
void Search::Worker::do_move(Position& pos, const Move move, StateInfo& st) { void Search::Worker::do_move(Position& pos, const Move move, StateInfo& st, Stack* const ss) {
do_move(pos, move, st, pos.gives_check(move)); do_move(pos, move, st, pos.gives_check(move), ss);
} }
void Search::Worker::do_move(Position& pos, const Move move, StateInfo& st, const bool givesCheck) { void Search::Worker::do_move(
DirtyPiece dp = pos.do_move(move, st, givesCheck, &tt); Position& pos, const Move move, StateInfo& st, const bool givesCheck, Stack* const ss) {
bool capture = pos.capture_stage(move);
DirtyPiece dp = pos.do_move(move, st, givesCheck, &tt);
nodes.fetch_add(1, std::memory_order_relaxed); nodes.fetch_add(1, std::memory_order_relaxed);
accumulatorStack.push(dp); accumulatorStack.push(dp);
if (ss != nullptr)
{
ss->currentMove = move;
ss->continuationHistory = &continuationHistory[ss->inCheck][capture][dp.pc][move.to_sq()];
ss->continuationCorrectionHistory = &continuationCorrectionHistory[dp.pc][move.to_sq()];
}
} }
void Search::Worker::do_null_move(Position& pos, StateInfo& st) { pos.do_null_move(st, tt); } void Search::Worker::do_null_move(Position& pos, StateInfo& st) { pos.do_null_move(st, tt); }
@@ -586,9 +601,9 @@ void Search::Worker::undo_null_move(Position& pos) { pos.undo_null_move(); }
// Reset histories, usually before a new game // Reset histories, usually before a new game
void Search::Worker::clear() { void Search::Worker::clear() {
mainHistory.fill(67); mainHistory.fill(68);
captureHistory.fill(-688); captureHistory.fill(-689);
pawnHistory.fill(-1287); pawnHistory.fill(-1238);
pawnCorrectionHistory.fill(5); pawnCorrectionHistory.fill(5);
minorPieceCorrectionHistory.fill(0); minorPieceCorrectionHistory.fill(0);
nonPawnCorrectionHistory.fill(0); nonPawnCorrectionHistory.fill(0);
@@ -603,10 +618,10 @@ void Search::Worker::clear() {
for (StatsType c : {NoCaptures, Captures}) for (StatsType c : {NoCaptures, Captures})
for (auto& to : continuationHistory[inCheck][c]) for (auto& to : continuationHistory[inCheck][c])
for (auto& h : to) for (auto& h : to)
h.fill(-473); h.fill(-529);
for (size_t i = 1; i < reductions.size(); ++i) for (size_t i = 1; i < reductions.size(); ++i)
reductions[i] = int(2796 / 128.0 * std::log(i)); reductions[i] = int(2809 / 128.0 * std::log(i));
refreshTable.clear(networks[numaAccessToken]); refreshTable.clear(networks[numaAccessToken]);
} }
@@ -623,10 +638,7 @@ Value Search::Worker::search(
// Dive into quiescence search when the depth reaches zero // Dive into quiescence search when the depth reaches zero
if (depth <= 0) if (depth <= 0)
{ return qsearch<PvNode ? PV : NonPV>(pos, ss, alpha, beta);
constexpr auto nt = PvNode ? PV : NonPV;
return qsearch<nt>(pos, ss, alpha, beta);
}
// Limit the depth if extensions made it too large // Limit the depth if extensions made it too large
depth = std::min(depth, MAX_PLY - 1); depth = std::min(depth, MAX_PLY - 1);
@@ -721,7 +733,10 @@ Value Search::Worker::search(
if (!PvNode && !excludedMove && ttData.depth > depth - (ttData.value <= beta) if (!PvNode && !excludedMove && ttData.depth > depth - (ttData.value <= beta)
&& is_valid(ttData.value) // Can happen when !ttHit or when access race in probe() && is_valid(ttData.value) // Can happen when !ttHit or when access race in probe()
&& (ttData.bound & (ttData.value >= beta ? BOUND_LOWER : BOUND_UPPER)) && (ttData.bound & (ttData.value >= beta ? BOUND_LOWER : BOUND_UPPER))
&& (cutNode == (ttData.value >= beta) || depth > 5)) && (cutNode == (ttData.value >= beta) || depth > 5)
// avoid a TT cutoff if the rule50 count is high and the TT move is zeroing
&& (depth > 8 || ttData.move == Move::none() || pos.rule50_count() < 80
|| (!ttCapture && type_of(pos.moved_piece(ttData.move)) != PAWN)))
{ {
// If ttMove is quiet, update move sorting heuristics on TT hit // If ttMove is quiet, update move sorting heuristics on TT hit
if (ttData.move && ttData.value >= beta) if (ttData.move && ttData.value >= beta)
@@ -729,24 +744,24 @@ Value Search::Worker::search(
// Bonus for a quiet ttMove that fails high // Bonus for a quiet ttMove that fails high
if (!ttCapture) if (!ttCapture)
update_quiet_histories(pos, ss, *this, ttData.move, update_quiet_histories(pos, ss, *this, ttData.move,
std::min(125 * depth - 77, 1157)); std::min(130 * depth - 71, 1043));
// Extra penalty for early quiet moves of the previous ply // Extra penalty for early quiet moves of the previous ply
if (prevSq != SQ_NONE && (ss - 1)->moveCount <= 3 && !priorCapture) if (prevSq != SQ_NONE && (ss - 1)->moveCount < 4 && !priorCapture)
update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -2301); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -2142);
} }
// Partial workaround for the graph history interaction problem // Partial workaround for the graph history interaction problem
// For high rule50 counts don't produce transposition table cutoffs. // For high rule50 counts don't produce transposition table cutoffs.
if (pos.rule50_count() < 90) if (pos.rule50_count() < 96)
{ {
if (depth >= 8 && ttData.move && pos.pseudo_legal(ttData.move) && pos.legal(ttData.move) if (depth >= 8 && ttData.move && pos.pseudo_legal(ttData.move) && pos.legal(ttData.move)
&& !is_decisive(ttData.value)) && !is_decisive(ttData.value))
{ {
do_move(pos, ttData.move, st); pos.do_move(ttData.move, st);
Key nextPosKey = pos.key(); Key nextPosKey = pos.key();
auto [ttHitNext, ttDataNext, ttWriterNext] = tt.probe(nextPosKey); auto [ttHitNext, ttDataNext, ttWriterNext] = tt.probe(nextPosKey);
undo_move(pos, ttData.move); pos.undo_move(ttData.move);
// Check that the ttValue after the tt move would also trigger a cutoff // Check that the ttValue after the tt move would also trigger a cutoff
if (!is_valid(ttDataNext.value)) if (!is_valid(ttDataNext.value))
@@ -849,61 +864,60 @@ Value Search::Worker::search(
} }
// Use static evaluation difference to improve quiet move ordering // Use static evaluation difference to improve quiet move ordering
if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture && !ttHit) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture)
{ {
int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1858, 1492) + 661; int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -2023, 1563) + 583;
mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus * 1057 / 1024; mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus * 944 / 1024;
if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) if (!ttHit && type_of(pos.piece_on(prevSq)) != PAWN
pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] && ((ss - 1)->currentMove).type_of() != PROMOTION)
<< bonus * 1266 / 1024; pawnHistory[pawn_history_index(pos)][pos.piece_on(prevSq)][prevSq]
<< bonus * 1438 / 1024;
} }
// Set up the improving flag, which is true if current static evaluation is // Set up the improving flag, which is true if current static evaluation is
// bigger than the previous static evaluation at our turn (if we were in // bigger than the previous static evaluation at our turn (if we were in
// check at our previous move we go back until we weren't in check) and is // check at our previous move we go back until we weren't in check) and is
// false otherwise. The improving flag is used in various pruning heuristics. // false otherwise. The improving flag is used in various pruning heuristics.
improving = ss->staticEval > (ss - 2)->staticEval; improving = ss->staticEval > (ss - 2)->staticEval;
opponentWorsening = ss->staticEval > -(ss - 1)->staticEval; opponentWorsening = ss->staticEval > -(ss - 1)->staticEval;
if (priorReduction >= 3 && !opponentWorsening) if (priorReduction >= 3 && !opponentWorsening)
depth++; depth++;
if (priorReduction >= 1 && depth >= 2 && ss->staticEval + (ss - 1)->staticEval > 175) if (priorReduction >= 2 && depth >= 2 && ss->staticEval + (ss - 1)->staticEval > 173)
depth--; depth--;
// Step 7. Razoring // Step 7. Razoring
// If eval is really low, skip search entirely and return the qsearch value. // If eval is really low, skip search entirely and return the qsearch value.
// For PvNodes, we must have a guard against mates being returned. // For PvNodes, we must have a guard against mates being returned.
if (!PvNode && eval < alpha - 486 - 325 * depth * depth) if (!PvNode && eval < alpha - 514 - 294 * depth * depth)
return qsearch<NonPV>(pos, ss, alpha, beta); return qsearch<NonPV>(pos, ss, alpha, beta);
// Step 8. Futility pruning: child node // Step 8. Futility pruning: child node
// The depth condition is important for mate finding. // The depth condition is important for mate finding.
{ {
auto futility_margin = [&](Depth d) { auto futility_margin = [&](Depth d) {
Value futilityMult = 93 - 20 * (cutNode && !ss->ttHit); Value futilityMult = 91 - 21 * !ss->ttHit;
return futilityMult * d // return futilityMult * d //
- improving * futilityMult * 2 // - 2094 * improving * futilityMult / 1024 //
- opponentWorsening * futilityMult / 3 // - 1324 * opponentWorsening * futilityMult / 4096 //
+ (ss - 1)->statScore / 376 // + (ss - 1)->statScore / 331 //
+ std::abs(correctionValue) / 168639; + std::abs(correctionValue) / 158105;
}; };
if (!ss->ttPv && depth < 14 && eval - futility_margin(depth) >= beta && eval >= beta if (!ss->ttPv && depth < 14 && eval - futility_margin(depth) >= beta && eval >= beta
&& (!ttData.move || ttCapture) && !is_loss(beta) && !is_win(eval)) && (!ttData.move || ttCapture) && !is_loss(beta) && !is_win(eval))
return beta + (eval - beta) / 3; return (2 * beta + eval) / 3;
} }
// Step 9. Null move search with verification search // Step 9. Null move search with verification search
if (cutNode && (ss - 1)->currentMove != Move::null() && eval >= beta if (cutNode && ss->staticEval >= beta - 18 * depth + 390 && !excludedMove
&& ss->staticEval >= beta - 19 * depth + 389 && !excludedMove && pos.non_pawn_material(us) && pos.non_pawn_material(us) && ss->ply >= nmpMinPly && !is_loss(beta))
&& ss->ply >= nmpMinPly && !is_loss(beta))
{ {
assert(eval - beta >= 0); assert((ss - 1)->currentMove != Move::null());
// Null move dynamic reduction based on depth // Null move dynamic reduction based on depth
Depth R = 7 + depth / 3; Depth R = 6 + depth / 3;
ss->currentMove = Move::null(); ss->currentMove = Move::null();
ss->continuationHistory = &continuationHistory[0][0][NO_PIECE][0]; ss->continuationHistory = &continuationHistory[0][0][NO_PIECE][0];
@@ -936,18 +950,18 @@ Value Search::Worker::search(
} }
} }
improving |= ss->staticEval >= beta + 94; improving |= ss->staticEval >= beta;
// Step 10. Internal iterative reductions // Step 10. Internal iterative reductions
// For PV nodes without a ttMove as well as for deep enough cutNodes, we decrease depth. // At sufficient depth, reduce depth for PV/Cut nodes without a TTMove.
// (*Scaler) Especially if they make IIR less aggressive. // (*Scaler) Especially if they make IIR less aggressive.
if (!allNode && depth >= 6 && !ttData.move) if (!allNode && depth >= 6 && !ttData.move && priorReduction <= 3)
depth--; depth--;
// Step 11. ProbCut // Step 11. ProbCut
// If we have a good enough capture (or queen promotion) and a reduced search // If we have a good enough capture (or queen promotion) and a reduced search
// returns a value much above beta, we can (almost) safely prune the previous move. // returns a value much above beta, we can (almost) safely prune the previous move.
probCutBeta = beta + 201 - 58 * improving; probCutBeta = beta + 224 - 64 * improving;
if (depth >= 3 if (depth >= 3
&& !is_decisive(beta) && !is_decisive(beta)
// If value from transposition table is lower than probCutBeta, don't attempt // If value from transposition table is lower than probCutBeta, don't attempt
@@ -957,7 +971,7 @@ Value Search::Worker::search(
assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta); assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta);
MovePicker mp(pos, ttData.move, probCutBeta - ss->staticEval, &captureHistory); MovePicker mp(pos, ttData.move, probCutBeta - ss->staticEval, &captureHistory);
Depth probCutDepth = std::max(depth - 5, 0); Depth probCutDepth = std::clamp(depth - 5 - (ss->staticEval - beta) / 306, 0, depth);
while ((move = mp.next_move()) != Move::none()) while ((move = mp.next_move()) != Move::none())
{ {
@@ -968,15 +982,7 @@ Value Search::Worker::search(
assert(pos.capture_stage(move)); assert(pos.capture_stage(move));
movedPiece = pos.moved_piece(move); do_move(pos, move, st, ss);
do_move(pos, move, st);
ss->currentMove = move;
ss->continuationHistory =
&continuationHistory[ss->inCheck][true][movedPiece][move.to_sq()];
ss->continuationCorrectionHistory =
&continuationCorrectionHistory[movedPiece][move.to_sq()];
// Perform a preliminary qsearch to verify that the move holds // Perform a preliminary qsearch to verify that the move holds
value = -qsearch<NonPV>(pos, ss + 1, -probCutBeta, -probCutBeta + 1); value = -qsearch<NonPV>(pos, ss + 1, -probCutBeta, -probCutBeta + 1);
@@ -1004,7 +1010,7 @@ Value Search::Worker::search(
moves_loop: // When in check, search starts here moves_loop: // When in check, search starts here
// Step 12. A small Probcut idea // Step 12. A small Probcut idea
probCutBeta = beta + 400; probCutBeta = beta + 418;
if ((ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 4 && ttData.value >= probCutBeta if ((ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 4 && ttData.value >= probCutBeta
&& !is_decisive(beta) && is_valid(ttData.value) && !is_decisive(ttData.value)) && !is_decisive(beta) && is_valid(ttData.value) && !is_decisive(ttData.value))
return probCutBeta; return probCutBeta;
@@ -1055,8 +1061,6 @@ moves_loop: // When in check, search starts here
movedPiece = pos.moved_piece(move); movedPiece = pos.moved_piece(move);
givesCheck = pos.gives_check(move); givesCheck = pos.gives_check(move);
(ss + 1)->quietMoveStreak = (!capture && !givesCheck) ? (ss->quietMoveStreak + 1) : 0;
// Calculate new depth for this move // Calculate new depth for this move
newDepth = depth - 1; newDepth = depth - 1;
@@ -1068,7 +1072,7 @@ moves_loop: // When in check, search starts here
// Smaller or even negative value is better for short time controls // Smaller or even negative value is better for short time controls
// Bigger value is better for long time controls // Bigger value is better for long time controls
if (ss->ttPv) if (ss->ttPv)
r += 968; r += 946;
// Step 14. Pruning at shallow depth. // Step 14. Pruning at shallow depth.
// Depth conditions are important for mate finding. // Depth conditions are important for mate finding.
@@ -1087,52 +1091,44 @@ moves_loop: // When in check, search starts here
int captHist = captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)]; int captHist = captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)];
// Futility pruning for captures // Futility pruning for captures
if (!givesCheck && lmrDepth < 7 && !ss->inCheck) if (!givesCheck && lmrDepth < 7)
{ {
Value futilityValue = ss->staticEval + 232 + 224 * lmrDepth Value futilityValue = ss->staticEval + 231 + 211 * lmrDepth
+ PieceValue[capturedPiece] + 131 * captHist / 1024; + PieceValue[capturedPiece] + 130 * captHist / 1024;
if (futilityValue <= alpha) if (futilityValue <= alpha)
continue; continue;
} }
// SEE based pruning for captures and checks // SEE based pruning for captures and checks
int seeHist = std::clamp(captHist / 31, -137 * depth, 125 * depth); // Avoid pruning sacrifices of our last piece for stalemate
if (!pos.see_ge(move, -158 * depth - seeHist)) int margin = std::max(157 * depth + captHist / 29, 0);
{ if ((alpha >= VALUE_DRAW || pos.non_pawn_material(us) != PieceValue[movedPiece])
bool mayStalemateTrap = && !pos.see_ge(move, -margin))
depth > 2 && alpha < 0 && pos.non_pawn_material(us) == PieceValue[movedPiece] continue;
&& PieceValue[movedPiece] >= RookValue
// it can't be stalemate if we moved a piece adjacent to the king
&& !(attacks_bb<KING>(pos.square<KING>(us)) & move.from_sq())
&& !mp.can_move_king_or_pawn();
// avoid pruning sacrifices of our last piece for stalemate
if (!mayStalemateTrap)
continue;
}
} }
else else
{ {
int history = (*contHist[0])[movedPiece][move.to_sq()] int history = (*contHist[0])[movedPiece][move.to_sq()]
+ (*contHist[1])[movedPiece][move.to_sq()] + (*contHist[1])[movedPiece][move.to_sq()]
+ pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; + pawnHistory[pawn_history_index(pos)][movedPiece][move.to_sq()];
// Continuation history based pruning // Continuation history based pruning
if (history < -4229 * depth) if (history < -4312 * depth)
continue; continue;
history += 68 * mainHistory[us][move.from_to()] / 32; history += 76 * mainHistory[us][move.from_to()] / 32;
lmrDepth += history / 3388; // (*Scaler): Generally, a lower divisor scales well
lmrDepth += history / 3220;
Value baseFutility = (bestMove ? 46 : 230); Value futilityValue = ss->staticEval + 47 + 171 * !bestMove + 134 * lmrDepth
Value futilityValue = + 90 * (ss->staticEval > alpha);
ss->staticEval + baseFutility + 117 * lmrDepth + 102 * (ss->staticEval > alpha);
// Futility pruning: parent node // Futility pruning: parent node
// (*Scaler): Generally, more frequent futility pruning // (*Scaler): Generally, more frequent futility pruning
// scales well with respect to time and threads // scales well with respect to time and threads
if (!ss->inCheck && lmrDepth < 12 && futilityValue <= alpha) if (!ss->inCheck && lmrDepth < 11 && futilityValue <= alpha)
{ {
if (bestValue <= futilityValue && !is_decisive(bestValue) if (bestValue <= futilityValue && !is_decisive(bestValue)
&& !is_win(futilityValue)) && !is_win(futilityValue))
@@ -1159,12 +1155,11 @@ moves_loop: // When in check, search starts here
// (*Scaler) Generally, higher singularBeta (i.e closer to ttValue) // (*Scaler) Generally, higher singularBeta (i.e closer to ttValue)
// and lower extension margins scale well. // and lower extension margins scale well.
if (!rootNode && move == ttData.move && !excludedMove if (!rootNode && move == ttData.move && !excludedMove && depth >= 6 + ss->ttPv
&& depth >= 6 - (completedDepth > 27) + ss->ttPv && is_valid(ttData.value) && is_valid(ttData.value) && !is_decisive(ttData.value) && (ttData.bound & BOUND_LOWER)
&& !is_decisive(ttData.value) && (ttData.bound & BOUND_LOWER)
&& ttData.depth >= depth - 3) && ttData.depth >= depth - 3)
{ {
Value singularBeta = ttData.value - (58 + 76 * (ss->ttPv && !PvNode)) * depth / 57; Value singularBeta = ttData.value - (56 + 81 * (ss->ttPv && !PvNode)) * depth / 60;
Depth singularDepth = newDepth / 2; Depth singularDepth = newDepth / 2;
ss->excludedMove = move; ss->excludedMove = move;
@@ -1173,11 +1168,11 @@ moves_loop: // When in check, search starts here
if (value < singularBeta) if (value < singularBeta)
{ {
int corrValAdj = std::abs(correctionValue) / 248400; int corrValAdj = std::abs(correctionValue) / 229958;
int doubleMargin = -4 + 244 * PvNode - 206 * !ttCapture - corrValAdj int doubleMargin = -4 + 198 * PvNode - 212 * !ttCapture - corrValAdj
- 997 * ttMoveHistory / 131072 - (ss->ply > rootDepth) * 47; - 921 * ttMoveHistory / 127649 - (ss->ply > rootDepth) * 45;
int tripleMargin = 84 + 269 * PvNode - 253 * !ttCapture + 91 * ss->ttPv - corrValAdj int tripleMargin = 76 + 308 * PvNode - 250 * !ttCapture + 92 * ss->ttPv - corrValAdj
- (ss->ply * 2 > rootDepth * 3) * 54; - (ss->ply * 2 > rootDepth * 3) * 52;
extension = extension =
1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin); 1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin);
@@ -1192,7 +1187,10 @@ moves_loop: // When in check, search starts here
// singular (multiple moves fail high), and we can prune the whole // singular (multiple moves fail high), and we can prune the whole
// subtree by returning a softbound. // subtree by returning a softbound.
else if (value >= beta && !is_decisive(value)) else if (value >= beta && !is_decisive(value))
{
ttMoveHistory << std::max(-400 - 100 * depth, -4000);
return value; return value;
}
// Negative extensions // Negative extensions
// If other moves failed high over (ttValue - margin) without the // If other moves failed high over (ttValue - margin) without the
@@ -1212,50 +1210,41 @@ moves_loop: // When in check, search starts here
} }
// Step 16. Make the move // Step 16. Make the move
do_move(pos, move, st, givesCheck); do_move(pos, move, st, givesCheck, ss);
// Add extension to new depth // Add extension to new depth
newDepth += extension; newDepth += extension;
// Update the current move (this must be done after singular extension search)
ss->currentMove = move;
ss->continuationHistory =
&continuationHistory[ss->inCheck][capture][movedPiece][move.to_sq()];
ss->continuationCorrectionHistory =
&continuationCorrectionHistory[movedPiece][move.to_sq()];
uint64_t nodeCount = rootNode ? uint64_t(nodes) : 0; uint64_t nodeCount = rootNode ? uint64_t(nodes) : 0;
// Decrease reduction for PvNodes (*Scaler) // Decrease reduction for PvNodes (*Scaler)
if (ss->ttPv) if (ss->ttPv)
r -= 2437 + PvNode * 926 + (ttData.value > alpha) * 901 r -= 2618 + PvNode * 991 + (ttData.value > alpha) * 903
+ (ttData.depth >= depth) * (943 + cutNode * 1180); + (ttData.depth >= depth) * (978 + cutNode * 1051);
// These reduction adjustments have no proven non-linear scaling // These reduction adjustments have no proven non-linear scaling
r += 650; // Base reduction offset to compensate for other tweaks r += 843; // Base reduction offset to compensate for other tweaks
r -= moveCount * 66; r -= moveCount * 66;
r -= std::abs(correctionValue) / 28047; r -= std::abs(correctionValue) / 30450;
// Increase reduction for cut nodes // Increase reduction for cut nodes
if (cutNode) if (cutNode)
r += 2864 + 966 * !ttData.move; r += 3094 + 1056 * !ttData.move;
// Increase reduction if ttMove is a capture // Increase reduction if ttMove is a capture
if (ttCapture) if (ttCapture)
r += 1350; r += 1415;
// Increase reduction if next ply has a lot of fail high // Increase reduction if next ply has a lot of fail high
if ((ss + 1)->cutoffCnt > 2) if ((ss + 1)->cutoffCnt > 2)
r += 1036 + allNode * 848; r += 1051 + allNode * 814;
r += (ss + 1)->quietMoveStreak * 50;
// For first picked move (ttMove) reduce reduction // For first picked move (ttMove) reduce reduction
if (move == ttData.move) if (move == ttData.move)
r -= 2006; r -= 2018;
if (capture) if (capture)
ss->statScore = 826 * int(PieceValue[pos.captured_piece()]) / 128 ss->statScore = 803 * int(PieceValue[pos.captured_piece()]) / 128
+ captureHistory[movedPiece][move.to_sq()][type_of(pos.captured_piece())]; + captureHistory[movedPiece][move.to_sq()][type_of(pos.captured_piece())];
else else
ss->statScore = 2 * mainHistory[us][move.from_to()] ss->statScore = 2 * mainHistory[us][move.from_to()]
@@ -1263,7 +1252,7 @@ moves_loop: // When in check, search starts here
+ (*contHist[1])[movedPiece][move.to_sq()]; + (*contHist[1])[movedPiece][move.to_sq()];
// Decrease/increase reduction for moves with a good/bad history // Decrease/increase reduction for moves with a good/bad history
r -= ss->statScore * 826 / 8192; r -= ss->statScore * 794 / 8192;
// Step 17. Late moves reduction / extension (LMR) // Step 17. Late moves reduction / extension (LMR)
if (depth >= 2 && moveCount > 1) if (depth >= 2 && moveCount > 1)
@@ -1273,9 +1262,7 @@ moves_loop: // When in check, search starts here
// beyond the first move depth. // beyond the first move depth.
// To prevent problems when the max value is less than the min value, // To prevent problems when the max value is less than the min value,
// std::clamp has been replaced by a more robust implementation. // std::clamp has been replaced by a more robust implementation.
Depth d = std::max(1, std::min(newDepth - r / 1024, Depth d = std::max(1, std::min(newDepth - r / 1024, newDepth + 2)) + PvNode;
newDepth + !allNode + (PvNode && !bestMove)))
+ PvNode;
ss->reduction = newDepth - d; ss->reduction = newDepth - d;
value = -search<NonPV>(pos, ss + 1, -(alpha + 1), -alpha, d, true); value = -search<NonPV>(pos, ss + 1, -(alpha + 1), -alpha, d, true);
@@ -1284,11 +1271,11 @@ moves_loop: // When in check, search starts here
// Do a full-depth search when reduced LMR search fails high // Do a full-depth search when reduced LMR search fails high
// (*Scaler) Usually doing more shallower searches // (*Scaler) Usually doing more shallower searches
// doesn't scale well to longer TCs // doesn't scale well to longer TCs
if (value > alpha && d < newDepth) if (value > alpha)
{ {
// Adjust full-depth search based on LMR results - if the result was // Adjust full-depth search based on LMR results - if the result was
// good enough search deeper, if it was bad enough search shallower. // good enough search deeper, if it was bad enough search shallower.
const bool doDeeperSearch = value > (bestValue + 42 + 2 * newDepth); const bool doDeeperSearch = d < newDepth && value > (bestValue + 43 + 2 * newDepth);
const bool doShallowerSearch = value < bestValue + 9; const bool doShallowerSearch = value < bestValue + 9;
newDepth += doDeeperSearch - doShallowerSearch; newDepth += doDeeperSearch - doShallowerSearch;
@@ -1297,10 +1284,8 @@ moves_loop: // When in check, search starts here
value = -search<NonPV>(pos, ss + 1, -(alpha + 1), -alpha, newDepth, !cutNode); value = -search<NonPV>(pos, ss + 1, -(alpha + 1), -alpha, newDepth, !cutNode);
// Post LMR continuation history updates // Post LMR continuation history updates
update_continuation_histories(ss, movedPiece, move.to_sq(), 1508); update_continuation_histories(ss, movedPiece, move.to_sq(), 1365);
} }
else if (value > alpha && value < bestValue + 9)
newDepth--;
} }
// Step 18. Full-depth search when LMR is skipped // Step 18. Full-depth search when LMR is skipped
@@ -1308,11 +1293,11 @@ moves_loop: // When in check, search starts here
{ {
// Increase reduction if ttMove is not present // Increase reduction if ttMove is not present
if (!ttData.move) if (!ttData.move)
r += 1128; r += 1118;
// Note that if expected reduction is high, we reduce search depth here // Note that if expected reduction is high, we reduce search depth here
value = -search<NonPV>(pos, ss + 1, -(alpha + 1), -alpha, value = -search<NonPV>(pos, ss + 1, -(alpha + 1), -alpha,
newDepth - (r > 3200) - (r > 4600 && newDepth > 2), !cutNode); newDepth - (r > 3212) - (r > 4784 && newDepth > 2), !cutNode);
} }
// For PV nodes only, do a full PV search on the first move or after a fail high, // For PV nodes only, do a full PV search on the first move or after a fail high,
@@ -1323,7 +1308,7 @@ moves_loop: // When in check, search starts here
(ss + 1)->pv[0] = Move::none(); (ss + 1)->pv[0] = Move::none();
// Extend move from transposition table if we are about to dive into qsearch. // Extend move from transposition table if we are about to dive into qsearch.
if (move == ttData.move && rootDepth > 8) if (move == ttData.move && ttData.depth > 1 && rootDepth > 8)
newDepth = std::max(newDepth, 1); newDepth = std::max(newDepth, 1);
value = -search<PV>(pos, ss + 1, -beta, -alpha, newDepth, false); value = -search<PV>(pos, ss + 1, -beta, -alpha, newDepth, false);
@@ -1394,7 +1379,7 @@ moves_loop: // When in check, search starts here
// In case we have an alternative move equal in eval to the current bestmove, // In case we have an alternative move equal in eval to the current bestmove,
// promote it to bestmove by pretending it just exceeds alpha (but not beta). // promote it to bestmove by pretending it just exceeds alpha (but not beta).
int inc = (value == bestValue && ss->ply + 2 >= rootDepth && (int(nodes) & 15) == 0 int inc = (value == bestValue && ss->ply + 2 >= rootDepth && (int(nodes) & 14) == 0
&& !is_win(std::abs(value) + 1)); && !is_win(std::abs(value) + 1));
if (value + inc > bestValue) if (value + inc > bestValue)
@@ -1410,14 +1395,14 @@ moves_loop: // When in check, search starts here
if (value >= beta) if (value >= beta)
{ {
// (* Scaler) Especially if they make cutoffCnt increment more often. // (*Scaler) Especially if they make cutoffCnt increment more often.
ss->cutoffCnt += (extension < 2) || PvNode; ss->cutoffCnt += (extension < 2) || PvNode;
assert(value >= beta); // Fail high assert(value >= beta); // Fail high
break; break;
} }
// Reduce other moves if we have found at least one score improvement // Reduce other moves if we have found at least one score improvement
if (depth > 2 && depth < 16 && !is_decisive(value)) if (depth > 2 && depth < 14 && !is_decisive(value))
depth -= 2; depth -= 2;
assert(depth > 0); assert(depth > 0);
@@ -1455,33 +1440,33 @@ moves_loop: // When in check, search starts here
else if (bestMove) else if (bestMove)
{ {
update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, capturesSearched, depth, update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, capturesSearched, depth,
ttData.move, moveCount); ttData.move);
if (!PvNode) if (!PvNode)
ttMoveHistory << (bestMove == ttData.move ? 800 : -879); ttMoveHistory << (bestMove == ttData.move ? 809 : -865);
} }
// Bonus for prior quiet countermove that caused the fail low // Bonus for prior quiet countermove that caused the fail low
else if (!priorCapture && prevSq != SQ_NONE) else if (!priorCapture && prevSq != SQ_NONE)
{ {
int bonusScale = -220; int bonusScale = -228;
bonusScale += std::min(-(ss - 1)->statScore / 103, 323); bonusScale -= (ss - 1)->statScore / 104;
bonusScale += std::min(73 * depth, 531); bonusScale += std::min(63 * depth, 508);
bonusScale += 174 * ((ss - 1)->moveCount > 8); bonusScale += 184 * ((ss - 1)->moveCount > 8);
bonusScale += 144 * (!ss->inCheck && bestValue <= ss->staticEval - 104); bonusScale += 143 * (!ss->inCheck && bestValue <= ss->staticEval - 92);
bonusScale += 128 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 82); bonusScale += 149 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 70);
bonusScale = std::max(bonusScale, 0); bonusScale = std::max(bonusScale, 0);
const int scaledBonus = std::min(159 * depth - 94, 1501) * bonusScale; const int scaledBonus = std::min(144 * depth - 92, 1365) * bonusScale;
update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq,
scaledBonus * 412 / 32768); scaledBonus * 400 / 32768);
mainHistory[~us][((ss - 1)->currentMove).from_to()] << scaledBonus * 203 / 32768; mainHistory[~us][((ss - 1)->currentMove).from_to()] << scaledBonus * 220 / 32768;
if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION)
pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] pawnHistory[pawn_history_index(pos)][pos.piece_on(prevSq)][prevSq]
<< scaledBonus * 1040 / 32768; << scaledBonus * 1164 / 32768;
} }
// Bonus for prior capture countermove that caused the fail low // Bonus for prior capture countermove that caused the fail low
@@ -1489,7 +1474,7 @@ moves_loop: // When in check, search starts here
{ {
Piece capturedPiece = pos.captured_piece(); Piece capturedPiece = pos.captured_piece();
assert(capturedPiece != NO_PIECE); assert(capturedPiece != NO_PIECE);
captureHistory[pos.piece_on(prevSq)][prevSq][type_of(capturedPiece)] << 1080; captureHistory[pos.piece_on(prevSq)][prevSq][type_of(capturedPiece)] << 964;
} }
if (PvNode) if (PvNode)
@@ -1516,9 +1501,11 @@ moves_loop: // When in check, search starts here
&& ((bestValue < ss->staticEval && bestValue < beta) // negative correction & no fail high && ((bestValue < ss->staticEval && bestValue < beta) // negative correction & no fail high
|| (bestValue > ss->staticEval && bestMove))) // positive correction & no fail low || (bestValue > ss->staticEval && bestMove))) // positive correction & no fail low
{ {
auto bonus = std::clamp(int(bestValue - ss->staticEval) * depth / 8, auto bonus =
-CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); std::clamp(int(bestValue - ss->staticEval) * depth / (8 + (bestValue > ss->staticEval)),
update_correction_history(pos, ss, *this, bonus); -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4);
update_correction_history(pos, ss, *this,
(1088 - 180 * (bestValue > ss->staticEval)) * bonus / 1024);
} }
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
@@ -1642,7 +1629,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
if (bestValue > alpha) if (bestValue > alpha)
alpha = bestValue; alpha = bestValue;
futilityBase = ss->staticEval + 376; futilityBase = ss->staticEval + 352;
} }
const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory,
@@ -1699,29 +1686,17 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
} }
} }
// Continuation history based pruning // Skip non-captures
if (!capture if (!capture)
&& (*contHist[0])[pos.moved_piece(move)][move.to_sq()]
+ pawnHistory[pawn_structure_index(pos)][pos.moved_piece(move)][move.to_sq()]
<= 6218)
continue; continue;
// Do not search moves with bad enough SEE values // Do not search moves with bad enough SEE values
if (!pos.see_ge(move, -74)) if (!pos.see_ge(move, -78))
continue; continue;
} }
// Step 7. Make and search the move // Step 7. Make and search the move
Piece movedPiece = pos.moved_piece(move); do_move(pos, move, st, givesCheck, ss);
do_move(pos, move, st, givesCheck);
// Update the current move
ss->currentMove = move;
ss->continuationHistory =
&continuationHistory[ss->inCheck][capture][movedPiece][move.to_sq()];
ss->continuationCorrectionHistory =
&continuationCorrectionHistory[movedPiece][move.to_sq()];
value = -qsearch<nodeType>(pos, ss + 1, -beta, -alpha); value = -qsearch<nodeType>(pos, ss + 1, -beta, -alpha);
undo_move(pos, move); undo_move(pos, move);
@@ -1789,7 +1764,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) const { Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) const {
int reductionScale = reductions[d] * reductions[mn]; int reductionScale = reductions[d] * reductions[mn];
return reductionScale - delta * 794 / rootDelta + !i * reductionScale * 205 / 512 + 1086; return reductionScale - delta * 757 / rootDelta + !i * reductionScale * 218 / 512 + 1200;
} }
// elapsed() returns the time elapsed since the search started. If the // elapsed() returns the time elapsed since the search started. If the
@@ -1878,42 +1853,41 @@ void update_all_stats(const Position& pos,
SearchedList& quietsSearched, SearchedList& quietsSearched,
SearchedList& capturesSearched, SearchedList& capturesSearched,
Depth depth, Depth depth,
Move ttMove, Move ttMove) {
int moveCount) {
CapturePieceToHistory& captureHistory = workerThread.captureHistory; CapturePieceToHistory& captureHistory = workerThread.captureHistory;
Piece movedPiece = pos.moved_piece(bestMove); Piece movedPiece = pos.moved_piece(bestMove);
PieceType capturedPiece; PieceType capturedPiece;
int bonus = std::min(143 * depth - 89, 1496) + 302 * (bestMove == ttMove); int bonus = std::min(151 * depth - 91, 1730) + 302 * (bestMove == ttMove);
int malus = std::min(737 * depth - 179, 3141) - 30 * moveCount; int malus = std::min(951 * depth - 156, 2468) - 30 * quietsSearched.size();
if (!pos.capture_stage(bestMove)) if (!pos.capture_stage(bestMove))
{ {
update_quiet_histories(pos, ss, workerThread, bestMove, bonus * 1059 / 1024); update_quiet_histories(pos, ss, workerThread, bestMove, bonus * 957 / 1024);
// Decrease stats for all non-best quiet moves // Decrease stats for all non-best quiet moves
for (Move move : quietsSearched) for (Move move : quietsSearched)
update_quiet_histories(pos, ss, workerThread, move, -malus * 1310 / 1024); update_quiet_histories(pos, ss, workerThread, move, -malus);
} }
else else
{ {
// Increase stats for the best move in case it was a capture move // Increase stats for the best move in case it was a capture move
capturedPiece = type_of(pos.piece_on(bestMove.to_sq())); capturedPiece = type_of(pos.piece_on(bestMove.to_sq()));
captureHistory[movedPiece][bestMove.to_sq()][capturedPiece] << bonus * 1213 / 1024; captureHistory[movedPiece][bestMove.to_sq()][capturedPiece] << bonus;
} }
// Extra penalty for a quiet early move that was not a TT move in // Extra penalty for a quiet early move that was not a TT move in
// previous ply when it gets refuted. // previous ply when it gets refuted.
if (prevSq != SQ_NONE && ((ss - 1)->moveCount == 1 + (ss - 1)->ttHit) && !pos.captured_piece()) if (prevSq != SQ_NONE && ((ss - 1)->moveCount == 1 + (ss - 1)->ttHit) && !pos.captured_piece())
update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -malus * 580 / 1024); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -malus * 503 / 1024);
// Decrease stats for all non-best capture moves // Decrease stats for all non-best capture moves
for (Move move : capturesSearched) for (Move move : capturesSearched)
{ {
movedPiece = pos.moved_piece(move); movedPiece = pos.moved_piece(move);
capturedPiece = type_of(pos.piece_on(move.to_sq())); capturedPiece = type_of(pos.piece_on(move.to_sq()));
captureHistory[movedPiece][move.to_sq()][capturedPiece] << -malus * 1388 / 1024; captureHistory[movedPiece][move.to_sq()][capturedPiece] << -malus * 1157 / 1024;
} }
} }
@@ -1922,7 +1896,7 @@ void update_all_stats(const Position& pos,
// at ply -1, -2, -3, -4, and -6 with current move. // at ply -1, -2, -3, -4, and -6 with current move.
void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) {
static constexpr std::array<ConthistBonus, 6> conthist_bonuses = { static constexpr std::array<ConthistBonus, 6> conthist_bonuses = {
{{1, 1092}, {2, 631}, {3, 294}, {4, 517}, {5, 126}, {6, 445}}}; {{1, 1157}, {2, 648}, {3, 288}, {4, 576}, {5, 140}, {6, 441}}};
for (const auto [i, weight] : conthist_bonuses) for (const auto [i, weight] : conthist_bonuses)
{ {
@@ -1930,7 +1904,7 @@ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) {
if (ss->inCheck && i > 2) if (ss->inCheck && i > 2)
break; break;
if (((ss - i)->currentMove).is_ok()) if (((ss - i)->currentMove).is_ok())
(*(ss - i)->continuationHistory)[pc][to] << bonus * weight / 1024; (*(ss - i)->continuationHistory)[pc][to] << (bonus * weight / 1024) + 88 * (i < 2);
} }
} }
@@ -1943,14 +1917,13 @@ void update_quiet_histories(
workerThread.mainHistory[us][move.from_to()] << bonus; // Untuned to prevent duplicate effort workerThread.mainHistory[us][move.from_to()] << bonus; // Untuned to prevent duplicate effort
if (ss->ply < LOW_PLY_HISTORY_SIZE) if (ss->ply < LOW_PLY_HISTORY_SIZE)
workerThread.lowPlyHistory[ss->ply][move.from_to()] << bonus * 792 / 1024; workerThread.lowPlyHistory[ss->ply][move.from_to()] << bonus * 761 / 1024;
update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus * 955 / 1024);
bonus * (bonus > 0 ? 1082 : 784) / 1024);
int pIndex = pawn_structure_index(pos); int pIndex = pawn_history_index(pos);
workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()]
<< bonus * (bonus > 0 ? 705 : 450) / 1024; << (bonus * (bonus > 0 ? 800 : 500) / 1024) + 70;
} }
} }
+3 -3
View File
@@ -77,7 +77,6 @@ struct Stack {
bool ttHit; bool ttHit;
int cutoffCnt; int cutoffCnt;
int reduction; int reduction;
int quietMoveStreak;
}; };
@@ -331,8 +330,9 @@ class Worker {
private: private:
void iterative_deepening(); void iterative_deepening();
void do_move(Position& pos, const Move move, StateInfo& st); void do_move(Position& pos, const Move move, StateInfo& st, Stack* const ss);
void do_move(Position& pos, const Move move, StateInfo& st, const bool givesCheck); void
do_move(Position& pos, const Move move, StateInfo& st, const bool givesCheck, Stack* const ss);
void do_null_move(Position& pos, StateInfo& st); void do_null_move(Position& pos, StateInfo& st);
void undo_move(Position& pos, const Move move); void undo_move(Position& pos, const Move move);
void undo_null_move(Position& pos); void undo_null_move(Position& pos);
+10 -1
View File
@@ -170,11 +170,20 @@ class Tune {
static OptionsMap* options; static OptionsMap* options;
}; };
template<typename... Args>
constexpr void tune_check_args(Args&&...) {
static_assert((!std::is_fundamental_v<Args> && ...), "TUNE macro arguments wrong");
}
// Some macro magic :-) we define a dummy int variable that the compiler initializes calling Tune::add() // Some macro magic :-) we define a dummy int variable that the compiler initializes calling Tune::add()
#define STRINGIFY(x) #x #define STRINGIFY(x) #x
#define UNIQUE2(x, y) x##y #define UNIQUE2(x, y) x##y
#define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__ #define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__
#define TUNE(...) int UNIQUE(p, __LINE__) = Tune::add(STRINGIFY((__VA_ARGS__)), __VA_ARGS__) #define TUNE(...) \
int UNIQUE(p, __LINE__) = []() -> int { \
tune_check_args(__VA_ARGS__); \
return Tune::add(STRINGIFY((__VA_ARGS__)), __VA_ARGS__); \
}();
#define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true #define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true
+3 -3
View File
@@ -637,12 +637,12 @@ void UCIEngine::on_update_full(const Engine::InfoFull& info, bool showWDL) {
<< " multipv " << info.multiPV // << " multipv " << info.multiPV //
<< " score " << format_score(info.score); // << " score " << format_score(info.score); //
if (showWDL)
ss << " wdl " << info.wdl;
if (!info.bound.empty()) if (!info.bound.empty())
ss << " " << info.bound; ss << " " << info.bound;
if (showWDL)
ss << " wdl " << info.wdl;
ss << " nodes " << info.nodes // ss << " nodes " << info.nodes //
<< " nps " << info.nps // << " nps " << info.nps //
<< " hashfull " << info.hashfull // << " hashfull " << info.hashfull //
+3 -3
View File
@@ -111,7 +111,7 @@ Option::Option(OnChange f) :
max(0), max(0),
on_change(std::move(f)) {} on_change(std::move(f)) {}
Option::Option(double v, int minv, int maxv, OnChange f) : Option::Option(int v, int minv, int maxv, OnChange f) :
type("spin"), type("spin"),
min(minv), min(minv),
max(maxv), max(maxv),
@@ -155,7 +155,7 @@ Option& Option::operator=(const std::string& v) {
if ((type != "button" && type != "string" && v.empty()) if ((type != "button" && type != "string" && v.empty())
|| (type == "check" && v != "true" && v != "false") || (type == "check" && v != "true" && v != "false")
|| (type == "spin" && (std::stof(v) < min || std::stof(v) > max))) || (type == "spin" && (std::stoi(v) < min || std::stoi(v) > max)))
return *this; return *this;
if (type == "combo") if (type == "combo")
@@ -203,7 +203,7 @@ std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
} }
else if (o.type == "spin") else if (o.type == "spin")
os << " default " << int(stof(o.defaultValue)) << " min " << o.min << " max " os << " default " << stoi(o.defaultValue) << " min " << o.min << " max "
<< o.max; << o.max;
break; break;
+1 -1
View File
@@ -43,7 +43,7 @@ class Option {
Option(OnChange = nullptr); Option(OnChange = nullptr);
Option(bool v, OnChange = nullptr); Option(bool v, OnChange = nullptr);
Option(const char* v, OnChange = nullptr); Option(const char* v, OnChange = nullptr);
Option(double v, int minv, int maxv, OnChange = nullptr); Option(int v, int minv, int maxv, OnChange = nullptr);
Option(const char* v, const char* cur, OnChange = nullptr); Option(const char* v, const char* cur, OnChange = nullptr);
Option& operator=(const std::string&); Option& operator=(const std::string&);
+1 -1
View File
@@ -16,7 +16,7 @@ EXPECT_SCRIPT=$(mktemp)
cat << 'EOF' > $EXPECT_SCRIPT cat << 'EOF' > $EXPECT_SCRIPT
#!/usr/bin/expect -f #!/usr/bin/expect -f
set timeout 30 set timeout 120
lassign [lrange $argv 0 4] pos depth result chess960 logfile lassign [lrange $argv 0 4] pos depth result chess960 logfile
log_file -noappend $logfile log_file -noappend $logfile
spawn ./stockfish spawn ./stockfish