mirror of
https://github.com/opelly27/Stockfish.git
synced 2026-05-20 06:17:49 +00:00
Compare commits
211 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 81490ebc75 | |||
| c402fe7d26 | |||
| 2b8bb8e226 | |||
| 8a3df0f92f | |||
| 6e2236c37a | |||
| d8d1ecca8c | |||
| bb9c6bc6a1 | |||
| 97724370e7 | |||
| b16db14c87 | |||
| 2c51afdb14 | |||
| 1d01b275ce | |||
| dbbb3fa477 | |||
| 18686e29c7 | |||
| e45d4f1b65 | |||
| f4ecc899d8 | |||
| 122c78b521 | |||
| aa339506db | |||
| 292c9efb1d | |||
| 9f2f46c212 | |||
| 61ab908db3 | |||
| 6cd70676b4 | |||
| 56c9b608c9 | |||
| dbab8b03cf | |||
| 8e28c99f79 | |||
| e42258db5a | |||
| 69fa1111e6 | |||
| ffae13edff | |||
| 21d43e9500 | |||
| 5c616bc46b | |||
| 2f459fb161 | |||
| c8f7fa6a02 | |||
| 7bb14c2489 | |||
| 6349062d42 | |||
| 7182c55e5c | |||
| b536b0ac67 | |||
| 60497a85d6 | |||
| a8bdf69c71 | |||
| a6c614da03 | |||
| 98ffe0cd97 | |||
| 2b0ba70436 | |||
| 27b87ddf5d | |||
| 319b8e8e7b | |||
| 44461911f7 | |||
| a285850bf6 | |||
| cae61bbb65 | |||
| ab09c74783 | |||
| e3367756b5 | |||
| 49d2cd8b13 | |||
| beb956f823 | |||
| faf08671ff | |||
| 458a920788 | |||
| 871e6b8c83 | |||
| 76d8f6128a | |||
| c0e1235fef | |||
| 74049a450c | |||
| fbdb373b64 | |||
| c0bbce092b | |||
| cf8a50e654 | |||
| 675672cfc1 | |||
| 4cceeb7380 | |||
| 77018c77cc | |||
| fd78fb05f6 | |||
| afd7d0ea4d | |||
| 36092b855a | |||
| 3bbe4802b1 | |||
| c001a4e62d | |||
| 1536e31065 | |||
| 92c2167481 | |||
| a4786db4c2 | |||
| c24ad8d8b5 | |||
| 961a4dad5c | |||
| 7a13d4ed60 | |||
| 4d4c80d7fd | |||
| 2b821682aa | |||
| be754a2379 | |||
| ec5ef2b6df | |||
| df4da8dc41 | |||
| 6118151c66 | |||
| 2fd1c48e60 | |||
| 961047ed6e | |||
| e29499ee4b | |||
| 7f4b72cdfd | |||
| 686a5a0df9 | |||
| df40de9486 | |||
| fcb391919f | |||
| ae4db5ebfd | |||
| db0615eed9 | |||
| d6e8089f50 | |||
| b521e405d3 | |||
| d7c358cf19 | |||
| bc6a8d09e9 | |||
| df05ecb1d5 | |||
| 1de1eb2d0d | |||
| 081761d084 | |||
| b9a32fe331 | |||
| a06234c639 | |||
| df9b2a87db | |||
| 821aaf3836 | |||
| 272f0f88c3 | |||
| 76d124ed70 | |||
| ec3eaad64f | |||
| f7420652b7 | |||
| d1760a1f15 | |||
| c59583bbf0 | |||
| d61378cacb | |||
| cd55c268cb | |||
| a5af8510a5 | |||
| 288fdc5597 | |||
| 85c802d0b9 | |||
| 3b535b5ade | |||
| cea5240909 | |||
| 5dec3e547e | |||
| c964e902c5 | |||
| 9ce0ef3ac0 | |||
| 9c19021808 | |||
| c679e8f360 | |||
| 13824d8b96 | |||
| 145e4c2a10 | |||
| c8262f8aec | |||
| 486f72af54 | |||
| fda3945c07 | |||
| 8f31d74cf6 | |||
| f5cc77bc7c | |||
| 9dc62809c8 | |||
| 123dd68452 | |||
| f1a8580118 | |||
| 87c8b324f8 | |||
| 6324c2de75 | |||
| aea08de018 | |||
| 2f8c692caa | |||
| 96f2541191 | |||
| cb8accada2 | |||
| 13eb540020 | |||
| 32c204fb56 | |||
| e229015127 | |||
| aa2dc962f5 | |||
| 4c926b8eb4 | |||
| 0761d9504e | |||
| 2af46deede | |||
| a5fb69008c | |||
| 1c8a931309 | |||
| 151a0dda91 | |||
| 8c8a30233c | |||
| 86e3fedf7e | |||
| 7a3c3eacdf | |||
| ff31d92b94 | |||
| 08d8adbade | |||
| 5e119f5139 | |||
| 0e932757e5 | |||
| 8ef6c837b7 | |||
| 89bbe86800 | |||
| 7818d23afb | |||
| 0abd692543 | |||
| 5ae64e2244 | |||
| 5aa801e721 | |||
| 3102896a00 | |||
| ccd2e602a0 | |||
| 999f5ec446 | |||
| 43e78187d7 | |||
| 76b0de40a1 | |||
| c7884470fb | |||
| 6c7a594362 | |||
| 2d5c50d85b | |||
| 33772a0418 | |||
| 5c936572e9 | |||
| 91a7557ab4 | |||
| d23f96d156 | |||
| 2523f72ff9 | |||
| a85e3055f4 | |||
| 78134b7641 | |||
| 6703ec8ab0 | |||
| dd9818c2c1 | |||
| f18acf97ed | |||
| e2165155d1 | |||
| 1d5f79db1c | |||
| c4d30f3649 | |||
| 2d70487caa | |||
| 0be41dbb67 | |||
| c643ee0b45 | |||
| 10aa774d08 | |||
| 8718438943 | |||
| fc5f64b383 | |||
| 3dcd2bb69b | |||
| b300a9d43e | |||
| 747d98bf1b | |||
| df827ea7ee | |||
| 92052bc16b | |||
| 5c0037de7f | |||
| 09e529edd3 | |||
| 89e846c476 | |||
| 81262320c3 | |||
| 00f84ed99a | |||
| 26271586cb | |||
| a413bf7aad | |||
| 9a73df7379 | |||
| 5772509e8b | |||
| 641724e3a5 | |||
| 57ead90f18 | |||
| 07dc336b0f | |||
| 84a96a3d9c | |||
| 998d8721bd | |||
| 9dab4660ce | |||
| 90ef97dcbd | |||
| 24576d77ab | |||
| f58d616198 | |||
| bcd6985871 | |||
| 87445881ec | |||
| 48bfe86d27 | |||
| b330602cdc | |||
| 9964fbbe25 | |||
| db02ddcc90 |
@@ -1,333 +0,0 @@
|
||||
name: Stockfish
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- tools
|
||||
- github_ci
|
||||
- github_ci_armv7
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- tools
|
||||
jobs:
|
||||
Stockfish:
|
||||
name: ${{ matrix.config.name }}
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
env:
|
||||
COMPILER: ${{ matrix.config.compiler }}
|
||||
COMP: ${{ matrix.config.comp }}
|
||||
CXXFLAGS: "-Werror"
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
# set the variable for the required tests:
|
||||
# run_expensive_tests: true
|
||||
# run_32bit_tests: true
|
||||
# run_64bit_tests: true
|
||||
# run_armv8_tests: true
|
||||
# run_armv7_tests: true
|
||||
- {
|
||||
name: "Ubuntu 20.04 GCC",
|
||||
os: ubuntu-20.04,
|
||||
compiler: g++,
|
||||
comp: gcc,
|
||||
run_expensive_tests: true,
|
||||
run_32bit_tests: true,
|
||||
run_64bit_tests: true,
|
||||
shell: 'bash {0}'
|
||||
}
|
||||
- {
|
||||
name: "Ubuntu 20.04 Clang",
|
||||
os: ubuntu-20.04,
|
||||
compiler: clang++,
|
||||
comp: clang,
|
||||
run_32bit_tests: true,
|
||||
run_64bit_tests: true,
|
||||
shell: 'bash {0}'
|
||||
}
|
||||
- {
|
||||
name: "Ubuntu 20.04 NDK armv8",
|
||||
os: ubuntu-20.04,
|
||||
compiler: aarch64-linux-android21-clang++,
|
||||
comp: ndk,
|
||||
run_armv8_tests: true,
|
||||
shell: 'bash {0}'
|
||||
}
|
||||
- {
|
||||
name: "Ubuntu 20.04 NDK armv7",
|
||||
os: ubuntu-20.04,
|
||||
compiler: armv7a-linux-androideabi21-clang++,
|
||||
comp: ndk,
|
||||
run_armv7_tests: true,
|
||||
shell: 'bash {0}'
|
||||
}
|
||||
- {
|
||||
name: "MacOS 10.15 Apple Clang",
|
||||
os: macos-10.15,
|
||||
compiler: clang++,
|
||||
comp: clang,
|
||||
run_64bit_tests: true,
|
||||
shell: 'bash {0}'
|
||||
}
|
||||
- {
|
||||
name: "MacOS 10.15 GCC 10",
|
||||
os: macos-10.15,
|
||||
compiler: g++-10,
|
||||
comp: gcc,
|
||||
run_64bit_tests: true,
|
||||
shell: 'bash {0}'
|
||||
}
|
||||
- {
|
||||
name: "Windows 2022 Mingw-w64 GCC x86_64",
|
||||
os: windows-2022,
|
||||
compiler: g++,
|
||||
comp: mingw,
|
||||
run_64bit_tests: true,
|
||||
msys_sys: 'mingw64',
|
||||
msys_env: 'x86_64-gcc',
|
||||
shell: 'msys2 {0}'
|
||||
}
|
||||
- {
|
||||
name: "Windows 2022 Mingw-w64 GCC i686",
|
||||
os: windows-2022,
|
||||
compiler: g++,
|
||||
comp: mingw,
|
||||
run_32bit_tests: true,
|
||||
msys_sys: 'mingw32',
|
||||
msys_env: 'i686-gcc',
|
||||
shell: 'msys2 {0}'
|
||||
}
|
||||
- {
|
||||
name: "Windows 2022 Mingw-w64 Clang x86_64",
|
||||
os: windows-2022,
|
||||
compiler: clang++,
|
||||
comp: clang,
|
||||
run_64bit_tests: true,
|
||||
msys_sys: 'clang64',
|
||||
msys_env: 'clang-x86_64-clang',
|
||||
shell: 'msys2 {0}'
|
||||
}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: src
|
||||
shell: ${{ matrix.config.shell }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download required linux packages
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install expect valgrind g++-multilib qemu-user
|
||||
|
||||
- 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 expect
|
||||
|
||||
- name: Download the used network from the fishtest framework
|
||||
run: |
|
||||
make net
|
||||
|
||||
- name: Extract the bench number from the commit history
|
||||
run: |
|
||||
git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig
|
||||
[ -s git_sig ] && echo "benchref=$(cat git_sig)" >> $GITHUB_ENV && echo "Reference bench:" $(cat git_sig) || echo "No bench found"
|
||||
|
||||
- name: Check compiler
|
||||
run: |
|
||||
export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin
|
||||
$COMPILER -v
|
||||
|
||||
- name: Test help target
|
||||
run: |
|
||||
make help
|
||||
|
||||
# x86-32 tests
|
||||
|
||||
- name: Test debug x86-32 build
|
||||
if: ${{ matrix.config.run_32bit_tests }}
|
||||
run: |
|
||||
export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG"
|
||||
make clean
|
||||
make -j2 ARCH=x86-32 optimize=no debug=yes build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test x86-32 build
|
||||
if: ${{ matrix.config.run_32bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-32 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test x86-32-sse41-popcnt build
|
||||
if: ${{ matrix.config.run_32bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-32-sse41-popcnt build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test x86-32-sse2 build
|
||||
if: ${{ matrix.config.run_32bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-32-sse2 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test general-32 build
|
||||
if: ${{ matrix.config.run_32bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=general-32 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
# x86-64 tests
|
||||
|
||||
- name: Test debug x86-64-modern build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG"
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-modern optimize=no debug=yes build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test x86-64-modern build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-modern build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test x86-64-ssse3 build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-ssse3 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test x86-64-sse3-popcnt build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-sse3-popcnt build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test x86-64 build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test general-64 build
|
||||
if: matrix.config.run_64bit_tests
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=general-64 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
# x86-64 with newer extensions tests
|
||||
|
||||
- name: Compile x86-64-avx2 build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-avx2 build
|
||||
|
||||
- name: Compile x86-64-bmi2 build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-bmi2 build
|
||||
|
||||
- name: Compile x86-64-avx512 build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-avx512 build
|
||||
|
||||
- name: Compile x86-64-vnni512 build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-vnni512 build
|
||||
|
||||
- name: Compile x86-64-vnni256 build
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-vnni256 build
|
||||
|
||||
# armv8 tests
|
||||
|
||||
- name: Test armv8 build
|
||||
if: ${{ matrix.config.run_armv8_tests }}
|
||||
run: |
|
||||
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
|
||||
export LDFLAGS="-static -Wno-unused-command-line-argument"
|
||||
make clean
|
||||
make -j2 ARCH=armv8 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
# armv7 tests
|
||||
|
||||
- name: Test armv7 build
|
||||
if: ${{ matrix.config.run_armv7_tests }}
|
||||
run: |
|
||||
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
|
||||
export LDFLAGS="-static -Wno-unused-command-line-argument"
|
||||
make clean
|
||||
make -j2 ARCH=armv7 build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
- name: Test armv7-neon build
|
||||
if: ${{ matrix.config.run_armv7_tests }}
|
||||
run: |
|
||||
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
|
||||
export LDFLAGS="-static -Wno-unused-command-line-argument"
|
||||
make clean
|
||||
make -j2 ARCH=armv7-neon build
|
||||
../tests/signature.sh $benchref
|
||||
|
||||
# Other tests
|
||||
|
||||
- name: Check perft and search reproducibility
|
||||
if: ${{ matrix.config.run_64bit_tests }}
|
||||
run: |
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-modern build
|
||||
../tests/perft.sh
|
||||
../tests/reprosearch.sh
|
||||
|
||||
# Sanitizers
|
||||
|
||||
- name: Run under valgrind
|
||||
if: ${{ matrix.config.run_expensive_tests }}
|
||||
run: |
|
||||
export CXXFLAGS="-O1 -fno-inline"
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-modern debug=yes optimize=no build > /dev/null
|
||||
../tests/instrumented.sh --valgrind
|
||||
../tests/instrumented.sh --valgrind-thread
|
||||
|
||||
- name: Run with UB sanitizer
|
||||
if: ${{ matrix.config.run_expensive_tests }}
|
||||
run: |
|
||||
export CXXFLAGS="-O1 -fno-inline"
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-modern sanitize=undefined optimize=no debug=yes build > /dev/null
|
||||
../tests/instrumented.sh --sanitizer-undefined
|
||||
|
||||
- name: Run with thread sanitizer
|
||||
if: ${{ matrix.config.run_expensive_tests }}
|
||||
run: |
|
||||
export CXXFLAGS="-O1 -fno-inline"
|
||||
make clean
|
||||
make -j2 ARCH=x86-64-modern sanitize=thread optimize=no debug=yes build > /dev/null
|
||||
../tests/instrumented.sh --sanitizer-thread
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
# Files from build
|
||||
**/*.o
|
||||
**/*.s
|
||||
src/.depend
|
||||
|
||||
# Built binary
|
||||
src/stockfish*
|
||||
src/-lstdc++.res
|
||||
|
||||
# Neural network for the NNUE evaluation
|
||||
**/*.nnue
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
language: cpp
|
||||
dist: bionic
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
packages: ['g++-8', 'g++-8-multilib', 'g++-multilib', 'valgrind', 'expect', 'curl']
|
||||
env:
|
||||
- COMPILER=g++-8
|
||||
- COMP=gcc
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
packages: ['clang-10', 'llvm-10-dev', 'g++-multilib', 'valgrind', 'expect', 'curl']
|
||||
env:
|
||||
- COMPILER=clang++-10
|
||||
- COMP=clang
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode12
|
||||
compiler: gcc
|
||||
env:
|
||||
- COMPILER=g++
|
||||
- COMP=gcc
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode12
|
||||
compiler: clang
|
||||
env:
|
||||
- COMPILER=clang++
|
||||
- COMP=clang
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
before_script:
|
||||
- cd src
|
||||
|
||||
script:
|
||||
# Obtain bench reference from git log
|
||||
- git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig
|
||||
- export benchref=$(cat git_sig)
|
||||
- echo "Reference bench:" $benchref
|
||||
|
||||
#
|
||||
# Compiler version string
|
||||
- $COMPILER -v
|
||||
|
||||
#
|
||||
# Verify bench number against various builds
|
||||
- export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG"
|
||||
- make clean && make -j2 ARCH=x86-64 optimize=no debug=yes build && ../tests/signature.sh $benchref
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref; fi
|
||||
|
||||
#
|
||||
# Check perft and reproducible search
|
||||
- export CXXFLAGS="-Werror"
|
||||
- make clean && make -j2 ARCH=x86-64 build
|
||||
- ../tests/perft.sh
|
||||
- ../tests/reprosearch.sh
|
||||
|
||||
#
|
||||
# Valgrind
|
||||
#
|
||||
- export CXXFLAGS="-O1 -fno-inline"
|
||||
- if [ -x "$(command -v valgrind )" ]; then make clean && make -j2 ARCH=x86-64 debug=yes optimize=no build > /dev/null && ../tests/instrumented.sh --valgrind; fi
|
||||
- if [ -x "$(command -v valgrind )" ]; then ../tests/instrumented.sh --valgrind-thread; fi
|
||||
|
||||
#
|
||||
# Sanitizer
|
||||
#
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi
|
||||
@@ -1,4 +1,4 @@
|
||||
# List of authors for Stockfish
|
||||
# List of authors for Stockfish, as of August 4, 2020
|
||||
|
||||
# Founders of the Stockfish project and fishtest infrastructure
|
||||
Tord Romstad (romstad)
|
||||
@@ -19,42 +19,32 @@ Alain Savard (Rocky640)
|
||||
Alayan Feh (Alayan-stk-2)
|
||||
Alexander Kure
|
||||
Alexander Pagel (Lolligerhans)
|
||||
Alfredo Menezes (lonfom169)
|
||||
Ali AlZhrani (Cooffe)
|
||||
Andrei Vetrov (proukornew)
|
||||
Andrew Grant (AndyGrant)
|
||||
Andrey Neporada (nepal)
|
||||
Andy Duplain
|
||||
Antoine Champion (antoinechampion)
|
||||
Aram Tumanian (atumanian)
|
||||
Arjun Temurnikar
|
||||
Artem Solopiy (EntityFX)
|
||||
Auguste Pop
|
||||
Balint Pfliegel
|
||||
Ben Chaney (Chaneybenjamini)
|
||||
Ben Koshy (BKSpurgeon)
|
||||
Bill Henry (VoyagerOne)
|
||||
Bojun Guo (noobpwnftw, Nooby)
|
||||
braich
|
||||
Brian Sheppard (SapphireBrand, briansheppard-toast)
|
||||
Bruno de Melo Costa (BM123499)
|
||||
Bryan Cross (crossbr)
|
||||
candirufish
|
||||
Chess13234
|
||||
Chris Cain (ceebo)
|
||||
Dale Weiler (graphitemaster)
|
||||
Dan Schmidt (dfannius)
|
||||
Daniel Axtens (daxtens)
|
||||
Daniel Dugovic (ddugovic)
|
||||
Dariusz Orzechowski (dorzechowski)
|
||||
Dariusz Orzechowski
|
||||
David Zar
|
||||
Daylen Yang (daylen)
|
||||
Deshawn Mohan-Smith (GoldenRare)
|
||||
Dieter Dobbelaere (ddobbelaere)
|
||||
DiscanX
|
||||
Dominik Schlösser (domschl)
|
||||
double-beep
|
||||
Douglas Matos Gomes (dsmsgms)
|
||||
Eduardo Cáceres (eduherminio)
|
||||
Eelco de Groot (KingDefender)
|
||||
Elvin Liu (solarlight2)
|
||||
@@ -63,15 +53,12 @@ Ernesto Gatti
|
||||
Linmiao Xu (linrock)
|
||||
Fabian Beuke (madnight)
|
||||
Fabian Fichter (ianfab)
|
||||
Fanael Linithien (Fanael)
|
||||
fanon
|
||||
Fauzi Akram Dabat (FauziAkram)
|
||||
Felix Wittmann
|
||||
gamander
|
||||
Gary Heckman (gheckman)
|
||||
George Sobala (gsobala)
|
||||
gguliash
|
||||
Giacomo Lorenzetti (G-Lorenz)
|
||||
Gian-Carlo Pascutto (gcp)
|
||||
Gontran Lemaire (gonlem)
|
||||
Goodkov Vasiliy Aleksandrovich (goodkov)
|
||||
@@ -92,26 +79,21 @@ Jean Gauthier (OuaisBla)
|
||||
Jean-Francois Romang (jromang)
|
||||
Jekaa
|
||||
Jerry Donald Watson (jerrydonaldwatson)
|
||||
jjoshua2
|
||||
Jonathan Calovski (Mysseno)
|
||||
Jonathan Buladas Dumale (SFisGOD)
|
||||
Jonathan Dumale (SFisGOD)
|
||||
Joost VandeVondele (vondele)
|
||||
Jörg Oster (joergoster)
|
||||
Joseph Ellis (jhellis3)
|
||||
Joseph R. Prostko
|
||||
Julian Willemer (NightlyKing)
|
||||
jundery
|
||||
Justin Blanchard (UncombedCoconut)
|
||||
Kelly Wilson
|
||||
Ken Takusagawa
|
||||
Kian E (KJE-98)
|
||||
kinderchocolate
|
||||
Kiran Panditrao (Krgp)
|
||||
Kojirion
|
||||
Krystian Kuzniarek (kuzkry)
|
||||
Leonardo Ljubičić (ICCF World Champion)
|
||||
Leonid Pechenik (lp--)
|
||||
Liam Keegan (lkeegan)
|
||||
Linus Arver (listx)
|
||||
loco-loco
|
||||
Lub van den Berg (ElbertoOne)
|
||||
@@ -122,10 +104,8 @@ Maciej Żenczykowski (zenczykowski)
|
||||
Malcolm Campbell (xoto10)
|
||||
Mark Tenzer (31m059)
|
||||
marotear
|
||||
Matt Ginsberg (mattginsberg)
|
||||
Matthew Lai (matthewlai)
|
||||
Matthew Sullivan (Matt14916)
|
||||
Maxim Molchanov (Maxim)
|
||||
Michael An (man)
|
||||
Michael Byrne (MichaelB7)
|
||||
Michael Chaly (Vizvezdenec)
|
||||
@@ -134,7 +114,6 @@ Michael Whiteley (protonspring)
|
||||
Michel Van den Bergh (vdbergh)
|
||||
Miguel Lahoz (miguel-l)
|
||||
Mikael Bäckman (mbootsector)
|
||||
Mike Babigian (Farseer)
|
||||
Mira
|
||||
Miroslav Fontán (Hexik)
|
||||
Moez Jellouli (MJZ1977)
|
||||
@@ -146,8 +125,6 @@ Niklas Fiekas (niklasf)
|
||||
Nikolay Kostov (NikolayIT)
|
||||
Nguyen Pham (nguyenpham)
|
||||
Norman Schmidt (FireFather)
|
||||
notruck
|
||||
Ofek Shochat (OfekShochat, ghostway)
|
||||
Ondrej Mosnáček (WOnder93)
|
||||
Oskar Werkelin Ahlin
|
||||
Pablo Vazquez
|
||||
@@ -156,7 +133,6 @@ Pascal Romaret
|
||||
Pasquale Pigazzini (ppigazzini)
|
||||
Patrick Jansen (mibere)
|
||||
pellanda
|
||||
Peter Schneider (pschneider1968)
|
||||
Peter Zsifkovits (CoffeeOne)
|
||||
Praveen Kumar Tummala (praveentml)
|
||||
Rahul Dsilva (silversolver1)
|
||||
@@ -169,23 +145,19 @@ Rodrigo Exterckötter Tjäder
|
||||
Ron Britvich (Britvich)
|
||||
Ronald de Man (syzygy1, syzygy)
|
||||
rqs
|
||||
Rui Coelho (ruicoelhopedro)
|
||||
Ryan Schmitt
|
||||
Ryan Takker
|
||||
Sami Kiminki (skiminki)
|
||||
Sebastian Buchwald (UniQP)
|
||||
Sergei Antonov (saproj)
|
||||
Sergei Ivanov (svivanov72)
|
||||
Sergio Vieri (sergiovieri)
|
||||
sf-x
|
||||
Shane Booth (shane31)
|
||||
Shawn Varghese (xXH4CKST3RXx)
|
||||
Siad Daboul (Topologist)
|
||||
Stefan Geschwentner (locutus2)
|
||||
Stefano Cardanobile (Stefano80)
|
||||
Steinar Gunderson (sesse)
|
||||
Stéphane Nicolet (snicolet)
|
||||
Prokop Randáček (ProkopRandacek)
|
||||
Thanar2
|
||||
thaspel
|
||||
theo77186
|
||||
@@ -193,13 +165,11 @@ Tom Truscott
|
||||
Tom Vijlbrief (tomtor)
|
||||
Tomasz Sobczyk (Sopel97)
|
||||
Torsten Franz (torfranz, tfranzer)
|
||||
Torsten Hellwig (Torom)
|
||||
Tracey Emery (basepr1me)
|
||||
tttak
|
||||
Unai Corzo (unaiic)
|
||||
Uri Blass (uriblass)
|
||||
Vince Negri (cuddlestmonkey)
|
||||
xefoci7612
|
||||
zz4032
|
||||
|
||||
|
||||
|
||||
@@ -1,52 +1,46 @@
|
||||
## Overview
|
||||
|
||||
[](https://github.com/official-stockfish/Stockfish/actions)
|
||||
[](https://travis-ci.org/official-stockfish/Stockfish)
|
||||
[](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master)
|
||||
|
||||
[Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine
|
||||
derived from Glaurung 2.1. Stockfish is not a complete chess program and requires a
|
||||
UCI-compatible graphical user interface (GUI) (e.g. XBoard with PolyGlot, Scid,
|
||||
Cute Chess, eboard, Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in order
|
||||
to be used comfortably. Read the documentation for your GUI of choice for information
|
||||
about how to use Stockfish with it.
|
||||
derived from Glaurung 2.1. It features two evaluation functions, the classical
|
||||
evaluation based on handcrafted terms, and the NNUE evaluation based on
|
||||
efficiently updateable neural networks. The classical evaluation runs efficiently
|
||||
on most 64bit CPU architectures, while the NNUE evaluation benefits strongly from the
|
||||
vector intrinsics available on modern CPUs (avx2 or similar).
|
||||
|
||||
Stockfish is not a complete chess program and requires a
|
||||
UCI-compatible GUI (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard, Arena,
|
||||
Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably.
|
||||
Read the documentation for your GUI of choice for information about how to use
|
||||
Stockfish with it.
|
||||
|
||||
The Stockfish engine features two evaluation functions for chess. The efficiently
|
||||
updatable neural network (NNUE) based evaluation is the default and by far the strongest.
|
||||
The classical evaluation based on handcrafted terms remains available. The strongest
|
||||
network is integrated in the binary and downloaded automatically during the build process.
|
||||
The NNUE evaluation benefits from the vector intrinsics available on most CPUs (sse2,
|
||||
avx2, neon, or similar).
|
||||
|
||||
## Files
|
||||
|
||||
This distribution of Stockfish consists of the following files:
|
||||
|
||||
* [Readme.md](https://github.com/official-stockfish/Stockfish/blob/master/README.md),
|
||||
the file you are currently reading.
|
||||
* Readme.md, the file you are currently reading.
|
||||
|
||||
* [Copying.txt](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt),
|
||||
a text file containing the GNU General Public License version 3.
|
||||
* Copying.txt, a text file containing the GNU General Public License version 3.
|
||||
|
||||
* [AUTHORS](https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS),
|
||||
a text file with the list of authors for the project
|
||||
|
||||
* [src](https://github.com/official-stockfish/Stockfish/tree/master/src),
|
||||
a subdirectory containing the full source code, including a Makefile
|
||||
* src, a subdirectory containing the full source code, including a Makefile
|
||||
that can be used to compile Stockfish on Unix-like systems.
|
||||
|
||||
* a file with the .nnue extension, storing the neural network for the NNUE
|
||||
evaluation. Binary distributions will have this file embedded.
|
||||
To use the NNUE evaluation an additional data file with neural network parameters
|
||||
needs to be downloaded. The filename for the default set can be found as the default
|
||||
value of the `EvalFile` UCI option, with the format
|
||||
`nn-[SHA256 first 12 digits].nnue` (e.g. nn-c157e0a5755b.nnue). This file can be downloaded from
|
||||
```
|
||||
https://tests.stockfishchess.org/api/nn/[filename]
|
||||
```
|
||||
replacing `[filename]` as needed.
|
||||
|
||||
## The UCI protocol and available options
|
||||
|
||||
The Universal Chess Interface (UCI) is a standard protocol used to communicate with
|
||||
a chess engine, and is the recommended way to do so for typical graphical user interfaces
|
||||
(GUI) or chess tools. Stockfish implements the majority of its options as described
|
||||
in [the UCI protocol](https://www.shredderchess.com/download/div/uci.zip).
|
||||
## UCI options
|
||||
|
||||
Developers can see the default values for UCI options available in Stockfish by typing
|
||||
`./stockfish uci` in a terminal, but the majority of users will typically see them and
|
||||
change them via a chess GUI. This is a list of available UCI options in Stockfish:
|
||||
Currently, Stockfish has the following UCI options:
|
||||
|
||||
* #### Threads
|
||||
The number of CPU threads used for searching a position. For best performance, set
|
||||
@@ -55,9 +49,6 @@ change them via a chess GUI. This is a list of available UCI options in Stockfis
|
||||
* #### Hash
|
||||
The size of the hash table in MB. It is recommended to set Hash after setting Threads.
|
||||
|
||||
* #### Clear Hash
|
||||
Clear the hash table.
|
||||
|
||||
* #### Ponder
|
||||
Let Stockfish ponder its next move while the opponent is thinking.
|
||||
|
||||
@@ -67,14 +58,19 @@ change them via a chess GUI. This is a list of available UCI options in Stockfis
|
||||
|
||||
* #### Use NNUE
|
||||
Toggle between the NNUE and classical evaluation functions. If set to "true",
|
||||
the network parameters must be available to load from file (see also EvalFile),
|
||||
if they are not embedded in the binary.
|
||||
the network parameters must be availabe to load from file (see also EvalFile).
|
||||
|
||||
* #### EvalFile
|
||||
The name of the file of the NNUE evaluation parameters. Depending on the GUI the
|
||||
filename might have to include the full path to the folder/directory that contains
|
||||
the file. Other locations, such as the directory that contains the binary and the
|
||||
working directory, are also searched.
|
||||
filename should include the full path to the folder/directory that contains the file.
|
||||
|
||||
* #### Contempt
|
||||
A positive value for contempt favors middle game positions and avoids draws,
|
||||
effective for the classical evaluation only.
|
||||
|
||||
* #### Analysis Contempt
|
||||
By default, contempt is set to prefer the side to move. Set this option to "White"
|
||||
or "Black" to analyse with contempt for that side, or "Off" to disable contempt.
|
||||
|
||||
* #### UCI_AnalyseMode
|
||||
An option handled by your GUI.
|
||||
@@ -107,14 +103,14 @@ change them via a chess GUI. This is a list of available UCI options in Stockfis
|
||||
Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6`
|
||||
|
||||
It is recommended to store .rtbw files on an SSD. There is no loss in storing
|
||||
the .rtbz files on a regular HDD. It is recommended to verify all md5 checksums
|
||||
the .rtbz files on a regular HD. It is recommended to verify all md5 checksums
|
||||
of the downloaded tablebase files (`md5sum -c checksum.md5`) as corruption will
|
||||
lead to engine crashes.
|
||||
|
||||
* #### SyzygyProbeDepth
|
||||
Minimum remaining search depth for which a position is probed. Set this option
|
||||
to a higher value to probe less aggressively if you experience too much slowdown
|
||||
(in terms of nps) due to tablebase probing.
|
||||
to a higher value to probe less agressively if you experience too much slowdown
|
||||
(in terms of nps) due to TB probing.
|
||||
|
||||
* #### Syzygy50MoveRule
|
||||
Disable to let fifty-move rule draws detected by Syzygy tablebase probes count
|
||||
@@ -136,84 +132,42 @@ change them via a chess GUI. This is a list of available UCI options in Stockfis
|
||||
Tells the engine to use nodes searched instead of wall time to account for
|
||||
elapsed time. Useful for engine testing.
|
||||
|
||||
* #### Clear Hash
|
||||
Clear the hash table.
|
||||
|
||||
* #### Debug Log File
|
||||
Write all communication to and from the engine into a text file.
|
||||
|
||||
For developers the following non-standard commands might be of interest, mainly useful for debugging:
|
||||
|
||||
* #### bench *ttSize threads limit fenFile limitType evalType*
|
||||
Performs a standard benchmark using various options. The signature of a version
|
||||
(standard node count) is obtained using all defaults. `bench` is currently
|
||||
`bench 16 1 13 default depth mixed`.
|
||||
|
||||
* #### compiler
|
||||
Give information about the compiler and environment used for building a binary.
|
||||
|
||||
* #### d
|
||||
Display the current position, with ascii art and fen.
|
||||
|
||||
* #### eval
|
||||
Return the evaluation of the current position.
|
||||
|
||||
* #### export_net [filename]
|
||||
Exports the currently loaded network to a file.
|
||||
If the currently loaded network is the embedded network and the filename
|
||||
is not specified then the network is saved to the file matching the name
|
||||
of the embedded network, as defined in evaluate.h.
|
||||
If the currently loaded network is not the embedded network (some net set
|
||||
through the UCI setoption) then the filename parameter is required and the
|
||||
network is saved into that file.
|
||||
|
||||
* #### flip
|
||||
Flips the side to move.
|
||||
|
||||
|
||||
## A note on classical evaluation versus NNUE evaluation
|
||||
## classical and NNUE evaluation
|
||||
|
||||
Both approaches assign a value to a position that is used in alpha-beta (PVS) search
|
||||
to find the best move. The classical evaluation computes this value as a function
|
||||
of various chess concepts, handcrafted by experts, tested and tuned using fishtest.
|
||||
The NNUE evaluation computes this value with a neural network based on basic
|
||||
inputs (e.g. piece positions only). The network is optimized and trained
|
||||
on the evaluations of millions of positions at moderate search depth.
|
||||
on the evalutions of millions of positions at moderate search depth.
|
||||
|
||||
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
|
||||
It can be evaluated efficiently on CPUs, and exploits the fact that only parts
|
||||
of the neural network need to be updated after a typical chess move.
|
||||
[The nodchip repository](https://github.com/nodchip/Stockfish) provided the first
|
||||
version of the needed tools to train and develop the NNUE networks. Today, more
|
||||
advanced training tools are available in
|
||||
[the nnue-pytorch repository](https://github.com/glinscott/nnue-pytorch/),
|
||||
while data generation tools are available in
|
||||
[a dedicated branch](https://github.com/official-stockfish/Stockfish/tree/tools).
|
||||
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
|
||||
tools to train and develop the NNUE networks.
|
||||
|
||||
On CPUs supporting modern vector instructions (avx2 and similar), the NNUE evaluation
|
||||
results in much stronger playing strength, even if the nodes per second computed by
|
||||
the engine is somewhat lower (roughly 80% of nps is typical).
|
||||
results in stronger playing strength, even if the nodes per second computed by the engine
|
||||
is somewhat lower (roughly 60% of nps is typical).
|
||||
|
||||
Notes:
|
||||
Note that the NNUE evaluation depends on the Stockfish binary and the network parameter
|
||||
file (see EvalFile). Not every parameter file is compatible with a given Stockfish binary.
|
||||
The default value of the EvalFile UCI option is the name of a network that is guaranteed
|
||||
to be compatible with that binary.
|
||||
|
||||
1) the NNUE evaluation depends on the Stockfish binary and the network parameter file
|
||||
(see the EvalFile UCI option). Not every parameter file is compatible with a given
|
||||
Stockfish binary, but the default value of the EvalFile UCI option is the name of a
|
||||
network that is guaranteed to be compatible with that binary.
|
||||
|
||||
2) to use the NNUE evaluation, the additional data file with neural network parameters
|
||||
needs to be available. Normally, this file is already embedded in the binary or it can
|
||||
be downloaded. The filename for the default (recommended) net can be found as the default
|
||||
value of the `EvalFile` UCI option, with the format `nn-[SHA256 first 12 digits].nnue`
|
||||
(for instance, `nn-c157e0a5755b.nnue`). This file can be downloaded from
|
||||
```
|
||||
https://tests.stockfishchess.org/api/nn/[filename]
|
||||
```
|
||||
replacing `[filename]` as needed.
|
||||
|
||||
## What to expect from the Syzygy tablebases?
|
||||
## What to expect from Syzygybases?
|
||||
|
||||
If the engine is searching a position that is not in the tablebases (e.g.
|
||||
a position with 8 pieces), it will access the tablebases during the search.
|
||||
If the engine reports a very large score (typically 153.xx), this means
|
||||
it has found a winning line into a tablebase position.
|
||||
that it has found a winning line into a tablebase position.
|
||||
|
||||
If the engine is given a position to search that is in the tablebases, it
|
||||
will use the tablebases at the beginning of the search to preselect all
|
||||
@@ -221,14 +175,14 @@ good moves, i.e. all moves that preserve the win or preserve the draw while
|
||||
taking into account the 50-move rule.
|
||||
It will then perform a search only on those moves. **The engine will not move
|
||||
immediately**, unless there is only a single good move. **The engine likely
|
||||
will not report a mate score, even if the position is known to be won.**
|
||||
will not report a mate score even if the position is known to be won.**
|
||||
|
||||
It is therefore clear that this behaviour is not identical to what one might
|
||||
be used to with Nalimov tablebases. There are technical reasons for this
|
||||
difference, the main technical reason being that Nalimov tablebases use the
|
||||
DTM metric (distance-to-mate), while the Syzygy tablebases use a variation of the
|
||||
DTM metric (distance-to-mate), while Syzygybases use a variation of the
|
||||
DTZ metric (distance-to-zero, zero meaning any move that resets the 50-move
|
||||
counter). This special metric is one of the reasons that the Syzygy tablebases are
|
||||
counter). This special metric is one of the reasons that Syzygybases are
|
||||
more compact than Nalimov tablebases, while still storing all information
|
||||
needed for optimal play and in addition being able to take into account
|
||||
the 50-move rule.
|
||||
@@ -237,8 +191,8 @@ the 50-move rule.
|
||||
|
||||
Stockfish supports large pages on Linux and Windows. Large pages make
|
||||
the hash access more efficient, improving the engine speed, especially
|
||||
on large hash sizes. Typical increases are 5..10% in terms of nodes per
|
||||
second, but speed increases up to 30% have been measured. The support is
|
||||
on large hash sizes. Typical increases are 5..10% in terms of nps, but
|
||||
speed increases up to 30% have been measured. The support is
|
||||
automatic. Stockfish attempts to use large pages when available and
|
||||
will fall back to regular memory allocation when this is not the case.
|
||||
|
||||
@@ -246,17 +200,17 @@ will fall back to regular memory allocation when this is not the case.
|
||||
|
||||
Large page support on Linux is obtained by the Linux kernel
|
||||
transparent huge pages functionality. Typically, transparent huge pages
|
||||
are already enabled, and no configuration is needed.
|
||||
are already enabled and no configuration is needed.
|
||||
|
||||
### Support on Windows
|
||||
|
||||
The use of large pages requires "Lock Pages in Memory" privilege. See
|
||||
[Enable the Lock Pages in Memory Option (Windows)](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows)
|
||||
on how to enable this privilege, then run [RAMMap](https://docs.microsoft.com/en-us/sysinternals/downloads/rammap)
|
||||
to double-check that large pages are used. We suggest that you reboot
|
||||
your computer after you have enabled large pages, because long Windows
|
||||
sessions suffer from memory fragmentation, which may prevent Stockfish
|
||||
from getting large pages: a fresh session is better in this regard.
|
||||
on how to enable this privilege. Logout/login may be needed
|
||||
afterwards. Due to memory fragmentation, it may not always be
|
||||
possible to allocate large pages even when enabled. A reboot
|
||||
might alleviate this problem. To determine whether large pages
|
||||
are in use, see the engine log.
|
||||
|
||||
## Compiling Stockfish yourself from the sources
|
||||
|
||||
@@ -271,26 +225,26 @@ targets with corresponding descriptions.
|
||||
```
|
||||
cd src
|
||||
make help
|
||||
make net
|
||||
make build ARCH=x86-64-modern
|
||||
```
|
||||
|
||||
When not using the Makefile to compile (for instance, with Microsoft MSVC) you
|
||||
When not using the Makefile to compile (for instance with Microsoft MSVC) you
|
||||
need to manually set/unset some switches in the compiler command line; see
|
||||
file *types.h* for a quick reference.
|
||||
|
||||
When reporting an issue or a bug, please tell us which Stockfish version
|
||||
and which compiler you used to create your executable. This information
|
||||
can be found by typing the following command in a console:
|
||||
When reporting an issue or a bug, please tell us which version and
|
||||
compiler you used to create your executable. These informations can
|
||||
be found by typing the following commands in a console:
|
||||
|
||||
```
|
||||
./stockfish compiler
|
||||
./stockfish
|
||||
compiler
|
||||
```
|
||||
|
||||
## Understanding the code base and participating in the project
|
||||
|
||||
Stockfish's improvement over the last decade has been a great community
|
||||
effort. There are a few ways to help contribute to its growth.
|
||||
Stockfish's improvement over the last couple of years has been a great
|
||||
community effort. There are a few ways to help contribute to its growth.
|
||||
|
||||
### Donating hardware
|
||||
|
||||
@@ -311,9 +265,8 @@ generic rather than being focused on Stockfish's precise implementation.
|
||||
Nevertheless, a helpful resource.
|
||||
|
||||
* The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish).
|
||||
Discussions about Stockfish take place these days mainly in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking)
|
||||
group and on the [Stockfish Discord channel](https://discord.gg/nv8gDtt).
|
||||
The engine testing is done on [Fishtest](https://tests.stockfishchess.org/tests).
|
||||
Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking)
|
||||
group and engine testing is done on [Fishtest](https://tests.stockfishchess.org/tests).
|
||||
If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test)
|
||||
first, where the basics of Stockfish development are explained.
|
||||
|
||||
@@ -321,17 +274,16 @@ first, where the basics of Stockfish development are explained.
|
||||
## Terms of use
|
||||
|
||||
Stockfish is free, and distributed under the **GNU General Public License version 3**
|
||||
(GPL v3). Essentially, this means you are free to do almost exactly
|
||||
(GPL v3). Essentially, this means that you are free to do almost exactly
|
||||
what you want with the program, including distributing it among your
|
||||
friends, making it available for download from your website, selling
|
||||
friends, making it available for download from your web site, selling
|
||||
it (either by itself or as part of some bigger software package), or
|
||||
using it as the starting point for a software project of your own.
|
||||
|
||||
The only real limitation is that whenever you distribute Stockfish in
|
||||
some way, you MUST always include the license and the full source code
|
||||
(or a pointer to where the source code can be found) to generate the
|
||||
exact binary you are distributing. If you make any changes to the
|
||||
source code, these changes must also be made available under the GPL v3.
|
||||
some way, you must always include the full source code, or a pointer
|
||||
to where the source code can be found. If you make any changes to the
|
||||
source code, these changes must also be made available under the GPL.
|
||||
|
||||
For full details, read the copy of the GPL v3 found in the file named
|
||||
[*Copying.txt*](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt).
|
||||
*Copying.txt*.
|
||||
|
||||
+152
-233
@@ -1,235 +1,154 @@
|
||||
Contributors to Fishtest with >10,000 CPU hours, as of 2022-04-14.
|
||||
Contributors with >10,000 CPU hours as of January 7, 2020
|
||||
Thank you!
|
||||
|
||||
Username CPU Hours Games played
|
||||
------------------------------------------------------------------
|
||||
noobpwnftw 31714850 2267266129
|
||||
mlang 2954099 198421098
|
||||
technologov 2324150 102449398
|
||||
dew 1670874 99276012
|
||||
grandphish2 1134273 68070459
|
||||
okrout 901194 77738874
|
||||
TueRens 821388 50207666
|
||||
tvijlbrief 795993 51894442
|
||||
pemo 744463 32486677
|
||||
JojoM 724378 43660674
|
||||
mibere 703840 46867607
|
||||
linrock 626939 17408017
|
||||
gvreuls 534079 34352532
|
||||
cw 507221 34006775
|
||||
fastgm 489749 29344518
|
||||
crunchy 427035 27344275
|
||||
CSU_Dynasty 424643 28525220
|
||||
ctoks 415771 27364603
|
||||
oz 369200 27017658
|
||||
bcross 342642 23671289
|
||||
Fisherman 327231 21829379
|
||||
velislav 325670 20911076
|
||||
leszek 321295 19874113
|
||||
Dantist 274747 16910258
|
||||
mgrabiak 237604 15418700
|
||||
robal 217959 13840386
|
||||
glinscott 217799 13780820
|
||||
nordlandia 211692 13484886
|
||||
drabel 201967 13798360
|
||||
bking_US 198894 11876016
|
||||
mhoram 194862 12261809
|
||||
Thanar 179852 12365359
|
||||
vdv 175544 9904472
|
||||
spams 157128 10319326
|
||||
rpngn 154081 9652139
|
||||
marrco 150300 9402229
|
||||
sqrt2 147963 9724586
|
||||
vdbergh 137430 8955097
|
||||
CoffeeOne 137100 5024116
|
||||
malala 136182 8002293
|
||||
xoto 133759 9159372
|
||||
davar 125240 8117121
|
||||
dsmith 122059 7570238
|
||||
amicic 119659 7937885
|
||||
Data 113305 8220352
|
||||
BrunoBanani 112960 7436849
|
||||
CypressChess 108321 7759588
|
||||
DesolatedDodo 106811 6776980
|
||||
MaZePallas 102823 6633619
|
||||
sterni1971 100532 5880772
|
||||
sunu 100167 7040199
|
||||
ElbertoOne 99028 7023771
|
||||
skiminki 98123 6478402
|
||||
brabos 92118 6186135
|
||||
cuistot 90358 5351004
|
||||
psk 89957 5984901
|
||||
racerschmacer 85712 6119648
|
||||
Vizvezdenec 83761 5344740
|
||||
zeryl 83680 5250995
|
||||
sschnee 83003 4840890
|
||||
0x3C33 82614 5271253
|
||||
BRAVONE 81239 5054681
|
||||
nssy 76497 5259388
|
||||
teddybaer 75125 5407666
|
||||
jromang 74796 5175825
|
||||
Pking_cda 73776 5293873
|
||||
Calis007 72477 4088576
|
||||
solarlight 70517 5028306
|
||||
dv8silencer 70287 3883992
|
||||
Bobo1239 68515 4652287
|
||||
manap 66273 4121774
|
||||
yurikvelo 65716 4457300
|
||||
tinker 64333 4268790
|
||||
Wolfgang 62644 3817410
|
||||
qurashee 61208 3429862
|
||||
robnjr 57262 4053117
|
||||
Freja 56938 3733019
|
||||
ttruscott 56010 3680085
|
||||
rkl 55132 4164467
|
||||
renouve 53811 3501516
|
||||
megaman7de 52434 3243016
|
||||
MaxKlaxxMiner 51977 3153032
|
||||
finfish 51360 3370515
|
||||
eva42 51272 3599691
|
||||
eastorwest 51058 3451555
|
||||
rap 49985 3219146
|
||||
pb00067 49727 3298270
|
||||
Spprtr 48920 3161711
|
||||
bigpen0r 47667 3336927
|
||||
ronaldjerum 47654 3240695
|
||||
biffhero 46564 3111352
|
||||
Fifis 45843 3088497
|
||||
VoyagerOne 45476 3452465
|
||||
speedycpu 43842 3003273
|
||||
jbwiebe 43305 2805433
|
||||
Antihistamine 41788 2761312
|
||||
mhunt 41735 2691355
|
||||
homyur 39893 2850481
|
||||
gri 39871 2515779
|
||||
armo9494 39064 2832326
|
||||
oryx 38867 2976992
|
||||
SC 37299 2731694
|
||||
Garf 37213 2986270
|
||||
tolkki963 37059 2154330
|
||||
csnodgrass 36207 2688994
|
||||
jmdana 36157 2210661
|
||||
strelock 34716 2074055
|
||||
DMBK 34010 2482916
|
||||
EthanOConnor 33370 2090311
|
||||
slakovv 32915 2021889
|
||||
gopeto 30993 2028106
|
||||
manapbk 30987 1810399
|
||||
Prcuvu 30377 2170122
|
||||
anst 30301 2190091
|
||||
jkiiski 30136 1904470
|
||||
hyperbolic.tom 29840 2017394
|
||||
chuckstablers 29659 2093438
|
||||
Pyafue 29650 1902349
|
||||
ncfish1 29105 1704011
|
||||
belzedar94 27935 1789106
|
||||
OuaisBla 27636 1578800
|
||||
chriswk 26902 1868317
|
||||
achambord 26582 1767323
|
||||
Patrick_G 26276 1801617
|
||||
yorkman 26193 1992080
|
||||
SFTUser 25182 1675689
|
||||
nabildanial 24942 1519409
|
||||
Sharaf_DG 24765 1786697
|
||||
rodneyc 24275 1410450
|
||||
agg177 23890 1395014
|
||||
JanErik 23408 1703875
|
||||
Isidor 23388 1680691
|
||||
Norabor 23339 1602636
|
||||
Ente 23270 1651432
|
||||
cisco2015 22897 1762669
|
||||
MarcusTullius 22688 1274821
|
||||
Zirie 22542 1472937
|
||||
team-oh 22272 1636708
|
||||
MazeOfGalious 21978 1629593
|
||||
sg4032 21947 1643265
|
||||
ianh2105 21725 1632562
|
||||
xor12 21628 1680365
|
||||
dex 21612 1467203
|
||||
nesoneg 21494 1463031
|
||||
Roady 21323 1433822
|
||||
sphinx 21211 1384728
|
||||
user213718 21196 1397710
|
||||
spcc 21065 1311338
|
||||
jjoshua2 21001 1423089
|
||||
horst.prack 20878 1465656
|
||||
0xB00B1ES 20590 1208666
|
||||
j3corre 20405 941444
|
||||
kdave 20364 1389254
|
||||
Adrian.Schmidt123 20316 1281436
|
||||
Ulysses 20217 1351500
|
||||
markkulix 19976 1115258
|
||||
wei 19973 1745989
|
||||
rstoesser 19569 1293588
|
||||
eudhan 19274 1283717
|
||||
fishtester 18995 1238686
|
||||
vulcan 18871 1729392
|
||||
jundery 18445 1115855
|
||||
iisiraider 18247 1101015
|
||||
ville 17883 1384026
|
||||
chris 17698 1487385
|
||||
purplefishies 17595 1092533
|
||||
dju 17353 978595
|
||||
Wencey 17125 805964
|
||||
DragonLord 17014 1162790
|
||||
thirdlife 16996 447356
|
||||
IgorLeMasson 16064 1147232
|
||||
ako027ako 15671 1173203
|
||||
AndreasKrug 15550 1194497
|
||||
Nikolay.IT 15154 1068349
|
||||
Andrew Grant 15114 895539
|
||||
scuzzi 14928 953313
|
||||
OssumOpossum 14857 1007129
|
||||
Karby 14808 867120
|
||||
jsys14 14652 855642
|
||||
enedene 14476 905279
|
||||
bpfliegel 14298 884523
|
||||
mpx86 14019 759568
|
||||
jpulman 13982 870599
|
||||
crocogoat 13803 1117422
|
||||
joster 13794 950160
|
||||
Nesa92 13786 1114691
|
||||
mbeier 13650 1044928
|
||||
Hjax 13535 915487
|
||||
Dark_wizzie 13422 1007152
|
||||
Jopo12321 13367 678852
|
||||
Rudolphous 13244 883140
|
||||
Machariel 13010 863104
|
||||
mabichito 12903 749391
|
||||
thijsk 12886 722107
|
||||
AdrianSA 12860 804972
|
||||
infinigon 12807 937332
|
||||
Flopzee 12698 894821
|
||||
fatmurphy 12547 853210
|
||||
SapphireBrand 12416 969604
|
||||
modolief 12386 896470
|
||||
Farseer 12249 694108
|
||||
pgontarz 12151 848794
|
||||
pirt 12008 923149
|
||||
stocky 11954 699440
|
||||
mschmidt 11941 803401
|
||||
dbernier 11609 818636
|
||||
Maxim 11543 836024
|
||||
infinity 11470 727027
|
||||
aga 11409 695071
|
||||
torbjo 11395 729145
|
||||
Thomas A. Anderson 11372 732094
|
||||
savage84 11358 670860
|
||||
FormazChar 11349 850327
|
||||
d64 11263 789184
|
||||
MooTheCow 11237 720174
|
||||
snicolet 11106 869170
|
||||
ali-al-zhrani 11098 768494
|
||||
whelanh 11067 235676
|
||||
Jackfish 10978 720078
|
||||
deflectooor 10886 520116
|
||||
basepi 10637 744851
|
||||
Cubox 10621 826448
|
||||
michaelrpg 10509 739239
|
||||
OIVAS7572 10420 995586
|
||||
dzjp 10343 732529
|
||||
Garruk 10334 704065
|
||||
ols 10259 570669
|
||||
lbraesch 10252 647825
|
||||
qoo_charly_cai 10212 620407
|
||||
Naven94 10069 503192
|
||||
Username CPU Hours Games played
|
||||
--------------------------------------------------
|
||||
noobpwnftw 9305707 695548021
|
||||
mlang 780050 61648867
|
||||
dew 621626 43921547
|
||||
mibere 524702 42238645
|
||||
crunchy 354587 27344275
|
||||
cw 354495 27274181
|
||||
fastgm 332801 22804359
|
||||
JojoM 295750 20437451
|
||||
CSU_Dynasty 262015 21828122
|
||||
Fisherman 232181 18939229
|
||||
ctoks 218866 17622052
|
||||
glinscott 201989 13780820
|
||||
tvijlbrief 201204 15337115
|
||||
velislav 188630 14348485
|
||||
gvreuls 187164 15149976
|
||||
bking_US 180289 11876016
|
||||
nordlandia 172076 13467830
|
||||
leszek 157152 11443978
|
||||
Thanar 148021 12365359
|
||||
spams 141975 10319326
|
||||
drabel 138073 11121749
|
||||
vdv 137850 9394330
|
||||
mgrabiak 133578 10454324
|
||||
TueRens 132485 10878471
|
||||
bcross 129683 11557084
|
||||
marrco 126078 9356740
|
||||
sqrt2 125830 9724586
|
||||
robal 122873 9593418
|
||||
vdbergh 120766 8926915
|
||||
malala 115926 8002293
|
||||
CoffeeOne 114241 5004100
|
||||
dsmith 113189 7570238
|
||||
BrunoBanani 104644 7436849
|
||||
Data 92328 8220352
|
||||
mhoram 89333 6695109
|
||||
davar 87924 7009424
|
||||
xoto 81094 6869316
|
||||
ElbertoOne 80899 7023771
|
||||
grandphish2 78067 6160199
|
||||
brabos 77212 6186135
|
||||
psk 75733 5984901
|
||||
BRAVONE 73875 5054681
|
||||
sunu 70771 5597972
|
||||
sterni1971 70605 5590573
|
||||
MaZePallas 66886 5188978
|
||||
Vizvezdenec 63708 4967313
|
||||
nssy 63462 5259388
|
||||
jromang 61634 4940891
|
||||
teddybaer 61231 5407666
|
||||
Pking_cda 60099 5293873
|
||||
solarlight 57469 5028306
|
||||
dv8silencer 56913 3883992
|
||||
tinker 54936 4086118
|
||||
renouve 49732 3501516
|
||||
Freja 49543 3733019
|
||||
robnjr 46972 4053117
|
||||
rap 46563 3219146
|
||||
Bobo1239 46036 3817196
|
||||
ttruscott 45304 3649765
|
||||
racerschmacer 44881 3975413
|
||||
finfish 44764 3370515
|
||||
eva42 41783 3599691
|
||||
biffhero 40263 3111352
|
||||
bigpen0r 39817 3291647
|
||||
mhunt 38871 2691355
|
||||
ronaldjerum 38820 3240695
|
||||
Antihistamine 38785 2761312
|
||||
pb00067 38038 3086320
|
||||
speedycpu 37591 3003273
|
||||
rkl 37207 3289580
|
||||
VoyagerOne 37050 3441673
|
||||
jbwiebe 35320 2805433
|
||||
cuistot 34191 2146279
|
||||
homyur 33927 2850481
|
||||
manap 32873 2327384
|
||||
gri 32538 2515779
|
||||
oryx 31267 2899051
|
||||
EthanOConnor 30959 2090311
|
||||
SC 30832 2730764
|
||||
csnodgrass 29505 2688994
|
||||
jmdana 29458 2205261
|
||||
strelock 28219 2067805
|
||||
jkiiski 27832 1904470
|
||||
Pyafue 27533 1902349
|
||||
Garf 27515 2747562
|
||||
eastorwest 27421 2317535
|
||||
slakovv 26903 2021889
|
||||
Prcuvu 24835 2170122
|
||||
anst 24714 2190091
|
||||
hyperbolic.tom 24319 2017394
|
||||
Patrick_G 23687 1801617
|
||||
Sharaf_DG 22896 1786697
|
||||
nabildanial 22195 1519409
|
||||
chriswk 21931 1868317
|
||||
achambord 21665 1767323
|
||||
Zirie 20887 1472937
|
||||
team-oh 20217 1636708
|
||||
Isidor 20096 1680691
|
||||
ncfish1 19931 1520927
|
||||
nesoneg 19875 1463031
|
||||
Spprtr 19853 1548165
|
||||
JanErik 19849 1703875
|
||||
agg177 19478 1395014
|
||||
SFTUser 19231 1567999
|
||||
xor12 19017 1680165
|
||||
sg4032 18431 1641865
|
||||
rstoesser 18118 1293588
|
||||
MazeOfGalious 17917 1629593
|
||||
j3corre 17743 941444
|
||||
cisco2015 17725 1690126
|
||||
ianh2105 17706 1632562
|
||||
dex 17678 1467203
|
||||
jundery 17194 1115855
|
||||
iisiraider 17019 1101015
|
||||
horst.prack 17012 1465656
|
||||
Adrian.Schmidt123 16563 1281436
|
||||
purplefishies 16342 1092533
|
||||
wei 16274 1745989
|
||||
ville 16144 1384026
|
||||
eudhan 15712 1283717
|
||||
OuaisBla 15581 972000
|
||||
DragonLord 15559 1162790
|
||||
dju 14716 875569
|
||||
chris 14479 1487385
|
||||
0xB00B1ES 14079 1001120
|
||||
OssumOpossum 13776 1007129
|
||||
enedene 13460 905279
|
||||
bpfliegel 13346 884523
|
||||
Ente 13198 1156722
|
||||
IgorLeMasson 13087 1147232
|
||||
jpulman 13000 870599
|
||||
ako027ako 12775 1173203
|
||||
Nikolay.IT 12352 1068349
|
||||
Andrew Grant 12327 895539
|
||||
joster 12008 950160
|
||||
AdrianSA 11996 804972
|
||||
Nesa92 11455 1111993
|
||||
fatmurphy 11345 853210
|
||||
Dark_wizzie 11108 1007152
|
||||
modolief 10869 896470
|
||||
mschmidt 10757 803401
|
||||
infinity 10594 727027
|
||||
mabichito 10524 749391
|
||||
Thomas A. Anderson 10474 732094
|
||||
thijsk 10431 719357
|
||||
Flopzee 10339 894821
|
||||
crocogoat 10104 1013854
|
||||
SapphireBrand 10104 969604
|
||||
stocky 10017 699440
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
version: 1.0.{build}
|
||||
clone_depth: 50
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- nnue-player-wip
|
||||
|
||||
# Operating system (build VM template)
|
||||
os: Visual Studio 2019
|
||||
|
||||
# Build platform, i.e. x86, x64, AnyCPU. This setting is optional.
|
||||
platform:
|
||||
- x86
|
||||
- x64
|
||||
|
||||
# build Configuration, i.e. Debug, Release, etc.
|
||||
configuration:
|
||||
- Debug
|
||||
- Release
|
||||
|
||||
matrix:
|
||||
# The build fail immediately once one of the job fails
|
||||
fast_finish: true
|
||||
|
||||
# Scripts that are called at very beginning, before repo cloning
|
||||
init:
|
||||
- cmake --version
|
||||
- msbuild /version
|
||||
|
||||
before_build:
|
||||
- ps: |
|
||||
# Get sources
|
||||
$src = get-childitem -Path *.cpp -Recurse | select -ExpandProperty FullName
|
||||
$src = $src -join ' '
|
||||
$src = $src.Replace("\", "/")
|
||||
|
||||
# Build CMakeLists.txt
|
||||
$t = 'cmake_minimum_required(VERSION 3.17)',
|
||||
'project(Stockfish)',
|
||||
'set(CMAKE_CXX_STANDARD 17)',
|
||||
'set(CMAKE_CXX_STANDARD_REQUIRED ON)',
|
||||
'set (CMAKE_CXX_EXTENSIONS OFF)',
|
||||
'set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src)',
|
||||
'set(source_files', $src, ')',
|
||||
'add_executable(stockfish ${source_files})'
|
||||
|
||||
# Write CMakeLists.txt withouth BOM
|
||||
$MyPath = (Get-Item -Path "." -Verbose).FullName + '\CMakeLists.txt'
|
||||
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
|
||||
[System.IO.File]::WriteAllLines($MyPath, $t, $Utf8NoBomEncoding)
|
||||
|
||||
# Obtain bench reference from git log
|
||||
$b = git log HEAD | sls "\b[Bb]ench[ :]+[0-9]{7}" | select -first 1
|
||||
$bench = $b -match '\D+(\d+)' | % { $matches[1] }
|
||||
Write-Host "Reference bench:" $bench
|
||||
$g = "Visual Studio 16 2019"
|
||||
If (${env:PLATFORM} -eq 'x64') { $a = "x64" }
|
||||
If (${env:PLATFORM} -eq 'x86') { $a = "Win32" }
|
||||
cmake -G "${g}" -A ${a} .
|
||||
Write-Host "Generated files for: " $g $a
|
||||
|
||||
build_script:
|
||||
- cmake --build . --config %CONFIGURATION% -- /verbosity:minimal
|
||||
|
||||
before_test:
|
||||
- cd src/%CONFIGURATION%
|
||||
- stockfish bench 2> out.txt >NUL
|
||||
- ps: |
|
||||
# Verify bench number
|
||||
$s = (gc "./out.txt" | out-string)
|
||||
$r = ($s -match 'Nodes searched \D+(\d+)' | % { $matches[1] })
|
||||
Write-Host "Engine bench:" $r
|
||||
Write-Host "Reference bench:" $bench
|
||||
If ($r -ne $bench) { exit 1 }
|
||||
+236
-471
File diff suppressed because it is too large
Load Diff
+3
-19
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -87,20 +87,16 @@ const vector<string> Defaults = {
|
||||
// Chess 960
|
||||
"setoption name UCI_Chess960 value true",
|
||||
"bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w HFhf - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6",
|
||||
"nqbnrkrb/pppppppp/8/8/8/8/PPPPPPPP/NQBNRKRB w KQkq - 0 1",
|
||||
"setoption name UCI_Chess960 value false"
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
/// setup_bench() builds a list of UCI commands to be run by bench. There
|
||||
/// are five parameters: TT size in MB, number of search threads that
|
||||
/// should be used, the limit value spent for each position, a file name
|
||||
/// where to look for positions in FEN format, the type of the limit:
|
||||
/// depth, perft, nodes and movetime (in millisecs), and evaluation type
|
||||
/// mixed (default), classical, NNUE.
|
||||
/// where to look for positions in FEN format and the type of the limit:
|
||||
/// depth, perft, nodes and movetime (in millisecs).
|
||||
///
|
||||
/// bench -> search default positions up to depth 13
|
||||
/// bench 64 1 15 -> search default positions up to depth 15 (TT = 64MB)
|
||||
@@ -119,7 +115,6 @@ vector<string> setup_bench(const Position& current, istream& is) {
|
||||
string limit = (is >> token) ? token : "13";
|
||||
string fenFile = (is >> token) ? token : "default";
|
||||
string limitType = (is >> token) ? token : "depth";
|
||||
string evalType = (is >> token) ? token : "mixed";
|
||||
|
||||
go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit;
|
||||
|
||||
@@ -151,25 +146,14 @@ vector<string> setup_bench(const Position& current, istream& is) {
|
||||
list.emplace_back("setoption name Hash value " + ttSize);
|
||||
list.emplace_back("ucinewgame");
|
||||
|
||||
size_t posCounter = 0;
|
||||
|
||||
for (const string& fen : fens)
|
||||
if (fen.find("setoption") != string::npos)
|
||||
list.emplace_back(fen);
|
||||
else
|
||||
{
|
||||
if (evalType == "classical" || (evalType == "mixed" && posCounter % 2 == 0))
|
||||
list.emplace_back("setoption name Use NNUE value false");
|
||||
else if (evalType == "NNUE" || (evalType == "mixed" && posCounter % 2 != 0))
|
||||
list.emplace_back("setoption name Use NNUE value true");
|
||||
list.emplace_back("position fen " + fen);
|
||||
list.emplace_back(go);
|
||||
++posCounter;
|
||||
}
|
||||
|
||||
list.emplace_back("setoption name Use NNUE value true");
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+5
-7
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -23,8 +23,6 @@
|
||||
#include "bitboard.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
namespace {
|
||||
|
||||
// There are 24 possible pawn squares: files A to D and ranks from 2 to 7.
|
||||
@@ -68,6 +66,7 @@ namespace {
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) {
|
||||
|
||||
assert(file_of(wpsq) <= FILE_D);
|
||||
@@ -97,6 +96,7 @@ void Bitbases::init() {
|
||||
KPKBitbase.set(idx);
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
KPKPosition::KPKPosition(unsigned idx) {
|
||||
@@ -150,8 +150,8 @@ namespace {
|
||||
Bitboard b = attacks_bb<KING>(ksq[stm]);
|
||||
|
||||
while (b)
|
||||
r |= stm == WHITE ? db[index(BLACK, ksq[BLACK], pop_lsb(b), psq)]
|
||||
: db[index(WHITE, pop_lsb(b), ksq[WHITE], psq)];
|
||||
r |= stm == WHITE ? db[index(BLACK, ksq[BLACK] , pop_lsb(&b), psq)]
|
||||
: db[index(WHITE, pop_lsb(&b), ksq[WHITE], psq)];
|
||||
|
||||
if (stm == WHITE)
|
||||
{
|
||||
@@ -168,5 +168,3 @@ namespace {
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+6
-25
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -22,14 +22,11 @@
|
||||
#include "bitboard.h"
|
||||
#include "misc.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
uint8_t PopCnt16[1 << 16];
|
||||
uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||
|
||||
Bitboard SquareBB[SQUARE_NB];
|
||||
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
||||
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
||||
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||
|
||||
@@ -42,22 +39,13 @@ namespace {
|
||||
Bitboard BishopTable[0x1480]; // To store bishop attacks
|
||||
|
||||
void init_magics(PieceType pt, Bitboard table[], Magic magics[]);
|
||||
|
||||
}
|
||||
|
||||
/// safe_destination() returns the bitboard of target square for the given step
|
||||
/// from the given square. If the step is off the board, returns empty bitboard.
|
||||
|
||||
inline Bitboard safe_destination(Square s, int step) {
|
||||
Square to = Square(s + step);
|
||||
return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
|
||||
}
|
||||
|
||||
|
||||
/// Bitboards::pretty() returns an ASCII representation of a bitboard suitable
|
||||
/// to be printed to standard output. Useful for debugging.
|
||||
|
||||
std::string Bitboards::pretty(Bitboard b) {
|
||||
const std::string Bitboards::pretty(Bitboard b) {
|
||||
|
||||
std::string s = "+---+---+---+---+---+---+---+---+\n";
|
||||
|
||||
@@ -108,17 +96,12 @@ void Bitboards::init() {
|
||||
|
||||
for (PieceType pt : { BISHOP, ROOK })
|
||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||
{
|
||||
if (PseudoAttacks[pt][s1] & s2)
|
||||
{
|
||||
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
|
||||
BetweenBB[s1][s2] = (attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1)));
|
||||
}
|
||||
BetweenBB[s1][s2] |= s2;
|
||||
}
|
||||
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
|
||||
@@ -127,10 +110,10 @@ namespace {
|
||||
Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST};
|
||||
Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
|
||||
|
||||
for (Direction d : (pt == ROOK ? RookDirections : BishopDirections))
|
||||
for(Direction d : (pt == ROOK ? RookDirections : BishopDirections))
|
||||
{
|
||||
Square s = sq;
|
||||
while (safe_destination(s, d) && !(occupied & s))
|
||||
while(safe_destination(s, d) && !(occupied & s))
|
||||
attacks |= (s += d);
|
||||
}
|
||||
|
||||
@@ -218,5 +201,3 @@ namespace {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+26
-34
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -23,21 +23,19 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
namespace Bitbases {
|
||||
|
||||
void init();
|
||||
bool probe(Square wksq, Square wpsq, Square bksq, Color us);
|
||||
|
||||
} // namespace Stockfish::Bitbases
|
||||
}
|
||||
|
||||
namespace Bitboards {
|
||||
|
||||
void init();
|
||||
std::string pretty(Bitboard b);
|
||||
const std::string pretty(Bitboard b);
|
||||
|
||||
} // namespace Stockfish::Bitboards
|
||||
}
|
||||
|
||||
constexpr Bitboard AllSquares = ~Bitboard(0);
|
||||
constexpr Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL;
|
||||
@@ -75,7 +73,6 @@ extern uint8_t PopCnt16[1 << 16];
|
||||
extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||
|
||||
extern Bitboard SquareBB[SQUARE_NB];
|
||||
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
|
||||
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
|
||||
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||
@@ -212,29 +209,23 @@ constexpr Bitboard adjacent_files_bb(Square s) {
|
||||
inline Bitboard line_bb(Square s1, Square s2) {
|
||||
|
||||
assert(is_ok(s1) && is_ok(s2));
|
||||
|
||||
return LineBB[s1][s2];
|
||||
}
|
||||
|
||||
|
||||
/// between_bb(s1, s2) returns a bitboard representing the squares in the semi-open
|
||||
/// segment between the squares s1 and s2 (excluding s1 but including s2). If the
|
||||
/// given squares are not on a same file/rank/diagonal, it returns s2. For instance,
|
||||
/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5, E6 and F7, but
|
||||
/// between_bb(SQ_E6, SQ_F8) will return a bitboard with the square F8. This trick
|
||||
/// allows to generate non-king evasion moves faster: the defending piece must either
|
||||
/// interpose itself to cover the check or capture the checking piece.
|
||||
/// between_bb() returns a bitboard representing squares that are linearly
|
||||
/// between the two given squares (excluding the given squares). If the given
|
||||
/// squares are not on a same file/rank/diagonal, we return 0. For instance,
|
||||
/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5 and E6.
|
||||
|
||||
inline Bitboard between_bb(Square s1, Square s2) {
|
||||
|
||||
assert(is_ok(s1) && is_ok(s2));
|
||||
|
||||
return BetweenBB[s1][s2];
|
||||
Bitboard b = line_bb(s1, s2) & ((AllSquares << s1) ^ (AllSquares << s2));
|
||||
return b & (b - 1); //exclude lsb
|
||||
}
|
||||
|
||||
|
||||
/// forward_ranks_bb() returns a bitboard representing the squares on the ranks in
|
||||
/// front of the given one, from the point of view of the given color. For instance,
|
||||
/// forward_ranks_bb() returns a bitboard representing the squares on the ranks
|
||||
/// in front of the given one, from the point of view of the given color. For instance,
|
||||
/// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2.
|
||||
|
||||
constexpr Bitboard forward_ranks_bb(Color c, Square s) {
|
||||
@@ -288,6 +279,16 @@ inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); }
|
||||
inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); }
|
||||
|
||||
|
||||
/// safe_destination() returns the bitboard of target square for the given step
|
||||
/// from the given square. If the step is off the board, returns empty bitboard.
|
||||
|
||||
inline Bitboard safe_destination(Square s, int step)
|
||||
{
|
||||
Square to = Square(s + step);
|
||||
return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
|
||||
}
|
||||
|
||||
|
||||
/// attacks_bb(Square) returns the pseudo attacks of the give piece type
|
||||
/// assuming an empty board.
|
||||
|
||||
@@ -421,20 +422,13 @@ inline Square msb(Bitboard b) {
|
||||
|
||||
#endif
|
||||
|
||||
/// least_significant_square_bb() returns the bitboard of the least significant
|
||||
/// square of a non-zero bitboard. It is equivalent to square_bb(lsb(bb)).
|
||||
|
||||
inline Bitboard least_significant_square_bb(Bitboard b) {
|
||||
assert(b);
|
||||
return b & -b;
|
||||
}
|
||||
|
||||
/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard
|
||||
|
||||
inline Square pop_lsb(Bitboard& b) {
|
||||
assert(b);
|
||||
const Square s = lsb(b);
|
||||
b &= b - 1;
|
||||
inline Square pop_lsb(Bitboard* b) {
|
||||
assert(*b);
|
||||
const Square s = lsb(*b);
|
||||
*b &= *b - 1;
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -446,6 +440,4 @@ inline Square frontmost_sq(Color c, Bitboard b) {
|
||||
return c == WHITE ? msb(b) : lsb(b);
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef BITBOARD_H_INCLUDED
|
||||
|
||||
+5
-9
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -22,8 +22,6 @@
|
||||
#include "endgame.h"
|
||||
#include "movegen.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
namespace {
|
||||
|
||||
// Used to drive the king towards the edge of the board
|
||||
@@ -555,8 +553,8 @@ ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
|
||||
assert(verify_material(pos, strongSide, RookValueMg, 2));
|
||||
assert(verify_material(pos, weakSide, RookValueMg, 1));
|
||||
|
||||
Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN));
|
||||
Square strongPawn2 = msb(pos.pieces(strongSide, PAWN));
|
||||
Square strongPawn1 = pos.squares<PAWN>(strongSide)[0];
|
||||
Square strongPawn2 = pos.squares<PAWN>(strongSide)[1];
|
||||
Square weakKing = pos.square<KING>(weakSide);
|
||||
|
||||
// Does the stronger side have a passed pawn?
|
||||
@@ -640,8 +638,8 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
|
||||
return SCALE_FACTOR_NONE;
|
||||
|
||||
Square weakKing = pos.square<KING>(weakSide);
|
||||
Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN));
|
||||
Square strongPawn2 = msb(pos.pieces(strongSide, PAWN));
|
||||
Square strongPawn1 = pos.squares<PAWN>(strongSide)[0];
|
||||
Square strongPawn2 = pos.squares<PAWN>(strongSide)[1];
|
||||
Square blockSq1, blockSq2;
|
||||
|
||||
if (relative_rank(strongSide, strongPawn1) > relative_rank(strongSide, strongPawn2))
|
||||
@@ -743,5 +741,3 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
|
||||
// it's probably at least a draw even with the pawn.
|
||||
return Bitbases::probe(strongKing, strongPawn, weakKing, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+1
-4
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "position.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
/// EndgameCode lists all supported endgame functions by corresponding codes
|
||||
|
||||
@@ -121,6 +120,4 @@ namespace Endgames {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef ENDGAME_H_INCLUDED
|
||||
|
||||
+167
-362
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -20,132 +20,46 @@
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstring> // For std::memset
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <streambuf>
|
||||
#include <vector>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "evaluate.h"
|
||||
#include "material.h"
|
||||
#include "misc.h"
|
||||
#include "pawns.h"
|
||||
#include "thread.h"
|
||||
#include "timeman.h"
|
||||
#include "uci.h"
|
||||
#include "incbin/incbin.h"
|
||||
|
||||
|
||||
// Macro to embed the default efficiently updatable neural network (NNUE) file
|
||||
// data in the engine binary (using incbin.h, by Dale Weiler).
|
||||
// This macro invocation will declare the following three variables
|
||||
// const unsigned char gEmbeddedNNUEData[]; // a pointer to the embedded data
|
||||
// const unsigned char *const gEmbeddedNNUEEnd; // a marker to the end
|
||||
// const unsigned int gEmbeddedNNUESize; // the size of the embedded file
|
||||
// Note that this does not work in Microsoft Visual Studio.
|
||||
#if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF)
|
||||
INCBIN(EmbeddedNNUE, EvalFileDefaultName);
|
||||
#else
|
||||
const unsigned char gEmbeddedNNUEData[1] = {0x0};
|
||||
const unsigned char *const gEmbeddedNNUEEnd = &gEmbeddedNNUEData[1];
|
||||
const unsigned int gEmbeddedNNUESize = 1;
|
||||
#endif
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
namespace Eval {
|
||||
|
||||
bool useNNUE;
|
||||
string currentEvalFileName = "None";
|
||||
std::string eval_file_loaded="None";
|
||||
|
||||
/// NNUE::init() tries to load a NNUE network at startup time, or when the engine
|
||||
/// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue"
|
||||
/// The name of the NNUE network is always retrieved from the EvalFile option.
|
||||
/// We search the given network in three locations: internally (the default
|
||||
/// network may be embedded in the binary), in the active working directory and
|
||||
/// in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY
|
||||
/// variable to have the engine search in a special directory in their distro.
|
||||
|
||||
void NNUE::init() {
|
||||
void init_NNUE() {
|
||||
|
||||
useNNUE = Options["Use NNUE"];
|
||||
if (!useNNUE)
|
||||
return;
|
||||
|
||||
string eval_file = string(Options["EvalFile"]);
|
||||
if (eval_file.empty())
|
||||
eval_file = EvalFileDefaultName;
|
||||
|
||||
#if defined(DEFAULT_NNUE_DIRECTORY)
|
||||
#define stringify2(x) #x
|
||||
#define stringify(x) stringify2(x)
|
||||
vector<string> dirs = { "<internal>" , "" , CommandLine::binaryDirectory , stringify(DEFAULT_NNUE_DIRECTORY) };
|
||||
#else
|
||||
vector<string> dirs = { "<internal>" , "" , CommandLine::binaryDirectory };
|
||||
#endif
|
||||
|
||||
for (string directory : dirs)
|
||||
if (currentEvalFileName != eval_file)
|
||||
{
|
||||
if (directory != "<internal>")
|
||||
{
|
||||
ifstream stream(directory + eval_file, ios::binary);
|
||||
if (load_eval(eval_file, stream))
|
||||
currentEvalFileName = eval_file;
|
||||
}
|
||||
|
||||
if (directory == "<internal>" && eval_file == EvalFileDefaultName)
|
||||
{
|
||||
// C++ way to prepare a buffer for a memory stream
|
||||
class MemoryBuffer : public basic_streambuf<char> {
|
||||
public: MemoryBuffer(char* p, size_t n) { setg(p, p, p + n); setp(p, p + n); }
|
||||
};
|
||||
|
||||
MemoryBuffer buffer(const_cast<char*>(reinterpret_cast<const char*>(gEmbeddedNNUEData)),
|
||||
size_t(gEmbeddedNNUESize));
|
||||
(void) gEmbeddedNNUEEnd; // Silence warning on unused variable
|
||||
|
||||
istream stream(&buffer);
|
||||
if (load_eval(eval_file, stream))
|
||||
currentEvalFileName = eval_file;
|
||||
}
|
||||
}
|
||||
std::string eval_file = std::string(Options["EvalFile"]);
|
||||
if (useNNUE && eval_file_loaded != eval_file)
|
||||
if (Eval::NNUE::load_eval_file(eval_file))
|
||||
eval_file_loaded = eval_file;
|
||||
}
|
||||
|
||||
/// NNUE::verify() verifies that the last net used was loaded successfully
|
||||
void NNUE::verify() {
|
||||
void verify_NNUE() {
|
||||
|
||||
string eval_file = string(Options["EvalFile"]);
|
||||
if (eval_file.empty())
|
||||
eval_file = EvalFileDefaultName;
|
||||
|
||||
if (useNNUE && currentEvalFileName != eval_file)
|
||||
std::string eval_file = std::string(Options["EvalFile"]);
|
||||
if (useNNUE && eval_file_loaded != eval_file)
|
||||
{
|
||||
|
||||
string msg1 = "If the UCI option \"Use NNUE\" is set to true, network evaluation parameters compatible with the engine must be available.";
|
||||
string msg2 = "The option is set to true, but the network file " + eval_file + " was not loaded successfully.";
|
||||
string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file.";
|
||||
string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + std::string(EvalFileDefaultName);
|
||||
string msg5 = "The engine will be terminated now.";
|
||||
|
||||
sync_cout << "info string ERROR: " << msg1 << sync_endl;
|
||||
sync_cout << "info string ERROR: " << msg2 << sync_endl;
|
||||
sync_cout << "info string ERROR: " << msg3 << sync_endl;
|
||||
sync_cout << "info string ERROR: " << msg4 << sync_endl;
|
||||
sync_cout << "info string ERROR: " << msg5 << sync_endl;
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
std::cerr << "Use of NNUE evaluation, but the file " << eval_file << " was not loaded successfully. "
|
||||
<< "These network evaluation parameters must be available, compatible with this version of the code. "
|
||||
<< "The UCI option EvalFile might need to specify the full path, including the directory/folder name, to the file." << std::endl;
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (useNNUE)
|
||||
sync_cout << "info string NNUE evaluation using " << eval_file << " enabled" << sync_endl;
|
||||
sync_cout << "info string NNUE evaluation using " << eval_file << " enabled." << sync_endl;
|
||||
else
|
||||
sync_cout << "info string classical evaluation enabled" << sync_endl;
|
||||
sync_cout << "info string classical evaluation enabled." << sync_endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +97,7 @@ namespace Trace {
|
||||
else
|
||||
os << scores[t][WHITE] << " | " << scores[t][BLACK];
|
||||
|
||||
os << " | " << scores[t][WHITE] - scores[t][BLACK] << " |\n";
|
||||
os << " | " << scores[t][WHITE] - scores[t][BLACK] << "\n";
|
||||
return os;
|
||||
}
|
||||
}
|
||||
@@ -193,17 +107,17 @@ using namespace Trace;
|
||||
namespace {
|
||||
|
||||
// Threshold for lazy and space evaluation
|
||||
constexpr Value LazyThreshold1 = Value(3631);
|
||||
constexpr Value LazyThreshold2 = Value(2084);
|
||||
constexpr Value SpaceThreshold = Value(11551);
|
||||
constexpr Value LazyThreshold1 = Value(1400);
|
||||
constexpr Value LazyThreshold2 = Value(1300);
|
||||
constexpr Value SpaceThreshold = Value(12222);
|
||||
|
||||
// KingAttackWeights[PieceType] contains king attack weights by piece type
|
||||
constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 76, 46, 45, 14 };
|
||||
constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 };
|
||||
|
||||
// SafeCheck[PieceType][single/multiple] contains safe check bonus by piece type,
|
||||
// higher if multiple safe checks are possible for that piece type.
|
||||
constexpr int SafeCheck[][2] = {
|
||||
{}, {}, {805, 1292}, {650, 984}, {1071, 1886}, {730, 1128}
|
||||
{}, {}, {792, 1283}, {645, 967}, {1084, 1897}, {772, 1119}
|
||||
};
|
||||
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
@@ -211,76 +125,73 @@ namespace {
|
||||
// MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game,
|
||||
// indexed by piece type and number of attacked squares in the mobility area.
|
||||
constexpr Score MobilityBonus[][32] = {
|
||||
{ S(-62,-79), S(-53,-57), S(-12,-31), S( -3,-17), S( 3, 7), S( 12, 13), // Knight
|
||||
S( 21, 16), S( 28, 21), S( 37, 26) },
|
||||
{ S(-47,-59), S(-20,-25), S( 14, -8), S( 29, 12), S( 39, 21), S( 53, 40), // Bishop
|
||||
S( 53, 56), S( 60, 58), S( 62, 65), S( 69, 72), S( 78, 78), S( 83, 87),
|
||||
S( 91, 88), S( 96, 98) },
|
||||
{ S(-60,-82), S(-24,-15), S( 0, 17) ,S( 3, 43), S( 4, 72), S( 14,100), // Rook
|
||||
S( 20,102), S( 30,122), S( 41,133), S(41 ,139), S( 41,153), S( 45,160),
|
||||
S( 57,165), S( 58,170), S( 67,175) },
|
||||
{ S(-29,-49), S(-16,-29), S( -8, -8), S( -8, 17), S( 18, 39), S( 25, 54), // Queen
|
||||
S( 23, 59), S( 37, 73), S( 41, 76), S( 54, 95), S( 65, 95) ,S( 68,101),
|
||||
S( 69,124), S( 70,128), S( 70,132), S( 70,133) ,S( 71,136), S( 72,140),
|
||||
S( 74,147), S( 76,149), S( 90,153), S(104,169), S(105,171), S(106,171),
|
||||
S(112,178), S(114,185), S(114,187), S(119,221) }
|
||||
};
|
||||
|
||||
// BishopPawns[distance from edge] contains a file-dependent penalty for pawns on
|
||||
// squares of the same color as our bishop.
|
||||
constexpr Score BishopPawns[int(FILE_NB) / 2] = {
|
||||
S(3, 8), S(3, 9), S(2, 7), S(3, 7)
|
||||
{ S(-62,-81), S(-53,-56), S(-12,-31), S( -4,-16), S( 3, 5), S( 13, 11), // Knight
|
||||
S( 22, 17), S( 28, 20), S( 33, 25) },
|
||||
{ S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishop
|
||||
S( 55, 54), S( 63, 57), S( 63, 65), S( 68, 73), S( 81, 78), S( 81, 86),
|
||||
S( 91, 88), S( 98, 97) },
|
||||
{ S(-60,-78), S(-20,-17), S( 2, 23), S( 3, 39), S( 3, 70), S( 11, 99), // Rook
|
||||
S( 22,103), S( 31,121), S( 40,134), S( 40,139), S( 41,158), S( 48,164),
|
||||
S( 57,168), S( 57,169), S( 62,172) },
|
||||
{ S(-30,-48), S(-12,-30), S( -8, -7), S( -9, 19), S( 20, 40), S( 23, 55), // Queen
|
||||
S( 23, 59), S( 35, 75), S( 38, 78), S( 53, 96), S( 64, 96), S( 65,100),
|
||||
S( 65,121), S( 66,127), S( 67,131), S( 67,133), S( 72,136), S( 72,141),
|
||||
S( 77,147), S( 79,150), S( 93,151), S(108,168), S(108,168), S(108,171),
|
||||
S(110,182), S(114,182), S(114,192), S(116,219) }
|
||||
};
|
||||
|
||||
// KingProtector[knight/bishop] contains penalty for each distance unit to own king
|
||||
constexpr Score KingProtector[] = { S(9, 9), S(7, 9) };
|
||||
constexpr Score KingProtector[] = { S(8, 9), S(6, 9) };
|
||||
|
||||
// Outpost[knight/bishop] contains bonuses for each knight or bishop occupying a
|
||||
// pawn protected square on rank 4 to 6 which is also safe from a pawn attack.
|
||||
constexpr Score Outpost[] = { S(54, 34), S(31, 25) };
|
||||
constexpr Score Outpost[] = { S(56, 36), S(30, 23) };
|
||||
|
||||
// PassedRank[Rank] contains a bonus according to the rank of a passed pawn
|
||||
constexpr Score PassedRank[RANK_NB] = {
|
||||
S(0, 0), S(2, 38), S(15, 36), S(22, 50), S(64, 81), S(166, 184), S(284, 269)
|
||||
S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
|
||||
};
|
||||
|
||||
constexpr Score RookOnClosedFile = S(10, 5);
|
||||
constexpr Score RookOnOpenFile[] = { S(18, 8), S(49, 26) };
|
||||
// RookOnFile[semiopen/open] contains bonuses for each rook when there is
|
||||
// no (friendly) pawn on the rook file.
|
||||
constexpr Score RookOnFile[] = { S(19, 7), S(48, 29) };
|
||||
|
||||
// ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to
|
||||
// which piece type attacks which one. Attacks on lesser pieces which are
|
||||
// pawn-defended are not considered.
|
||||
constexpr Score ThreatByMinor[PIECE_TYPE_NB] = {
|
||||
S(0, 0), S(6, 37), S(64, 50), S(82, 57), S(103, 130), S(81, 163)
|
||||
S(0, 0), S(5, 32), S(57, 41), S(77, 56), S(88, 119), S(79, 161)
|
||||
};
|
||||
|
||||
constexpr Score ThreatByRook[PIECE_TYPE_NB] = {
|
||||
S(0, 0), S(3, 44), S(36, 71), S(44, 59), S(0, 39), S(60, 39)
|
||||
S(0, 0), S(3, 46), S(37, 68), S(42, 60), S(0, 38), S(58, 41)
|
||||
};
|
||||
|
||||
constexpr Value CorneredBishop = Value(50);
|
||||
|
||||
// Assorted bonuses and penalties
|
||||
constexpr Score UncontestedOutpost = S( 0, 10);
|
||||
constexpr Score BadOutpost = S( -7, 36);
|
||||
constexpr Score BishopOnKingRing = S( 24, 0);
|
||||
constexpr Score BishopPawns = S( 3, 7);
|
||||
constexpr Score BishopXRayPawns = S( 4, 5);
|
||||
constexpr Score CorneredBishop = S( 50, 50);
|
||||
constexpr Score FlankAttacks = S( 8, 0);
|
||||
constexpr Score Hanging = S( 72, 40);
|
||||
constexpr Score Hanging = S( 69, 36);
|
||||
constexpr Score KnightOnQueen = S( 16, 11);
|
||||
constexpr Score LongDiagonalBishop = S( 45, 0);
|
||||
constexpr Score MinorBehindPawn = S( 18, 3);
|
||||
constexpr Score PassedFile = S( 13, 8);
|
||||
constexpr Score PawnlessFlank = S( 19, 97);
|
||||
constexpr Score ReachableOutpost = S( 33, 19);
|
||||
constexpr Score RestrictedPiece = S( 6, 7);
|
||||
constexpr Score PassedFile = S( 11, 8);
|
||||
constexpr Score PawnlessFlank = S( 17, 95);
|
||||
constexpr Score QueenInfiltration = S( -2, 14);
|
||||
constexpr Score ReachableOutpost = S( 31, 22);
|
||||
constexpr Score RestrictedPiece = S( 7, 7);
|
||||
constexpr Score RookOnKingRing = S( 16, 0);
|
||||
constexpr Score SliderOnQueen = S( 62, 21);
|
||||
constexpr Score ThreatByKing = S( 24, 87);
|
||||
constexpr Score RookOnQueenFile = S( 6, 11);
|
||||
constexpr Score SliderOnQueen = S( 60, 18);
|
||||
constexpr Score ThreatByKing = S( 24, 89);
|
||||
constexpr Score ThreatByPawnPush = S( 48, 39);
|
||||
constexpr Score ThreatBySafePawn = S(167, 99);
|
||||
constexpr Score ThreatBySafePawn = S(173, 94);
|
||||
constexpr Score TrappedRook = S( 55, 13);
|
||||
constexpr Score WeakQueenProtection = S( 14, 0);
|
||||
constexpr Score WeakQueen = S( 57, 19);
|
||||
constexpr Score WeakQueen = S( 56, 15);
|
||||
|
||||
|
||||
#undef S
|
||||
@@ -371,8 +282,8 @@ namespace {
|
||||
attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
|
||||
|
||||
// Init our king safety tables
|
||||
Square s = make_square(std::clamp(file_of(ksq), FILE_B, FILE_G),
|
||||
std::clamp(rank_of(ksq), RANK_2, RANK_7));
|
||||
Square s = make_square(Utility::clamp(file_of(ksq), FILE_B, FILE_G),
|
||||
Utility::clamp(rank_of(ksq), RANK_2, RANK_7));
|
||||
kingRing[Us] = attacks_bb<KING>(s) | s;
|
||||
|
||||
kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
|
||||
@@ -392,16 +303,15 @@ namespace {
|
||||
constexpr Direction Down = -pawn_push(Us);
|
||||
constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
|
||||
: Rank5BB | Rank4BB | Rank3BB);
|
||||
Bitboard b1 = pos.pieces(Us, Pt);
|
||||
const Square* pl = pos.squares<Pt>(Us);
|
||||
|
||||
Bitboard b, bb;
|
||||
Score score = SCORE_ZERO;
|
||||
|
||||
attackedBy[Us][Pt] = 0;
|
||||
|
||||
while (b1)
|
||||
for (Square s = *pl; s != SQ_NONE; s = *++pl)
|
||||
{
|
||||
Square s = pop_lsb(b1);
|
||||
|
||||
// Find attacked squares, including x-ray attacks for bishops and rooks
|
||||
b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN))
|
||||
: Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK))
|
||||
@@ -428,21 +338,21 @@ namespace {
|
||||
score += BishopOnKingRing;
|
||||
|
||||
int mob = popcount(b & mobilityArea[Us]);
|
||||
|
||||
mobility[Us] += MobilityBonus[Pt - 2][mob];
|
||||
|
||||
if (Pt == BISHOP || Pt == KNIGHT)
|
||||
{
|
||||
// Bonus if the piece is on an outpost square or can reach one
|
||||
// Bonus for knights (UncontestedOutpost) if few relevant targets
|
||||
bb = OutpostRanks & (attackedBy[Us][PAWN] | shift<Down>(pos.pieces(PAWN)))
|
||||
& ~pe->pawn_attacks_span(Them);
|
||||
// Reduced bonus for knights (BadOutpost) if few relevant targets
|
||||
bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them);
|
||||
Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN);
|
||||
|
||||
if ( Pt == KNIGHT
|
||||
&& bb & s & ~CenterFiles // on a side outpost
|
||||
&& !(b & targets) // no relevant attacks
|
||||
&& (!more_than_one(targets & (s & QueenSide ? QueenSide : KingSide))))
|
||||
score += UncontestedOutpost * popcount(pos.pieces(PAWN) & (s & QueenSide ? QueenSide : KingSide));
|
||||
score += BadOutpost;
|
||||
else if (bb & s)
|
||||
score += Outpost[Pt == BISHOP];
|
||||
else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us))
|
||||
@@ -455,14 +365,14 @@ namespace {
|
||||
// Penalty if the piece is far from the king
|
||||
score -= KingProtector[Pt == BISHOP] * distance(pos.square<KING>(Us), s);
|
||||
|
||||
if constexpr (Pt == BISHOP)
|
||||
if (Pt == BISHOP)
|
||||
{
|
||||
// Penalty according to the number of our pawns on the same color square as the
|
||||
// bishop, bigger when the center files are blocked with pawns and smaller
|
||||
// when the bishop is outside the pawn chain.
|
||||
Bitboard blocked = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces());
|
||||
|
||||
score -= BishopPawns[edge_distance(file_of(s))] * pos.pawns_on_same_color_squares(Us, s)
|
||||
score -= BishopPawns * pos.pawns_on_same_color_squares(Us, s)
|
||||
* (!(attackedBy[Us][PAWN] & s) + popcount(blocked & CenterFiles));
|
||||
|
||||
// Penalty for all enemy pawns x-rayed
|
||||
@@ -480,48 +390,45 @@ namespace {
|
||||
{
|
||||
Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST);
|
||||
if (pos.piece_on(s + d) == make_piece(Us, PAWN))
|
||||
score -= !pos.empty(s + d + pawn_push(Us)) ? 4 * make_score(CorneredBishop, CorneredBishop)
|
||||
: 3 * make_score(CorneredBishop, CorneredBishop);
|
||||
score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4
|
||||
: pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? CorneredBishop * 2
|
||||
: CorneredBishop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (Pt == ROOK)
|
||||
if (Pt == ROOK)
|
||||
{
|
||||
// Bonuses for rook on a (semi-)open or closed file
|
||||
if (pos.is_on_semiopen_file(Us, s))
|
||||
{
|
||||
score += RookOnOpenFile[pos.is_on_semiopen_file(Them, s)];
|
||||
}
|
||||
else
|
||||
{
|
||||
// If our pawn on this file is blocked, increase penalty
|
||||
if ( pos.pieces(Us, PAWN)
|
||||
& shift<Down>(pos.pieces())
|
||||
& file_bb(s))
|
||||
{
|
||||
score -= RookOnClosedFile;
|
||||
}
|
||||
// Bonus for rook on the same file as a queen
|
||||
if (file_bb(s) & pos.pieces(QUEEN))
|
||||
score += RookOnQueenFile;
|
||||
|
||||
// Penalty when trapped by the king, even more if the king cannot castle
|
||||
if (mob <= 3)
|
||||
{
|
||||
File kf = file_of(pos.square<KING>(Us));
|
||||
if ((kf < FILE_E) == (file_of(s) < kf))
|
||||
score -= TrappedRook * (1 + !pos.castling_rights(Us));
|
||||
}
|
||||
// Bonus for rook on an open or semi-open file
|
||||
if (pos.is_on_semiopen_file(Us, s))
|
||||
score += RookOnFile[pos.is_on_semiopen_file(Them, s)];
|
||||
|
||||
// Penalty when trapped by the king, even more if the king cannot castle
|
||||
else if (mob <= 3)
|
||||
{
|
||||
File kf = file_of(pos.square<KING>(Us));
|
||||
if ((kf < FILE_E) == (file_of(s) < kf))
|
||||
score -= TrappedRook * (1 + !pos.castling_rights(Us));
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (Pt == QUEEN)
|
||||
if (Pt == QUEEN)
|
||||
{
|
||||
// Penalty if any relative pin or discovered attack against the queen
|
||||
Bitboard queenPinners;
|
||||
if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners))
|
||||
score -= WeakQueen;
|
||||
|
||||
// Bonus for queen on weak square in enemy camp
|
||||
if (relative_rank(Us, s) > RANK_4 && (~pe->pawn_attacks_span(Them) & s))
|
||||
score += QueenInfiltration;
|
||||
}
|
||||
}
|
||||
if constexpr (T)
|
||||
if (T)
|
||||
Trace::add(Pt, Us, score);
|
||||
|
||||
return score;
|
||||
@@ -597,18 +504,18 @@ namespace {
|
||||
int kingFlankAttack = popcount(b1) + popcount(b2);
|
||||
int kingFlankDefense = popcount(b3);
|
||||
|
||||
kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] // (~10 Elo)
|
||||
+ 183 * popcount(kingRing[Us] & weak) // (~15 Elo)
|
||||
+ 148 * popcount(unsafeChecks) // (~4 Elo)
|
||||
+ 98 * popcount(pos.blockers_for_king(Us)) // (~2 Elo)
|
||||
+ 69 * kingAttacksCount[Them] // (~0.5 Elo)
|
||||
+ 3 * kingFlankAttack * kingFlankAttack / 8 // (~0.5 Elo)
|
||||
+ mg_value(mobility[Them] - mobility[Us]) // (~0.5 Elo)
|
||||
- 873 * !pos.count<QUEEN>(Them) // (~24 Elo)
|
||||
- 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) // (~5 Elo)
|
||||
- 6 * mg_value(score) / 8 // (~8 Elo)
|
||||
- 4 * kingFlankDefense // (~5 Elo)
|
||||
+ 37; // (~0.5 Elo)
|
||||
kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
|
||||
+ 185 * popcount(kingRing[Us] & weak)
|
||||
+ 148 * popcount(unsafeChecks)
|
||||
+ 98 * popcount(pos.blockers_for_king(Us))
|
||||
+ 69 * kingAttacksCount[Them]
|
||||
+ 3 * kingFlankAttack * kingFlankAttack / 8
|
||||
+ mg_value(mobility[Them] - mobility[Us])
|
||||
- 873 * !pos.count<QUEEN>(Them)
|
||||
- 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
|
||||
- 6 * mg_value(score) / 8
|
||||
- 4 * kingFlankDefense
|
||||
+ 37;
|
||||
|
||||
// Transform the kingDanger units into a Score, and subtract it from the evaluation
|
||||
if (kingDanger > 100)
|
||||
@@ -621,7 +528,7 @@ namespace {
|
||||
// Penalty if king flank is under attack, potentially moving toward the king
|
||||
score -= FlankAttacks * kingFlankAttack;
|
||||
|
||||
if constexpr (T)
|
||||
if (T)
|
||||
Trace::add(KING, Us, score);
|
||||
|
||||
return score;
|
||||
@@ -660,11 +567,11 @@ namespace {
|
||||
{
|
||||
b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]);
|
||||
while (b)
|
||||
score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(b)))];
|
||||
score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(&b)))];
|
||||
|
||||
b = weak & attackedBy[Us][ROOK];
|
||||
while (b)
|
||||
score += ThreatByRook[type_of(pos.piece_on(pop_lsb(b)))];
|
||||
score += ThreatByRook[type_of(pos.piece_on(pop_lsb(&b)))];
|
||||
|
||||
if (weak & attackedBy[Us][KING])
|
||||
score += ThreatByKing;
|
||||
@@ -722,7 +629,7 @@ namespace {
|
||||
score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance);
|
||||
}
|
||||
|
||||
if constexpr (T)
|
||||
if (T)
|
||||
Trace::add(THREAT, Us, score);
|
||||
|
||||
return score;
|
||||
@@ -762,7 +669,7 @@ namespace {
|
||||
|
||||
while (b)
|
||||
{
|
||||
Square s = pop_lsb(b);
|
||||
Square s = pop_lsb(&b);
|
||||
|
||||
assert(!(pos.pieces(Them, PAWN) & forward_file_bb(Us, s + Up)));
|
||||
|
||||
@@ -776,8 +683,8 @@ namespace {
|
||||
Square blockSq = s + Up;
|
||||
|
||||
// Adjust bonus based on the king's proximity
|
||||
bonus += make_score(0, ( king_proximity(Them, blockSq) * 19 / 4
|
||||
- king_proximity(Us, blockSq) * 2) * w);
|
||||
bonus += make_score(0, ( (king_proximity(Them, blockSq) * 19) / 4
|
||||
- king_proximity(Us, blockSq) * 2) * w);
|
||||
|
||||
// If blockSq is not the queening square then consider also a second push
|
||||
if (r != RANK_7)
|
||||
@@ -792,16 +699,14 @@ namespace {
|
||||
bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN);
|
||||
|
||||
if (!(pos.pieces(Them) & bb))
|
||||
unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them);
|
||||
unsafeSquares &= attackedBy[Them][ALL_PIECES];
|
||||
|
||||
// If there are no enemy pieces or attacks on passed pawn span, assign a big bonus.
|
||||
// Or if there is some, but they are all attacked by our pawns, assign a bit smaller bonus.
|
||||
// If there are no enemy attacks on passed pawn span, assign a big bonus.
|
||||
// Otherwise assign a smaller bonus if the path to queen is not attacked
|
||||
// and even smaller bonus if it is attacked but block square is not.
|
||||
int k = !unsafeSquares ? 36 :
|
||||
!(unsafeSquares & ~attackedBy[Us][PAWN]) ? 30 :
|
||||
!(unsafeSquares & squaresToQueen) ? 17 :
|
||||
!(unsafeSquares & blockSq) ? 7 :
|
||||
int k = !unsafeSquares ? 35 :
|
||||
!(unsafeSquares & squaresToQueen) ? 20 :
|
||||
!(unsafeSquares & blockSq) ? 9 :
|
||||
0 ;
|
||||
|
||||
// Assign a larger bonus if the block square is defended
|
||||
@@ -815,7 +720,7 @@ namespace {
|
||||
score += bonus - PassedFile * edge_distance(file_of(s));
|
||||
}
|
||||
|
||||
if constexpr (T)
|
||||
if (T)
|
||||
Trace::add(PASSED, Us, score);
|
||||
|
||||
return score;
|
||||
@@ -823,7 +728,7 @@ namespace {
|
||||
|
||||
|
||||
// Evaluation::space() computes a space evaluation for a given side, aiming to improve game
|
||||
// play in the opening. It is based on the number of safe squares on the four central files
|
||||
// play in the opening. It is based on the number of safe squares on the 4 central files
|
||||
// on ranks 2 to 4. Completely safe squares behind a friendly pawn are counted twice.
|
||||
// Finally, the space bonus is multiplied by a weight which decreases according to occupancy.
|
||||
|
||||
@@ -850,13 +755,11 @@ namespace {
|
||||
behind |= shift<Down>(behind);
|
||||
behind |= shift<Down+Down>(behind);
|
||||
|
||||
// Compute space score based on the number of safe squares and number of our pieces
|
||||
// increased with number of total blocked pawns in position.
|
||||
int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]);
|
||||
int weight = pos.count<ALL_PIECES>(Us) - 3 + std::min(pe->blocked_count(), 9);
|
||||
Score score = make_score(bonus * weight * weight / 16, 0);
|
||||
|
||||
if constexpr (T)
|
||||
if (T)
|
||||
Trace::add(SPACE, Us, score);
|
||||
|
||||
return score;
|
||||
@@ -871,7 +774,7 @@ namespace {
|
||||
Value Evaluation<T>::winnable(Score score) const {
|
||||
|
||||
int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
|
||||
+ int(rank_of(pos.square<KING>(WHITE)) - rank_of(pos.square<KING>(BLACK)));
|
||||
- distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
|
||||
|
||||
bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide)
|
||||
&& (pos.pieces(PAWN) & KingSide);
|
||||
@@ -898,7 +801,7 @@ namespace {
|
||||
// Now apply the bonus: note that we find the attacking side by extracting the
|
||||
// sign of the midgame or endgame values, and that we carefully cap the bonus
|
||||
// so that the midgame and endgame scores do not change sign after the bonus.
|
||||
int u = ((mg > 0) - (mg < 0)) * std::clamp(complexity + 50, -abs(mg), 0);
|
||||
int u = ((mg > 0) - (mg < 0)) * Utility::clamp(complexity + 50, -abs(mg), 0);
|
||||
int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg));
|
||||
|
||||
mg += u;
|
||||
@@ -908,42 +811,28 @@ namespace {
|
||||
Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK;
|
||||
int sf = me->scale_factor(pos, strongSide);
|
||||
|
||||
// If scale factor is not already specific, scale up/down via general heuristics
|
||||
// If scale factor is not already specific, scale down via general heuristics
|
||||
if (sf == SCALE_FACTOR_NORMAL)
|
||||
{
|
||||
if (pos.opposite_bishops())
|
||||
{
|
||||
// For pure opposite colored bishops endgames use scale factor
|
||||
// based on the number of passed pawns of the strong side.
|
||||
if ( pos.non_pawn_material(WHITE) == BishopValueMg
|
||||
&& pos.non_pawn_material(BLACK) == BishopValueMg)
|
||||
sf = 18 + 4 * popcount(pe->passed_pawns(strongSide));
|
||||
// For every other opposite colored bishops endgames use scale factor
|
||||
// based on the number of all pieces of the strong side.
|
||||
else
|
||||
sf = 22 + 3 * pos.count<ALL_PIECES>(strongSide);
|
||||
}
|
||||
// For rook endgames with strong side not having overwhelming pawn number advantage
|
||||
// and its pawns being on one flank and weak side protecting its pieces with a king
|
||||
// use lower scale factor.
|
||||
else if ( pos.non_pawn_material(WHITE) == RookValueMg
|
||||
&& pos.non_pawn_material(BLACK) == RookValueMg
|
||||
&& pos.count<PAWN>(strongSide) - pos.count<PAWN>(~strongSide) <= 1
|
||||
&& bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN))
|
||||
&& (attacks_bb<KING>(pos.square<KING>(~strongSide)) & pos.pieces(~strongSide, PAWN)))
|
||||
sf = 36;
|
||||
// For queen vs no queen endgames use scale factor
|
||||
// based on number of minors of side that doesn't have queen.
|
||||
else if (pos.count<QUEEN>() == 1)
|
||||
sf = 37 + 3 * (pos.count<QUEEN>(WHITE) == 1 ? pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK)
|
||||
: pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE));
|
||||
// In every other case use scale factor based on
|
||||
// the number of pawns of the strong side reduced if pawns are on a single flank.
|
||||
else
|
||||
sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide)) - 4 * !pawnsOnBothFlanks;
|
||||
|
||||
// Reduce scale factor in case of pawns being on a single flank
|
||||
sf -= 4 * !pawnsOnBothFlanks;
|
||||
sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide));
|
||||
}
|
||||
|
||||
// Interpolate between the middlegame and (scaled by 'sf') endgame score
|
||||
@@ -951,7 +840,7 @@ namespace {
|
||||
+ eg * int(PHASE_MIDGAME - me->game_phase()) * ScaleFactor(sf) / SCALE_FACTOR_NORMAL;
|
||||
v /= PHASE_MIDGAME;
|
||||
|
||||
if constexpr (T)
|
||||
if (T)
|
||||
{
|
||||
Trace::add(WINNABLE, make_score(u, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL - eg_value(score)));
|
||||
Trace::add(TOTAL, make_score(mg, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL));
|
||||
@@ -981,7 +870,7 @@ namespace {
|
||||
// Initialize score by reading the incrementally updated scores included in
|
||||
// the position object (material + piece square tables) and the material
|
||||
// imbalance. Score is computed internally from the white point of view.
|
||||
Score score = pos.psq_score() + me->imbalance() + pos.this_thread()->trend;
|
||||
Score score = pos.psq_score() + me->imbalance() + pos.this_thread()->contempt;
|
||||
|
||||
// Probe the pawn hash table
|
||||
pe = Pawns::probe(pos);
|
||||
@@ -989,9 +878,7 @@ namespace {
|
||||
|
||||
// Early exit if score is high
|
||||
auto lazy_skip = [&](Value lazyThreshold) {
|
||||
return abs(mg_value(score) + eg_value(score)) > lazyThreshold
|
||||
+ std::abs(pos.this_thread()->bestValue) * 5 / 4
|
||||
+ pos.non_pawn_material() / 32;
|
||||
return abs(mg_value(score) + eg_value(score)) / 2 > lazyThreshold + pos.non_pawn_material() / 64;
|
||||
};
|
||||
|
||||
if (lazy_skip(LazyThreshold1))
|
||||
@@ -1025,7 +912,7 @@ make_v:
|
||||
Value v = winnable(score);
|
||||
|
||||
// In case of tracing add all remaining individual evaluation terms
|
||||
if constexpr (T)
|
||||
if (T)
|
||||
{
|
||||
Trace::add(MATERIAL, pos.psq_score());
|
||||
Trace::add(IMBALANCE, me->imbalance());
|
||||
@@ -1037,44 +924,15 @@ make_v:
|
||||
v = (v / 16) * 16;
|
||||
|
||||
// Side to move point of view
|
||||
v = (pos.side_to_move() == WHITE ? v : -v);
|
||||
v = (pos.side_to_move() == WHITE ? v : -v) + Tempo;
|
||||
|
||||
// Damp down the evaluation linearly when shuffling
|
||||
v = v * (100 - pos.rule50_count()) / 100;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
/// Fisher Random Chess: correction for cornered bishops, to fix chess960 play with NNUE
|
||||
|
||||
Value fix_FRC(const Position& pos) {
|
||||
|
||||
constexpr Bitboard Corners = 1ULL << SQ_A1 | 1ULL << SQ_H1 | 1ULL << SQ_A8 | 1ULL << SQ_H8;
|
||||
|
||||
if (!(pos.pieces(BISHOP) & Corners))
|
||||
return VALUE_ZERO;
|
||||
|
||||
int correction = 0;
|
||||
|
||||
if ( pos.piece_on(SQ_A1) == W_BISHOP
|
||||
&& pos.piece_on(SQ_B2) == W_PAWN)
|
||||
correction -= CorneredBishop;
|
||||
|
||||
if ( pos.piece_on(SQ_H1) == W_BISHOP
|
||||
&& pos.piece_on(SQ_G2) == W_PAWN)
|
||||
correction -= CorneredBishop;
|
||||
|
||||
if ( pos.piece_on(SQ_A8) == B_BISHOP
|
||||
&& pos.piece_on(SQ_B7) == B_PAWN)
|
||||
correction += CorneredBishop;
|
||||
|
||||
if ( pos.piece_on(SQ_H8) == B_BISHOP
|
||||
&& pos.piece_on(SQ_G7) == B_PAWN)
|
||||
correction += CorneredBishop;
|
||||
|
||||
return pos.side_to_move() == WHITE ? Value(3 * correction)
|
||||
: -Value(3 * correction);
|
||||
}
|
||||
|
||||
} // namespace Eval
|
||||
} // namespace
|
||||
|
||||
|
||||
/// evaluate() is the evaluator for the outer world. It returns a static
|
||||
@@ -1082,43 +940,10 @@ make_v:
|
||||
|
||||
Value Eval::evaluate(const Position& pos) {
|
||||
|
||||
Value v;
|
||||
bool useClassical = false;
|
||||
|
||||
// Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical,
|
||||
// but we switch to NNUE during long shuffling or with high material on the board.
|
||||
if ( !useNNUE
|
||||
|| ((pos.this_thread()->depth > 9 || pos.count<ALL_PIECES>() > 7) &&
|
||||
abs(eg_value(pos.psq_score())) * 5 > (856 + pos.non_pawn_material() / 64) * (10 + pos.rule50_count())))
|
||||
{
|
||||
v = Evaluation<NO_TRACE>(pos).value(); // classical
|
||||
useClassical = abs(v) >= 297;
|
||||
}
|
||||
|
||||
// If result of a classical evaluation is much lower than threshold fall back to NNUE
|
||||
if (useNNUE && !useClassical)
|
||||
{
|
||||
Value nnue = NNUE::evaluate(pos, true); // NNUE
|
||||
int scale = 1036 + 22 * pos.non_pawn_material() / 1024;
|
||||
Color stm = pos.side_to_move();
|
||||
Value optimism = pos.this_thread()->optimism[stm];
|
||||
Value psq = (stm == WHITE ? 1 : -1) * eg_value(pos.psq_score());
|
||||
int complexity = 35 * abs(nnue - psq) / 256;
|
||||
|
||||
optimism = optimism * (44 + complexity) / 31;
|
||||
v = (nnue + optimism) * scale / 1024 - optimism;
|
||||
|
||||
if (pos.is_chess960())
|
||||
v += fix_FRC(pos);
|
||||
}
|
||||
|
||||
// Damp down the evaluation linearly when shuffling
|
||||
v = v * (195 - pos.rule50_count()) / 211;
|
||||
|
||||
// Guarantee evaluation does not hit the tablebase range
|
||||
v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
|
||||
|
||||
return v;
|
||||
if (Eval::useNNUE)
|
||||
return NNUE::evaluate(pos);
|
||||
else
|
||||
return Evaluation<NO_TRACE>(pos).value();
|
||||
}
|
||||
|
||||
/// trace() is like evaluate(), but instead of returning a value, it returns
|
||||
@@ -1126,7 +951,7 @@ Value Eval::evaluate(const Position& pos) {
|
||||
/// descriptions and values of each evaluation term. Useful for debugging.
|
||||
/// Trace scores are from white's point of view
|
||||
|
||||
std::string Eval::trace(Position& pos) {
|
||||
std::string Eval::trace(const Position& pos) {
|
||||
|
||||
if (pos.checkers())
|
||||
return "Final evaluation: none (in check)";
|
||||
@@ -1136,62 +961,42 @@ std::string Eval::trace(Position& pos) {
|
||||
|
||||
Value v;
|
||||
|
||||
std::memset(scores, 0, sizeof(scores));
|
||||
|
||||
// Reset any global variable used in eval
|
||||
pos.this_thread()->depth = 0;
|
||||
pos.this_thread()->trend = SCORE_ZERO;
|
||||
pos.this_thread()->bestValue = VALUE_ZERO;
|
||||
pos.this_thread()->optimism[WHITE] = VALUE_ZERO;
|
||||
pos.this_thread()->optimism[BLACK] = VALUE_ZERO;
|
||||
|
||||
v = Evaluation<TRACE>(pos).value();
|
||||
|
||||
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
|
||||
<< " Contributing terms for the classical eval:\n"
|
||||
<< "+------------+-------------+-------------+-------------+\n"
|
||||
<< "| Term | White | Black | Total |\n"
|
||||
<< "| | MG EG | MG EG | MG EG |\n"
|
||||
<< "+------------+-------------+-------------+-------------+\n"
|
||||
<< "| Material | " << Term(MATERIAL)
|
||||
<< "| Imbalance | " << Term(IMBALANCE)
|
||||
<< "| Pawns | " << Term(PAWN)
|
||||
<< "| Knights | " << Term(KNIGHT)
|
||||
<< "| Bishops | " << Term(BISHOP)
|
||||
<< "| Rooks | " << Term(ROOK)
|
||||
<< "| Queens | " << Term(QUEEN)
|
||||
<< "| Mobility | " << Term(MOBILITY)
|
||||
<< "|King safety | " << Term(KING)
|
||||
<< "| Threats | " << Term(THREAT)
|
||||
<< "| Passed | " << Term(PASSED)
|
||||
<< "| Space | " << Term(SPACE)
|
||||
<< "| Winnable | " << Term(WINNABLE)
|
||||
<< "+------------+-------------+-------------+-------------+\n"
|
||||
<< "| Total | " << Term(TOTAL)
|
||||
<< "+------------+-------------+-------------+-------------+\n";
|
||||
|
||||
if (Eval::useNNUE)
|
||||
ss << '\n' << NNUE::trace(pos) << '\n';
|
||||
|
||||
ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15);
|
||||
|
||||
v = pos.side_to_move() == WHITE ? v : -v;
|
||||
ss << "\nClassical evaluation " << to_cp(v) << " (white side)\n";
|
||||
if (Eval::useNNUE)
|
||||
{
|
||||
v = NNUE::evaluate(pos, false);
|
||||
v = pos.side_to_move() == WHITE ? v : -v;
|
||||
ss << "NNUE evaluation " << to_cp(v) << " (white side)\n";
|
||||
v = NNUE::evaluate(pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memset(scores, 0, sizeof(scores));
|
||||
|
||||
pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt
|
||||
|
||||
v = Evaluation<TRACE>(pos).value();
|
||||
|
||||
ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2)
|
||||
<< " Term | White | Black | Total \n"
|
||||
<< " | MG EG | MG EG | MG EG \n"
|
||||
<< " ------------+-------------+-------------+------------\n"
|
||||
<< " Material | " << Term(MATERIAL)
|
||||
<< " Imbalance | " << Term(IMBALANCE)
|
||||
<< " Pawns | " << Term(PAWN)
|
||||
<< " Knights | " << Term(KNIGHT)
|
||||
<< " Bishops | " << Term(BISHOP)
|
||||
<< " Rooks | " << Term(ROOK)
|
||||
<< " Queens | " << Term(QUEEN)
|
||||
<< " Mobility | " << Term(MOBILITY)
|
||||
<< " King safety | " << Term(KING)
|
||||
<< " Threats | " << Term(THREAT)
|
||||
<< " Passed | " << Term(PASSED)
|
||||
<< " Space | " << Term(SPACE)
|
||||
<< " Winnable | " << Term(WINNABLE)
|
||||
<< " ------------+-------------+-------------+------------\n"
|
||||
<< " Total | " << Term(TOTAL);
|
||||
}
|
||||
|
||||
v = evaluate(pos);
|
||||
v = pos.side_to_move() == WHITE ? v : -v;
|
||||
ss << "Final evaluation " << to_cp(v) << " (white side)";
|
||||
if (Eval::useNNUE)
|
||||
ss << " [with scaled NNUE, hybrid, ...]";
|
||||
ss << "\n";
|
||||
|
||||
ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+9
-22
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -20,43 +20,30 @@
|
||||
#define EVALUATE_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
class Position;
|
||||
|
||||
namespace Eval {
|
||||
|
||||
std::string trace(Position& pos);
|
||||
std::string trace(const Position& pos);
|
||||
Value evaluate(const Position& pos);
|
||||
|
||||
extern bool useNNUE;
|
||||
extern std::string currentEvalFileName;
|
||||
|
||||
// The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue
|
||||
// for the build process (profile-build and fishtest) to work. Do not change the
|
||||
// name of the macro, as it is used in the Makefile.
|
||||
#define EvalFileDefaultName "nn-6877cd24400e.nnue"
|
||||
extern std::string eval_file_loaded;
|
||||
void init_NNUE();
|
||||
void verify_NNUE();
|
||||
|
||||
namespace NNUE {
|
||||
|
||||
std::string trace(Position& pos);
|
||||
Value evaluate(const Position& pos, bool adjusted = false);
|
||||
|
||||
void init();
|
||||
void verify();
|
||||
|
||||
bool load_eval(std::string name, std::istream& stream);
|
||||
bool save_eval(std::ostream& stream);
|
||||
bool save_eval(const std::optional<std::string>& filename);
|
||||
Value evaluate(const Position& pos);
|
||||
Value compute_eval(const Position& pos);
|
||||
void update_eval(const Position& pos);
|
||||
bool load_eval_file(const std::string& evalFile);
|
||||
|
||||
} // namespace NNUE
|
||||
|
||||
} // namespace Eval
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef EVALUATE_H_INCLUDED
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
The file "incbin.h" is free and unencumbered software released into
|
||||
the public domain by Dale Weiler, see:
|
||||
<https://github.com/graphitemaster/incbin>
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
@@ -1,368 +0,0 @@
|
||||
/**
|
||||
* @file incbin.h
|
||||
* @author Dale Weiler
|
||||
* @brief Utility for including binary files
|
||||
*
|
||||
* Facilities for including binary files into the current translation unit and
|
||||
* making use from them externally in other translation units.
|
||||
*/
|
||||
#ifndef INCBIN_HDR
|
||||
#define INCBIN_HDR
|
||||
#include <limits.h>
|
||||
#if defined(__AVX512BW__) || \
|
||||
defined(__AVX512CD__) || \
|
||||
defined(__AVX512DQ__) || \
|
||||
defined(__AVX512ER__) || \
|
||||
defined(__AVX512PF__) || \
|
||||
defined(__AVX512VL__) || \
|
||||
defined(__AVX512F__)
|
||||
# define INCBIN_ALIGNMENT_INDEX 6
|
||||
#elif defined(__AVX__) || \
|
||||
defined(__AVX2__)
|
||||
# define INCBIN_ALIGNMENT_INDEX 5
|
||||
#elif defined(__SSE__) || \
|
||||
defined(__SSE2__) || \
|
||||
defined(__SSE3__) || \
|
||||
defined(__SSSE3__) || \
|
||||
defined(__SSE4_1__) || \
|
||||
defined(__SSE4_2__) || \
|
||||
defined(__neon__)
|
||||
# define INCBIN_ALIGNMENT_INDEX 4
|
||||
#elif ULONG_MAX != 0xffffffffu
|
||||
# define INCBIN_ALIGNMENT_INDEX 3
|
||||
# else
|
||||
# define INCBIN_ALIGNMENT_INDEX 2
|
||||
#endif
|
||||
|
||||
/* Lookup table of (1 << n) where `n' is `INCBIN_ALIGNMENT_INDEX' */
|
||||
#define INCBIN_ALIGN_SHIFT_0 1
|
||||
#define INCBIN_ALIGN_SHIFT_1 2
|
||||
#define INCBIN_ALIGN_SHIFT_2 4
|
||||
#define INCBIN_ALIGN_SHIFT_3 8
|
||||
#define INCBIN_ALIGN_SHIFT_4 16
|
||||
#define INCBIN_ALIGN_SHIFT_5 32
|
||||
#define INCBIN_ALIGN_SHIFT_6 64
|
||||
|
||||
/* Actual alignment value */
|
||||
#define INCBIN_ALIGNMENT \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_ALIGN_SHIFT, _), \
|
||||
INCBIN_ALIGNMENT_INDEX)
|
||||
|
||||
/* Stringize */
|
||||
#define INCBIN_STR(X) \
|
||||
#X
|
||||
#define INCBIN_STRINGIZE(X) \
|
||||
INCBIN_STR(X)
|
||||
/* Concatenate */
|
||||
#define INCBIN_CAT(X, Y) \
|
||||
X ## Y
|
||||
#define INCBIN_CONCATENATE(X, Y) \
|
||||
INCBIN_CAT(X, Y)
|
||||
/* Deferred macro expansion */
|
||||
#define INCBIN_EVAL(X) \
|
||||
X
|
||||
#define INCBIN_INVOKE(N, ...) \
|
||||
INCBIN_EVAL(N(__VA_ARGS__))
|
||||
|
||||
/* Green Hills uses a different directive for including binary data */
|
||||
#if defined(__ghs__)
|
||||
# if (__ghs_asm == 2)
|
||||
# define INCBIN_MACRO ".file"
|
||||
/* Or consider the ".myrawdata" entry in the ld file */
|
||||
# else
|
||||
# define INCBIN_MACRO "\tINCBIN"
|
||||
# endif
|
||||
#else
|
||||
# define INCBIN_MACRO ".incbin"
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# define INCBIN_ALIGN \
|
||||
__attribute__((aligned(INCBIN_ALIGNMENT)))
|
||||
#else
|
||||
# define INCBIN_ALIGN __declspec(align(INCBIN_ALIGNMENT))
|
||||
#endif
|
||||
|
||||
#if defined(__arm__) || /* GNU C and RealView */ \
|
||||
defined(__arm) || /* Diab */ \
|
||||
defined(_ARM) /* ImageCraft */
|
||||
# define INCBIN_ARM
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* Utilize .balign where supported */
|
||||
# define INCBIN_ALIGN_HOST ".balign " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
|
||||
# define INCBIN_ALIGN_BYTE ".balign 1\n"
|
||||
#elif defined(INCBIN_ARM)
|
||||
/*
|
||||
* On arm assemblers, the alignment value is calculated as (1 << n) where `n' is
|
||||
* the shift count. This is the value passed to `.align'
|
||||
*/
|
||||
# define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT_INDEX) "\n"
|
||||
# define INCBIN_ALIGN_BYTE ".align 0\n"
|
||||
#else
|
||||
/* We assume other inline assembler's treat `.align' as `.balign' */
|
||||
# define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
|
||||
# define INCBIN_ALIGN_BYTE ".align 1\n"
|
||||
#endif
|
||||
|
||||
/* INCBIN_CONST is used by incbin.c generated files */
|
||||
#if defined(__cplusplus)
|
||||
# define INCBIN_EXTERNAL extern "C"
|
||||
# define INCBIN_CONST extern const
|
||||
#else
|
||||
# define INCBIN_EXTERNAL extern
|
||||
# define INCBIN_CONST const
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Optionally override the linker section into which data is emitted.
|
||||
*
|
||||
* @warning If you use this facility, you'll have to deal with platform-specific linker output
|
||||
* section naming on your own
|
||||
*
|
||||
* Overriding the default linker output section, e.g for esp8266/Arduino:
|
||||
* @code
|
||||
* #define INCBIN_OUTPUT_SECTION ".irom.text"
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
* // Data is emitted into program memory that never gets copied to RAM
|
||||
* @endcode
|
||||
*/
|
||||
#if !defined(INCBIN_OUTPUT_SECTION)
|
||||
# if defined(__APPLE__)
|
||||
# define INCBIN_OUTPUT_SECTION ".const_data"
|
||||
# else
|
||||
# define INCBIN_OUTPUT_SECTION ".rodata"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
/* The directives are different for Apple branded compilers */
|
||||
# define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n"
|
||||
# define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
|
||||
# define INCBIN_INT ".long "
|
||||
# define INCBIN_MANGLE "_"
|
||||
# define INCBIN_BYTE ".byte "
|
||||
# define INCBIN_TYPE(...)
|
||||
#else
|
||||
# define INCBIN_SECTION ".section " INCBIN_OUTPUT_SECTION "\n"
|
||||
# define INCBIN_GLOBAL(NAME) ".global " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
|
||||
# if defined(__ghs__)
|
||||
# define INCBIN_INT ".word "
|
||||
# else
|
||||
# define INCBIN_INT ".int "
|
||||
# endif
|
||||
# if defined(__USER_LABEL_PREFIX__)
|
||||
# define INCBIN_MANGLE INCBIN_STRINGIZE(__USER_LABEL_PREFIX__)
|
||||
# else
|
||||
# define INCBIN_MANGLE ""
|
||||
# endif
|
||||
# if defined(INCBIN_ARM)
|
||||
/* On arm assemblers, `@' is used as a line comment token */
|
||||
# define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", %object\n"
|
||||
# elif defined(__MINGW32__) || defined(__MINGW64__)
|
||||
/* Mingw doesn't support this directive either */
|
||||
# define INCBIN_TYPE(NAME)
|
||||
# else
|
||||
/* It's safe to use `@' on other architectures */
|
||||
# define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", @object\n"
|
||||
# endif
|
||||
# define INCBIN_BYTE ".byte "
|
||||
#endif
|
||||
|
||||
/* List of style types used for symbol names */
|
||||
#define INCBIN_STYLE_CAMEL 0
|
||||
#define INCBIN_STYLE_SNAKE 1
|
||||
|
||||
/**
|
||||
* @brief Specify the prefix to use for symbol names.
|
||||
*
|
||||
* By default this is `g', producing symbols of the form:
|
||||
* @code
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char gFooData[];
|
||||
* // const unsigned char *const gFooEnd;
|
||||
* // const unsigned int gFooSize;
|
||||
* @endcode
|
||||
*
|
||||
* If however you specify a prefix before including: e.g:
|
||||
* @code
|
||||
* #define INCBIN_PREFIX incbin
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols instead:
|
||||
* // const unsigned char incbinFooData[];
|
||||
* // const unsigned char *const incbinFooEnd;
|
||||
* // const unsigned int incbinFooSize;
|
||||
* @endcode
|
||||
*/
|
||||
#if !defined(INCBIN_PREFIX)
|
||||
# define INCBIN_PREFIX g
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Specify the style used for symbol names.
|
||||
*
|
||||
* Possible options are
|
||||
* - INCBIN_STYLE_CAMEL "CamelCase"
|
||||
* - INCBIN_STYLE_SNAKE "snake_case"
|
||||
*
|
||||
* Default option is *INCBIN_STYLE_CAMEL* producing symbols of the form:
|
||||
* @code
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>FooData[];
|
||||
* // const unsigned char *const <prefix>FooEnd;
|
||||
* // const unsigned int <prefix>FooSize;
|
||||
* @endcode
|
||||
*
|
||||
* If however you specify a style before including: e.g:
|
||||
* @code
|
||||
* #define INCBIN_STYLE INCBIN_STYLE_SNAKE
|
||||
* #include "incbin.h"
|
||||
* INCBIN(foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>foo_data[];
|
||||
* // const unsigned char *const <prefix>foo_end;
|
||||
* // const unsigned int <prefix>foo_size;
|
||||
* @endcode
|
||||
*/
|
||||
#if !defined(INCBIN_STYLE)
|
||||
# define INCBIN_STYLE INCBIN_STYLE_CAMEL
|
||||
#endif
|
||||
|
||||
/* Style lookup tables */
|
||||
#define INCBIN_STYLE_0_DATA Data
|
||||
#define INCBIN_STYLE_0_END End
|
||||
#define INCBIN_STYLE_0_SIZE Size
|
||||
#define INCBIN_STYLE_1_DATA _data
|
||||
#define INCBIN_STYLE_1_END _end
|
||||
#define INCBIN_STYLE_1_SIZE _size
|
||||
|
||||
/* Style lookup: returning identifier */
|
||||
#define INCBIN_STYLE_IDENT(TYPE) \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_STYLE_, \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_EVAL(INCBIN_STYLE), \
|
||||
INCBIN_CONCATENATE(_, TYPE)))
|
||||
|
||||
/* Style lookup: returning string literal */
|
||||
#define INCBIN_STYLE_STRING(TYPE) \
|
||||
INCBIN_STRINGIZE( \
|
||||
INCBIN_STYLE_IDENT(TYPE)) \
|
||||
|
||||
/* Generate the global labels by indirectly invoking the macro with our style
|
||||
* type and concatenating the name against them. */
|
||||
#define INCBIN_GLOBAL_LABELS(NAME, TYPE) \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_GLOBAL, \
|
||||
INCBIN_CONCATENATE( \
|
||||
NAME, \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_STYLE_IDENT, \
|
||||
TYPE))) \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_TYPE, \
|
||||
INCBIN_CONCATENATE( \
|
||||
NAME, \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_STYLE_IDENT, \
|
||||
TYPE)))
|
||||
|
||||
/**
|
||||
* @brief Externally reference binary data included in another translation unit.
|
||||
*
|
||||
* Produces three external symbols that reference the binary data included in
|
||||
* another translation unit.
|
||||
*
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param NAME The name given for the binary data
|
||||
*
|
||||
* @code
|
||||
* INCBIN_EXTERN(Foo);
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // extern const unsigned char <prefix>FooData[];
|
||||
* // extern const unsigned char *const <prefix>FooEnd;
|
||||
* // extern const unsigned int <prefix>FooSize;
|
||||
* @endcode
|
||||
*/
|
||||
#define INCBIN_EXTERN(NAME) \
|
||||
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(DATA))[]; \
|
||||
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char *const \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(END)); \
|
||||
INCBIN_EXTERNAL const unsigned int \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(SIZE))
|
||||
|
||||
/**
|
||||
* @brief Include a binary file into the current translation unit.
|
||||
*
|
||||
* Includes a binary file into the current translation unit, producing three symbols
|
||||
* for objects that encode the data and size respectively.
|
||||
*
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param NAME The name to associate with this binary data (as an identifier.)
|
||||
* @param FILENAME The file to include (as a string literal.)
|
||||
*
|
||||
* @code
|
||||
* INCBIN(Icon, "icon.png");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>IconData[];
|
||||
* // const unsigned char *const <prefix>IconEnd;
|
||||
* // const unsigned int <prefix>IconSize;
|
||||
* @endcode
|
||||
*
|
||||
* @warning This must be used in global scope
|
||||
* @warning The identifiers may be different if INCBIN_STYLE is not default
|
||||
*
|
||||
* To externally reference the data included by this in another translation unit
|
||||
* please @see INCBIN_EXTERN.
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
#define INCBIN(NAME, FILENAME) \
|
||||
INCBIN_EXTERN(NAME)
|
||||
#else
|
||||
#define INCBIN(NAME, FILENAME) \
|
||||
__asm__(INCBIN_SECTION \
|
||||
INCBIN_GLOBAL_LABELS(NAME, DATA) \
|
||||
INCBIN_ALIGN_HOST \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) ":\n" \
|
||||
INCBIN_MACRO " \"" FILENAME "\"\n" \
|
||||
INCBIN_GLOBAL_LABELS(NAME, END) \
|
||||
INCBIN_ALIGN_BYTE \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) ":\n" \
|
||||
INCBIN_BYTE "1\n" \
|
||||
INCBIN_GLOBAL_LABELS(NAME, SIZE) \
|
||||
INCBIN_ALIGN_HOST \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(SIZE) ":\n" \
|
||||
INCBIN_INT INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) " - " \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) "\n" \
|
||||
INCBIN_ALIGN_HOST \
|
||||
".text\n" \
|
||||
); \
|
||||
INCBIN_EXTERN(NAME)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -21,20 +21,20 @@
|
||||
#include "bitboard.h"
|
||||
#include "endgame.h"
|
||||
#include "position.h"
|
||||
#include "psqt.h"
|
||||
#include "search.h"
|
||||
#include "syzygy/tbprobe.h"
|
||||
#include "thread.h"
|
||||
#include "tt.h"
|
||||
#include "uci.h"
|
||||
#include "syzygy/tbprobe.h"
|
||||
|
||||
using namespace Stockfish;
|
||||
namespace PSQT {
|
||||
void init();
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
std::cout << engine_info() << std::endl;
|
||||
|
||||
CommandLine::init(argc, argv);
|
||||
UCI::init(Options);
|
||||
Tune::init();
|
||||
PSQT::init();
|
||||
@@ -44,7 +44,7 @@ int main(int argc, char* argv[]) {
|
||||
Endgames::init();
|
||||
Threads.set(size_t(Options["Threads"]));
|
||||
Search::clear(); // After threads are up
|
||||
Eval::NNUE::init();
|
||||
Eval::init_NNUE();
|
||||
|
||||
UCI::loop(argc, argv);
|
||||
|
||||
|
||||
+24
-33
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -24,39 +24,32 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
namespace {
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
|
||||
// Polynomial material imbalance parameters
|
||||
|
||||
// One Score parameter for each pair (our piece, another of our pieces)
|
||||
constexpr Score QuadraticOurs[][PIECE_TYPE_NB] = {
|
||||
// OUR PIECE 2
|
||||
// bishop pair pawn knight bishop rook queen
|
||||
{S(1419, 1455) }, // Bishop pair
|
||||
{S( 101, 28), S( 37, 39) }, // Pawn
|
||||
{S( 57, 64), S(249, 187), S(-49, -62) }, // Knight OUR PIECE 1
|
||||
{S( 0, 0), S(118, 137), S( 10, 27), S( 0, 0) }, // Bishop
|
||||
{S( -63, -68), S( -5, 3), S(100, 81), S(132, 118), S(-246, -244) }, // Rook
|
||||
{S(-210, -211), S( 37, 14), S(147, 141), S(161, 105), S(-158, -174), S(-9,-31) } // Queen
|
||||
constexpr int QuadraticOurs[][PIECE_TYPE_NB] = {
|
||||
// OUR PIECES
|
||||
// pair pawn knight bishop rook queen
|
||||
{1438 }, // Bishop pair
|
||||
{ 40, 38 }, // Pawn
|
||||
{ 32, 255, -62 }, // Knight OUR PIECES
|
||||
{ 0, 104, 4, 0 }, // Bishop
|
||||
{ -26, -2, 47, 105, -208 }, // Rook
|
||||
{-189, 24, 117, 133, -134, -6 } // Queen
|
||||
};
|
||||
|
||||
// One Score parameter for each pair (our piece, their piece)
|
||||
constexpr Score QuadraticTheirs[][PIECE_TYPE_NB] = {
|
||||
// THEIR PIECE
|
||||
// bishop pair pawn knight bishop rook queen
|
||||
{ }, // Bishop pair
|
||||
{S( 33, 30) }, // Pawn
|
||||
{S( 46, 18), S(106, 84) }, // Knight OUR PIECE
|
||||
{S( 75, 35), S( 59, 44), S( 60, 15) }, // Bishop
|
||||
{S( 26, 35), S( 6, 22), S( 38, 39), S(-12, -2) }, // Rook
|
||||
{S( 97, 93), S(100, 163), S(-58, -91), S(112, 192), S(276, 225) } // Queen
|
||||
constexpr int QuadraticTheirs[][PIECE_TYPE_NB] = {
|
||||
// THEIR PIECES
|
||||
// pair pawn knight bishop rook queen
|
||||
{ }, // Bishop pair
|
||||
{ 36, }, // Pawn
|
||||
{ 9, 63, }, // Knight OUR PIECES
|
||||
{ 59, 65, 42, }, // Bishop
|
||||
{ 46, 39, 24, -24, }, // Rook
|
||||
{ 97, 100, -42, 137, 268, } // Queen
|
||||
};
|
||||
|
||||
#undef S
|
||||
|
||||
// Endgame evaluation and scaling functions are accessed directly and not through
|
||||
// the function maps because they correspond to more than one material hash key.
|
||||
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
|
||||
@@ -74,7 +67,7 @@ namespace {
|
||||
|
||||
bool is_KBPsK(const Position& pos, Color us) {
|
||||
return pos.non_pawn_material(us) == BishopValueMg
|
||||
&& pos.count<PAWN>(us) >= 1;
|
||||
&& pos.count<PAWN >(us) >= 1;
|
||||
}
|
||||
|
||||
bool is_KQKRPs(const Position& pos, Color us) {
|
||||
@@ -89,11 +82,11 @@ namespace {
|
||||
/// piece type for both colors.
|
||||
|
||||
template<Color Us>
|
||||
Score imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
|
||||
int imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
|
||||
|
||||
constexpr Color Them = ~Us;
|
||||
|
||||
Score bonus = SCORE_ZERO;
|
||||
int bonus = 0;
|
||||
|
||||
// Second-degree polynomial material imbalance, by Tord Romstad
|
||||
for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
|
||||
@@ -137,7 +130,7 @@ Entry* probe(const Position& pos) {
|
||||
|
||||
Value npm_w = pos.non_pawn_material(WHITE);
|
||||
Value npm_b = pos.non_pawn_material(BLACK);
|
||||
Value npm = std::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
|
||||
Value npm = Utility::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
|
||||
|
||||
// Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME]
|
||||
e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
|
||||
@@ -220,10 +213,8 @@ Entry* probe(const Position& pos) {
|
||||
{ pos.count<BISHOP>(BLACK) > 1, pos.count<PAWN>(BLACK), pos.count<KNIGHT>(BLACK),
|
||||
pos.count<BISHOP>(BLACK) , pos.count<ROOK>(BLACK), pos.count<QUEEN >(BLACK) } };
|
||||
|
||||
e->score = (imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16;
|
||||
e->value = int16_t((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
|
||||
return e;
|
||||
}
|
||||
|
||||
} // namespace Material
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+7
-7
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "position.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish::Material {
|
||||
namespace Material {
|
||||
|
||||
/// Material::Entry contains various information about a material configuration.
|
||||
/// It contains a material imbalance evaluation, a function pointer to a special
|
||||
@@ -37,8 +37,8 @@ namespace Stockfish::Material {
|
||||
|
||||
struct Entry {
|
||||
|
||||
Score imbalance() const { return score; }
|
||||
Phase game_phase() const { return (Phase)gamePhase; }
|
||||
Score imbalance() const { return make_score(value, value); }
|
||||
Phase game_phase() const { return gamePhase; }
|
||||
bool specialized_eval_exists() const { return evaluationFunction != nullptr; }
|
||||
Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); }
|
||||
|
||||
@@ -57,15 +57,15 @@ struct Entry {
|
||||
const EndgameBase<Value>* evaluationFunction;
|
||||
const EndgameBase<ScaleFactor>* scalingFunction[COLOR_NB]; // Could be one for each
|
||||
// side (e.g. KPKP, KBPsK)
|
||||
Score score;
|
||||
int16_t gamePhase;
|
||||
int16_t value;
|
||||
uint8_t factor[COLOR_NB];
|
||||
Phase gamePhase;
|
||||
};
|
||||
|
||||
typedef HashTable<Entry, 8192> Table;
|
||||
|
||||
Entry* probe(const Position& pos);
|
||||
|
||||
} // namespace Stockfish::Material
|
||||
} // namespace Material
|
||||
|
||||
#endif // #ifndef MATERIAL_H_INCLUDED
|
||||
|
||||
+78
-177
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -36,8 +36,6 @@ typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP,
|
||||
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD);
|
||||
typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY);
|
||||
typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY);
|
||||
typedef bool(*fun4_t)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT);
|
||||
typedef WORD(*fun5_t)();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -53,23 +51,16 @@ typedef WORD(*fun5_t)();
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) || defined(__e2k__)
|
||||
#define POSIXALIGNEDALLOC
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include "misc.h"
|
||||
#include "thread.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
namespace {
|
||||
|
||||
/// Version number. If Version is left empty, then compile date in the format
|
||||
/// DD-MM-YY and show in engine_info.
|
||||
const string Version = "15";
|
||||
const string Version = "";
|
||||
|
||||
/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
|
||||
/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
|
||||
@@ -112,14 +103,7 @@ public:
|
||||
|
||||
static Logger l;
|
||||
|
||||
if (l.file.is_open())
|
||||
{
|
||||
cout.rdbuf(l.out.buf);
|
||||
cin.rdbuf(l.in.buf);
|
||||
l.file.close();
|
||||
}
|
||||
|
||||
if (!fname.empty())
|
||||
if (!fname.empty() && !l.file.is_open())
|
||||
{
|
||||
l.file.open(fname, ifstream::out);
|
||||
|
||||
@@ -132,18 +116,23 @@ public:
|
||||
cin.rdbuf(&l.in);
|
||||
cout.rdbuf(&l.out);
|
||||
}
|
||||
else if (fname.empty() && l.file.is_open())
|
||||
{
|
||||
cout.rdbuf(l.out.buf);
|
||||
cin.rdbuf(l.in.buf);
|
||||
l.file.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
/// engine_info() returns the full name of the current Stockfish version. This
|
||||
/// will be either "Stockfish <Tag> DD-MM-YY" (where DD-MM-YY is the date when
|
||||
/// the program was compiled) or "Stockfish <Version>", depending on whether
|
||||
/// Version is empty.
|
||||
|
||||
string engine_info(bool to_uci) {
|
||||
const string engine_info(bool to_uci) {
|
||||
|
||||
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
|
||||
string month, day, year;
|
||||
@@ -166,7 +155,7 @@ string engine_info(bool to_uci) {
|
||||
|
||||
/// compiler_info() returns a string trying to describe the compiler we use
|
||||
|
||||
std::string compiler_info() {
|
||||
const std::string compiler_info() {
|
||||
|
||||
#define stringify2(x) #x
|
||||
#define stringify(x) stringify2(x)
|
||||
@@ -195,18 +184,6 @@ std::string compiler_info() {
|
||||
compiler += "(version ";
|
||||
compiler += stringify(_MSC_FULL_VER) "." stringify(_MSC_BUILD);
|
||||
compiler += ")";
|
||||
#elif defined(__e2k__) && defined(__LCC__)
|
||||
#define dot_ver2(n) \
|
||||
compiler += (char)'.'; \
|
||||
compiler += (char)('0' + (n) / 10); \
|
||||
compiler += (char)('0' + (n) % 10);
|
||||
|
||||
compiler += "MCST LCC ";
|
||||
compiler += "(version ";
|
||||
compiler += std::to_string(__LCC__ / 100);
|
||||
dot_ver2(__LCC__ % 100)
|
||||
dot_ver2(__LCC_MINOR__)
|
||||
compiler += ")";
|
||||
#elif __GNUC__
|
||||
compiler += "g++ (GNUC) ";
|
||||
compiler += make_version_string(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
|
||||
@@ -237,33 +214,26 @@ std::string compiler_info() {
|
||||
|
||||
compiler += "\nCompilation settings include: ";
|
||||
compiler += (Is64Bit ? " 64bit" : " 32bit");
|
||||
#if defined(USE_VNNI)
|
||||
compiler += " VNNI";
|
||||
#endif
|
||||
#if defined(USE_AVX512)
|
||||
compiler += " AVX512";
|
||||
#endif
|
||||
compiler += (HasPext ? " BMI2" : "");
|
||||
#if defined(USE_AVX2)
|
||||
compiler += " AVX2";
|
||||
#endif
|
||||
#if defined(USE_SSE42)
|
||||
compiler += " SSE42";
|
||||
#endif
|
||||
#if defined(USE_SSE41)
|
||||
compiler += " SSE41";
|
||||
#endif
|
||||
#if defined(USE_SSSE3)
|
||||
compiler += " SSSE3";
|
||||
#endif
|
||||
#if defined(USE_SSE2)
|
||||
compiler += " SSE2";
|
||||
#if defined(USE_SSE3)
|
||||
compiler += " SSE3";
|
||||
#endif
|
||||
compiler += (HasPopCnt ? " POPCNT" : "");
|
||||
#if defined(USE_MMX)
|
||||
compiler += " MMX";
|
||||
#endif
|
||||
#if defined(USE_NEON)
|
||||
compiler += " NEON";
|
||||
#endif
|
||||
|
||||
compiler += (HasPext ? " BMI2" : "");
|
||||
compiler += (HasPopCnt ? " POPCNT" : "");
|
||||
#if !defined(NDEBUG)
|
||||
compiler += " DEBUG";
|
||||
#endif
|
||||
@@ -346,16 +316,13 @@ void prefetch(void* addr) {
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/// std_aligned_alloc() is our wrapper for systems where the c++17 implementation
|
||||
/// does not guarantee the availability of aligned_alloc(). Memory allocated with
|
||||
/// std_aligned_alloc() must be freed with std_aligned_free().
|
||||
/// Wrappers for systems where the c++17 implementation doesn't guarantee the availability of aligned_alloc.
|
||||
/// Memory allocated with std_aligned_alloc must be freed with std_aligned_free.
|
||||
///
|
||||
|
||||
void* std_aligned_alloc(size_t alignment, size_t size) {
|
||||
|
||||
#if defined(POSIXALIGNEDALLOC)
|
||||
void *mem;
|
||||
return posix_memalign(&mem, alignment, size) ? nullptr : mem;
|
||||
#if defined(__APPLE__)
|
||||
return aligned_alloc(alignment, size);
|
||||
#elif defined(_WIN32)
|
||||
return _mm_malloc(size, alignment);
|
||||
#else
|
||||
@@ -364,8 +331,7 @@ void* std_aligned_alloc(size_t alignment, size_t size) {
|
||||
}
|
||||
|
||||
void std_aligned_free(void* ptr) {
|
||||
|
||||
#if defined(POSIXALIGNEDALLOC)
|
||||
#if defined(__APPLE__)
|
||||
free(ptr);
|
||||
#elif defined(_WIN32)
|
||||
_mm_free(ptr);
|
||||
@@ -374,16 +340,25 @@ void std_aligned_free(void* ptr) {
|
||||
#endif
|
||||
}
|
||||
|
||||
/// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages.
|
||||
/// aligned_ttmem_alloc() will return suitably aligned memory, and if possible use large pages.
|
||||
/// The returned pointer is the aligned one, while the mem argument is the one that needs
|
||||
/// to be passed to free. With c++17 some of this functionality could be simplified.
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
|
||||
static void* aligned_large_pages_alloc_windows(size_t allocSize) {
|
||||
void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
|
||||
|
||||
#if !defined(_WIN64)
|
||||
(void)allocSize; // suppress unused-parameter compiler warning
|
||||
return nullptr;
|
||||
#else
|
||||
constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page sizes
|
||||
size_t size = ((allocSize + alignment - 1) / alignment) * alignment; // multiple of alignment
|
||||
if (posix_memalign(&mem, alignment, size))
|
||||
mem = nullptr;
|
||||
madvise(mem, allocSize, MADV_HUGEPAGE);
|
||||
return mem;
|
||||
}
|
||||
|
||||
#elif defined(_WIN64)
|
||||
|
||||
static void* aligned_ttmem_alloc_large_pages(size_t allocSize) {
|
||||
|
||||
HANDLE hProcessToken { };
|
||||
LUID luid { };
|
||||
@@ -426,14 +401,25 @@ static void* aligned_large_pages_alloc_windows(size_t allocSize) {
|
||||
CloseHandle(hProcessToken);
|
||||
|
||||
return mem;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void* aligned_large_pages_alloc(size_t allocSize) {
|
||||
void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
|
||||
|
||||
static bool firstCall = true;
|
||||
|
||||
// Try to allocate large pages
|
||||
void* mem = aligned_large_pages_alloc_windows(allocSize);
|
||||
mem = aligned_ttmem_alloc_large_pages(allocSize);
|
||||
|
||||
// Suppress info strings on the first call. The first call occurs before 'uci'
|
||||
// is received and in that case this output confuses some GUIs.
|
||||
if (!firstCall)
|
||||
{
|
||||
if (mem)
|
||||
sync_cout << "info string Hash table allocation: Windows large pages used." << sync_endl;
|
||||
else
|
||||
sync_cout << "info string Hash table allocation: Windows large pages not used." << sync_endl;
|
||||
}
|
||||
firstCall = false;
|
||||
|
||||
// Fall back to regular, page aligned, allocation if necessary
|
||||
if (!mem)
|
||||
@@ -444,46 +430,37 @@ void* aligned_large_pages_alloc(size_t allocSize) {
|
||||
|
||||
#else
|
||||
|
||||
void* aligned_large_pages_alloc(size_t allocSize) {
|
||||
void* aligned_ttmem_alloc(size_t allocSize, void*& mem) {
|
||||
|
||||
#if defined(__linux__)
|
||||
constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page size
|
||||
#else
|
||||
constexpr size_t alignment = 4096; // assumed small page size
|
||||
#endif
|
||||
|
||||
// round up to multiples of alignment
|
||||
size_t size = ((allocSize + alignment - 1) / alignment) * alignment;
|
||||
void *mem = std_aligned_alloc(alignment, size);
|
||||
#if defined(MADV_HUGEPAGE)
|
||||
madvise(mem, size, MADV_HUGEPAGE);
|
||||
#endif
|
||||
return mem;
|
||||
constexpr size_t alignment = 64; // assumed cache line size
|
||||
size_t size = allocSize + alignment - 1; // allocate some extra space
|
||||
mem = malloc(size);
|
||||
void* ret = reinterpret_cast<void*>((uintptr_t(mem) + alignment - 1) & ~uintptr_t(alignment - 1));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/// aligned_large_pages_free() will free the previously allocated ttmem
|
||||
/// aligned_ttmem_free() will free the previously allocated ttmem
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(_WIN64)
|
||||
|
||||
void aligned_large_pages_free(void* mem) {
|
||||
void aligned_ttmem_free(void* mem) {
|
||||
|
||||
if (mem && !VirtualFree(mem, 0, MEM_RELEASE))
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
std::cerr << "Failed to free large page memory. Error code: 0x"
|
||||
<< std::hex << err
|
||||
<< std::dec << std::endl;
|
||||
std::cerr << "Failed to free transposition table. Error code: 0x" <<
|
||||
std::hex << err << std::dec << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void aligned_large_pages_free(void *mem) {
|
||||
std_aligned_free(mem);
|
||||
void aligned_ttmem_free(void *mem) {
|
||||
free(mem);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -497,11 +474,11 @@ void bindThisThread(size_t) {}
|
||||
|
||||
#else
|
||||
|
||||
/// best_node() retrieves logical processor information using Windows specific
|
||||
/// API and returns the best node id for the thread with index idx. Original
|
||||
/// best_group() retrieves logical processor information using Windows specific
|
||||
/// API and returns the best group id for the thread with index idx. Original
|
||||
/// code from Texel by Peter Österlund.
|
||||
|
||||
int best_node(size_t idx) {
|
||||
int best_group(size_t idx) {
|
||||
|
||||
int threads = 0;
|
||||
int nodes = 0;
|
||||
@@ -515,8 +492,7 @@ int best_node(size_t idx) {
|
||||
if (!fun1)
|
||||
return -1;
|
||||
|
||||
// First call to GetLogicalProcessorInformationEx() to get returnLength.
|
||||
// We expect the call to fail due to null buffer.
|
||||
// First call to get returnLength. We expect it to fail due to null buffer
|
||||
if (fun1(RelationAll, nullptr, &returnLength))
|
||||
return -1;
|
||||
|
||||
@@ -524,7 +500,7 @@ int best_node(size_t idx) {
|
||||
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr;
|
||||
ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(returnLength);
|
||||
|
||||
// Second call to GetLogicalProcessorInformationEx(), now we expect to succeed
|
||||
// Second call, now we expect to succeed
|
||||
if (!fun1(RelationAll, buffer, &returnLength))
|
||||
{
|
||||
free(buffer);
|
||||
@@ -574,99 +550,24 @@ int best_node(size_t idx) {
|
||||
void bindThisThread(size_t idx) {
|
||||
|
||||
// Use only local variables to be thread-safe
|
||||
int node = best_node(idx);
|
||||
int group = best_group(idx);
|
||||
|
||||
if (node == -1)
|
||||
if (group == -1)
|
||||
return;
|
||||
|
||||
// Early exit if the needed API are not available at runtime
|
||||
HMODULE k32 = GetModuleHandle("Kernel32.dll");
|
||||
auto fun2 = (fun2_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx");
|
||||
auto fun3 = (fun3_t)(void(*)())GetProcAddress(k32, "SetThreadGroupAffinity");
|
||||
auto fun4 = (fun4_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMask2");
|
||||
auto fun5 = (fun5_t)(void(*)())GetProcAddress(k32, "GetMaximumProcessorGroupCount");
|
||||
|
||||
if (!fun2 || !fun3)
|
||||
return;
|
||||
|
||||
if (!fun4 || !fun5)
|
||||
{
|
||||
GROUP_AFFINITY affinity;
|
||||
if (fun2(node, &affinity)) // GetNumaNodeProcessorMaskEx
|
||||
fun3(GetCurrentThread(), &affinity, nullptr); // SetThreadGroupAffinity
|
||||
}
|
||||
else
|
||||
{
|
||||
// If a numa node has more than one processor group, we assume they are
|
||||
// sized equal and we spread threads evenly across the groups.
|
||||
USHORT elements, returnedElements;
|
||||
elements = fun5(); // GetMaximumProcessorGroupCount
|
||||
GROUP_AFFINITY *affinity = (GROUP_AFFINITY*)malloc(elements * sizeof(GROUP_AFFINITY));
|
||||
if (fun4(node, affinity, elements, &returnedElements)) // GetNumaNodeProcessorMask2
|
||||
fun3(GetCurrentThread(), &affinity[idx % returnedElements], nullptr); // SetThreadGroupAffinity
|
||||
free(affinity);
|
||||
}
|
||||
GROUP_AFFINITY affinity;
|
||||
if (fun2(group, &affinity))
|
||||
fun3(GetCurrentThread(), &affinity, nullptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace WinProcGroup
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#define GETCWD _getcwd
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#define GETCWD getcwd
|
||||
#endif
|
||||
|
||||
namespace CommandLine {
|
||||
|
||||
string argv0; // path+name of the executable binary, as given by argv[0]
|
||||
string binaryDirectory; // path of the executable directory
|
||||
string workingDirectory; // path of the working directory
|
||||
|
||||
void init(int argc, char* argv[]) {
|
||||
(void)argc;
|
||||
string pathSeparator;
|
||||
|
||||
// extract the path+name of the executable binary
|
||||
argv0 = argv[0];
|
||||
|
||||
#ifdef _WIN32
|
||||
pathSeparator = "\\";
|
||||
#ifdef _MSC_VER
|
||||
// Under windows argv[0] may not have the extension. Also _get_pgmptr() had
|
||||
// issues in some windows 10 versions, so check returned values carefully.
|
||||
char* pgmptr = nullptr;
|
||||
if (!_get_pgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr)
|
||||
argv0 = pgmptr;
|
||||
#endif
|
||||
#else
|
||||
pathSeparator = "/";
|
||||
#endif
|
||||
|
||||
// extract the working directory
|
||||
workingDirectory = "";
|
||||
char buff[40000];
|
||||
char* cwd = GETCWD(buff, 40000);
|
||||
if (cwd)
|
||||
workingDirectory = cwd;
|
||||
|
||||
// extract the binary directory path from argv0
|
||||
binaryDirectory = argv0;
|
||||
size_t pos = binaryDirectory.find_last_of("\\/");
|
||||
if (pos == std::string::npos)
|
||||
binaryDirectory = "." + pathSeparator;
|
||||
else
|
||||
binaryDirectory.resize(pos + 1);
|
||||
|
||||
// pattern replacement: "./" at the start of path is replaced by the working directory
|
||||
if (binaryDirectory.find("." + pathSeparator) == 0)
|
||||
binaryDirectory.replace(0, 1, workingDirectory);
|
||||
}
|
||||
|
||||
|
||||
} // namespace CommandLine
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+11
-112
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -24,20 +24,17 @@
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
std::string engine_info(bool to_uci = false);
|
||||
std::string compiler_info();
|
||||
const std::string engine_info(bool to_uci = false);
|
||||
const std::string compiler_info();
|
||||
void prefetch(void* addr);
|
||||
void start_logger(const std::string& fname);
|
||||
void* std_aligned_alloc(size_t alignment, size_t size);
|
||||
void std_aligned_free(void* ptr);
|
||||
void* aligned_large_pages_alloc(size_t size); // memory aligned by page size, min alignment: 4096 bytes
|
||||
void aligned_large_pages_free(void* mem); // nop if mem == nullptr
|
||||
void* aligned_ttmem_alloc(size_t size, void*& mem);
|
||||
void aligned_ttmem_free(void* mem); // nop if mem == nullptr
|
||||
|
||||
void dbg_hit_on(bool b);
|
||||
void dbg_hit_on(bool c, bool b);
|
||||
@@ -45,7 +42,9 @@ void dbg_mean_of(int v);
|
||||
void dbg_print();
|
||||
|
||||
typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds
|
||||
|
||||
static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits");
|
||||
|
||||
inline TimePoint now() {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>
|
||||
(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
@@ -66,106 +65,15 @@ std::ostream& operator<<(std::ostream&, SyncCout);
|
||||
#define sync_cout std::cout << IO_LOCK
|
||||
#define sync_endl std::endl << IO_UNLOCK
|
||||
|
||||
namespace Utility {
|
||||
|
||||
// align_ptr_up() : get the first aligned element of an array.
|
||||
// ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes,
|
||||
// where N is the number of elements in the array.
|
||||
template <uintptr_t Alignment, typename T>
|
||||
T* align_ptr_up(T* ptr)
|
||||
{
|
||||
static_assert(alignof(T) < Alignment);
|
||||
|
||||
const uintptr_t ptrint = reinterpret_cast<uintptr_t>(reinterpret_cast<char*>(ptr));
|
||||
return reinterpret_cast<T*>(reinterpret_cast<char*>((ptrint + (Alignment - 1)) / Alignment * Alignment));
|
||||
/// Clamp a value between lo and hi. Available in c++17.
|
||||
template<class T> constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
|
||||
return v < lo ? lo : v > hi ? hi : v;
|
||||
}
|
||||
|
||||
|
||||
// IsLittleEndian : true if and only if the binary is compiled on a little endian machine
|
||||
static inline const union { uint32_t i; char c[4]; } Le = { 0x01020304 };
|
||||
static inline const bool IsLittleEndian = (Le.c[0] == 4);
|
||||
|
||||
|
||||
// RunningAverage : a class to calculate a running average of a series of values.
|
||||
// For efficiency, all computations are done with integers.
|
||||
class RunningAverage {
|
||||
public:
|
||||
|
||||
// Reset the running average to rational value p / q
|
||||
void set(int64_t p, int64_t q)
|
||||
{ average = p * PERIOD * RESOLUTION / q; }
|
||||
|
||||
// Update average with value v
|
||||
void update(int64_t v)
|
||||
{ average = RESOLUTION * v + (PERIOD - 1) * average / PERIOD; }
|
||||
|
||||
// Test if average is strictly greater than rational a / b
|
||||
bool is_greater(int64_t a, int64_t b) const
|
||||
{ return b * average > a * (PERIOD * RESOLUTION); }
|
||||
|
||||
int64_t value() const
|
||||
{ return average / (PERIOD * RESOLUTION); }
|
||||
|
||||
private :
|
||||
static constexpr int64_t PERIOD = 4096;
|
||||
static constexpr int64_t RESOLUTION = 1024;
|
||||
int64_t average;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t MaxSize>
|
||||
class ValueList {
|
||||
|
||||
public:
|
||||
std::size_t size() const { return size_; }
|
||||
void resize(std::size_t newSize) { size_ = newSize; }
|
||||
void push_back(const T& value) { values_[size_++] = value; }
|
||||
T& operator[](std::size_t index) { return values_[index]; }
|
||||
T* begin() { return values_; }
|
||||
T* end() { return values_ + size_; }
|
||||
const T& operator[](std::size_t index) const { return values_[index]; }
|
||||
const T* begin() const { return values_; }
|
||||
const T* end() const { return values_ + size_; }
|
||||
|
||||
void swap(ValueList& other) {
|
||||
const std::size_t maxSize = std::max(size_, other.size_);
|
||||
for (std::size_t i = 0; i < maxSize; ++i) {
|
||||
std::swap(values_[i], other.values_[i]);
|
||||
}
|
||||
std::swap(size_, other.size_);
|
||||
}
|
||||
|
||||
private:
|
||||
T values_[MaxSize];
|
||||
std::size_t size_ = 0;
|
||||
};
|
||||
|
||||
|
||||
/// sigmoid(t, x0, y0, C, P, Q) implements a sigmoid-like function using only integers,
|
||||
/// with the following properties:
|
||||
///
|
||||
/// - sigmoid is centered in (x0, y0)
|
||||
/// - sigmoid has amplitude [-P/Q , P/Q] instead of [-1 , +1]
|
||||
/// - limit is (y0 - P/Q) when t tends to -infinity
|
||||
/// - limit is (y0 + P/Q) when t tends to +infinity
|
||||
/// - the slope can be adjusted using C > 0, smaller C giving a steeper sigmoid
|
||||
/// - the slope of the sigmoid when t = x0 is P/(Q*C)
|
||||
/// - sigmoid is increasing with t when P > 0 and Q > 0
|
||||
/// - to get a decreasing sigmoid, change sign of P
|
||||
/// - mean value of the sigmoid is y0
|
||||
///
|
||||
/// Use <https://www.desmos.com/calculator/jhh83sqq92> to draw the sigmoid
|
||||
|
||||
inline int64_t sigmoid(int64_t t, int64_t x0,
|
||||
int64_t y0,
|
||||
int64_t C,
|
||||
int64_t P,
|
||||
int64_t Q)
|
||||
{
|
||||
assert(C > 0);
|
||||
assert(Q != 0);
|
||||
return y0 + P * (t-x0) / (Q * (std::abs(t-x0) + C)) ;
|
||||
}
|
||||
|
||||
|
||||
/// xorshift64star Pseudo-Random Number Generator
|
||||
/// This class is based on original code written and dedicated
|
||||
/// to the public domain by Sebastiano Vigna (2014).
|
||||
@@ -226,13 +134,4 @@ namespace WinProcGroup {
|
||||
void bindThisThread(size_t idx);
|
||||
}
|
||||
|
||||
namespace CommandLine {
|
||||
void init(int argc, char* argv[]);
|
||||
|
||||
extern std::string binaryDirectory; // path of the executable directory
|
||||
extern std::string workingDirectory; // path of the working directory
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef MISC_H_INCLUDED
|
||||
|
||||
+162
-71
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -21,21 +21,24 @@
|
||||
#include "movegen.h"
|
||||
#include "position.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
namespace {
|
||||
|
||||
template<GenType Type, Direction D>
|
||||
ExtMove* make_promotions(ExtMove* moveList, Square to) {
|
||||
ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {
|
||||
|
||||
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
{
|
||||
*moveList++ = make<PROMOTION>(to - D, to, QUEEN);
|
||||
if (attacks_bb<KNIGHT>(to) & ksq)
|
||||
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
|
||||
}
|
||||
|
||||
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
{
|
||||
*moveList++ = make<PROMOTION>(to - D, to, ROOK);
|
||||
*moveList++ = make<PROMOTION>(to - D, to, BISHOP);
|
||||
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
|
||||
if (!(attacks_bb<KNIGHT>(to) & ksq))
|
||||
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
|
||||
}
|
||||
|
||||
return moveList;
|
||||
@@ -52,16 +55,20 @@ namespace {
|
||||
constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
|
||||
constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
|
||||
|
||||
const Bitboard emptySquares = ~pos.pieces();
|
||||
const Bitboard enemies = Type == EVASIONS ? pos.checkers()
|
||||
: pos.pieces(Them);
|
||||
const Square ksq = pos.square<KING>(Them);
|
||||
Bitboard emptySquares;
|
||||
|
||||
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
|
||||
Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
|
||||
|
||||
Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target:
|
||||
Type == CAPTURES ? target : pos.pieces(Them));
|
||||
|
||||
// Single and double pawn pushes, no promotions
|
||||
if (Type != CAPTURES)
|
||||
{
|
||||
emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces());
|
||||
|
||||
Bitboard b1 = shift<Up>(pawnsNotOn7) & emptySquares;
|
||||
Bitboard b2 = shift<Up>(b1 & TRank3BB) & emptySquares;
|
||||
|
||||
@@ -73,24 +80,33 @@ namespace {
|
||||
|
||||
if (Type == QUIET_CHECKS)
|
||||
{
|
||||
// To make a quiet check, you either make a direct check by pushing a pawn
|
||||
// or push a blocker pawn that is not on the same file as the enemy king.
|
||||
// Discovered check promotion has been already generated amongst the captures.
|
||||
Square ksq = pos.square<KING>(Them);
|
||||
Bitboard dcCandidatePawns = pos.blockers_for_king(Them) & ~file_bb(ksq);
|
||||
b1 &= pawn_attacks_bb(Them, ksq) | shift< Up>(dcCandidatePawns);
|
||||
b2 &= pawn_attacks_bb(Them, ksq) | shift<Up+Up>(dcCandidatePawns);
|
||||
b1 &= pawn_attacks_bb(Them, ksq);
|
||||
b2 &= pawn_attacks_bb(Them, ksq);
|
||||
|
||||
// Add pawn pushes which give discovered check. This is possible only
|
||||
// if the pawn is not on the same file as the enemy king, because we
|
||||
// don't generate captures. Note that a possible discovery check
|
||||
// promotion has been already generated amongst the captures.
|
||||
Bitboard dcCandidateQuiets = pos.blockers_for_king(Them) & pawnsNotOn7;
|
||||
if (dcCandidateQuiets)
|
||||
{
|
||||
Bitboard dc1 = shift<Up>(dcCandidateQuiets) & emptySquares & ~file_bb(ksq);
|
||||
Bitboard dc2 = shift<Up>(dc1 & TRank3BB) & emptySquares;
|
||||
|
||||
b1 |= dc1;
|
||||
b2 |= dc2;
|
||||
}
|
||||
}
|
||||
|
||||
while (b1)
|
||||
{
|
||||
Square to = pop_lsb(b1);
|
||||
Square to = pop_lsb(&b1);
|
||||
*moveList++ = make_move(to - Up, to);
|
||||
}
|
||||
|
||||
while (b2)
|
||||
{
|
||||
Square to = pop_lsb(b2);
|
||||
Square to = pop_lsb(&b2);
|
||||
*moveList++ = make_move(to - Up - Up, to);
|
||||
}
|
||||
}
|
||||
@@ -98,24 +114,27 @@ namespace {
|
||||
// Promotions and underpromotions
|
||||
if (pawnsOn7)
|
||||
{
|
||||
if (Type == CAPTURES)
|
||||
emptySquares = ~pos.pieces();
|
||||
|
||||
if (Type == EVASIONS)
|
||||
emptySquares &= target;
|
||||
|
||||
Bitboard b1 = shift<UpRight>(pawnsOn7) & enemies;
|
||||
Bitboard b2 = shift<UpLeft >(pawnsOn7) & enemies;
|
||||
Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares;
|
||||
|
||||
if (Type == EVASIONS)
|
||||
b3 &= target;
|
||||
|
||||
while (b1)
|
||||
moveList = make_promotions<Type, UpRight>(moveList, pop_lsb(b1));
|
||||
moveList = make_promotions<Type, UpRight>(moveList, pop_lsb(&b1), ksq);
|
||||
|
||||
while (b2)
|
||||
moveList = make_promotions<Type, UpLeft >(moveList, pop_lsb(b2));
|
||||
moveList = make_promotions<Type, UpLeft >(moveList, pop_lsb(&b2), ksq);
|
||||
|
||||
while (b3)
|
||||
moveList = make_promotions<Type, Up >(moveList, pop_lsb(b3));
|
||||
moveList = make_promotions<Type, Up >(moveList, pop_lsb(&b3), ksq);
|
||||
}
|
||||
|
||||
// Standard and en passant captures
|
||||
// Standard and en-passant captures
|
||||
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
||||
{
|
||||
Bitboard b1 = shift<UpRight>(pawnsNotOn7) & enemies;
|
||||
@@ -123,13 +142,13 @@ namespace {
|
||||
|
||||
while (b1)
|
||||
{
|
||||
Square to = pop_lsb(b1);
|
||||
Square to = pop_lsb(&b1);
|
||||
*moveList++ = make_move(to - UpRight, to);
|
||||
}
|
||||
|
||||
while (b2)
|
||||
{
|
||||
Square to = pop_lsb(b2);
|
||||
Square to = pop_lsb(&b2);
|
||||
*moveList++ = make_move(to - UpLeft, to);
|
||||
}
|
||||
|
||||
@@ -137,8 +156,10 @@ namespace {
|
||||
{
|
||||
assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6));
|
||||
|
||||
// An en passant capture cannot resolve a discovered check
|
||||
if (Type == EVASIONS && (target & (pos.ep_square() + Up)))
|
||||
// An en passant capture can be an evasion only if the checking piece
|
||||
// is the double pushed pawn and so is in the target. Otherwise this
|
||||
// is a discovery check and we are forced to do otherwise.
|
||||
if (Type == EVASIONS && !(target & (pos.ep_square() - Up)))
|
||||
return moveList;
|
||||
|
||||
b1 = pawnsNotOn7 & pawn_attacks_bb(Them, pos.ep_square());
|
||||
@@ -146,7 +167,7 @@ namespace {
|
||||
assert(b1);
|
||||
|
||||
while (b1)
|
||||
*moveList++ = make<EN_PASSANT>(pop_lsb(b1), pos.ep_square());
|
||||
*moveList++ = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,19 +180,27 @@ namespace {
|
||||
|
||||
static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()");
|
||||
|
||||
Bitboard bb = pos.pieces(Us, Pt);
|
||||
const Square* pl = pos.squares<Pt>(Us);
|
||||
|
||||
while (bb)
|
||||
for (Square from = *pl; from != SQ_NONE; from = *++pl)
|
||||
{
|
||||
Square from = pop_lsb(bb);
|
||||
if (Checks)
|
||||
{
|
||||
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
|
||||
&& !(attacks_bb<Pt>(from) & target & pos.check_squares(Pt)))
|
||||
continue;
|
||||
|
||||
if (pos.blockers_for_king(~Us) & from)
|
||||
continue;
|
||||
}
|
||||
|
||||
Bitboard b = attacks_bb<Pt>(from, pos.pieces()) & target;
|
||||
|
||||
// To check, you either move freely a blocker or make a direct check.
|
||||
if (Checks && (Pt == QUEEN || !(pos.blockers_for_king(~Us) & from)))
|
||||
if (Checks)
|
||||
b &= pos.check_squares(Pt);
|
||||
|
||||
while (b)
|
||||
*moveList++ = make_move(from, pop_lsb(b));
|
||||
*moveList++ = make_move(from, pop_lsb(&b));
|
||||
}
|
||||
|
||||
return moveList;
|
||||
@@ -180,39 +209,46 @@ namespace {
|
||||
|
||||
template<Color Us, GenType Type>
|
||||
ExtMove* generate_all(const Position& pos, ExtMove* moveList) {
|
||||
|
||||
static_assert(Type != LEGAL, "Unsupported type in generate_all()");
|
||||
|
||||
constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations
|
||||
const Square ksq = pos.square<KING>(Us);
|
||||
constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations
|
||||
Bitboard target;
|
||||
|
||||
// Skip generating non-king moves when in double check
|
||||
if (Type != EVASIONS || !more_than_one(pos.checkers()))
|
||||
switch (Type)
|
||||
{
|
||||
target = Type == EVASIONS ? between_bb(ksq, lsb(pos.checkers()))
|
||||
: Type == NON_EVASIONS ? ~pos.pieces( Us)
|
||||
: Type == CAPTURES ? pos.pieces(~Us)
|
||||
: ~pos.pieces( ); // QUIETS || QUIET_CHECKS
|
||||
|
||||
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
|
||||
moveList = generate_moves<Us, KNIGHT, Checks>(pos, moveList, target);
|
||||
moveList = generate_moves<Us, BISHOP, Checks>(pos, moveList, target);
|
||||
moveList = generate_moves<Us, ROOK, Checks>(pos, moveList, target);
|
||||
moveList = generate_moves<Us, QUEEN, Checks>(pos, moveList, target);
|
||||
case CAPTURES:
|
||||
target = pos.pieces(~Us);
|
||||
break;
|
||||
case QUIETS:
|
||||
case QUIET_CHECKS:
|
||||
target = ~pos.pieces();
|
||||
break;
|
||||
case EVASIONS:
|
||||
{
|
||||
Square checksq = lsb(pos.checkers());
|
||||
target = between_bb(pos.square<KING>(Us), checksq) | checksq;
|
||||
break;
|
||||
}
|
||||
case NON_EVASIONS:
|
||||
target = ~pos.pieces(Us);
|
||||
break;
|
||||
default:
|
||||
static_assert(true, "Unsupported type in generate_all()");
|
||||
}
|
||||
|
||||
if (!Checks || pos.blockers_for_king(~Us) & ksq)
|
||||
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
|
||||
moveList = generate_moves<Us, KNIGHT, Checks>(pos, moveList, target);
|
||||
moveList = generate_moves<Us, BISHOP, Checks>(pos, moveList, target);
|
||||
moveList = generate_moves<Us, ROOK, Checks>(pos, moveList, target);
|
||||
moveList = generate_moves<Us, QUEEN, Checks>(pos, moveList, target);
|
||||
|
||||
if (Type != QUIET_CHECKS && Type != EVASIONS)
|
||||
{
|
||||
Bitboard b = attacks_bb<KING>(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target);
|
||||
if (Checks)
|
||||
b &= ~attacks_bb<QUEEN>(pos.square<KING>(~Us));
|
||||
|
||||
Square ksq = pos.square<KING>(Us);
|
||||
Bitboard b = attacks_bb<KING>(ksq) & target;
|
||||
while (b)
|
||||
*moveList++ = make_move(ksq, pop_lsb(b));
|
||||
*moveList++ = make_move(ksq, pop_lsb(&b));
|
||||
|
||||
if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING))
|
||||
for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } )
|
||||
if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING))
|
||||
for(CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } )
|
||||
if (!pos.castling_impeded(cr) && pos.can_castle(cr))
|
||||
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(cr));
|
||||
}
|
||||
@@ -223,10 +259,8 @@ namespace {
|
||||
} // namespace
|
||||
|
||||
|
||||
/// <CAPTURES> Generates all pseudo-legal captures plus queen promotions
|
||||
/// <QUIETS> Generates all pseudo-legal non-captures and underpromotions
|
||||
/// <EVASIONS> Generates all pseudo-legal check evasions when the side to move is in check
|
||||
/// <QUIET_CHECKS> Generates all pseudo-legal non-captures giving check, except castling and promotions
|
||||
/// <CAPTURES> Generates all pseudo-legal captures plus queen and checking knight promotions
|
||||
/// <QUIETS> Generates all pseudo-legal non-captures and underpromotions(except checking knight)
|
||||
/// <NON_EVASIONS> Generates all pseudo-legal captures and non-captures
|
||||
///
|
||||
/// Returns a pointer to the end of the move list.
|
||||
@@ -234,8 +268,8 @@ namespace {
|
||||
template<GenType Type>
|
||||
ExtMove* generate(const Position& pos, ExtMove* moveList) {
|
||||
|
||||
static_assert(Type != LEGAL, "Unsupported type in generate()");
|
||||
assert((Type == EVASIONS) == (bool)pos.checkers());
|
||||
static_assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS, "Unsupported type in generate()");
|
||||
assert(!pos.checkers());
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
|
||||
@@ -246,11 +280,70 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) {
|
||||
// Explicit template instantiations
|
||||
template ExtMove* generate<CAPTURES>(const Position&, ExtMove*);
|
||||
template ExtMove* generate<QUIETS>(const Position&, ExtMove*);
|
||||
template ExtMove* generate<EVASIONS>(const Position&, ExtMove*);
|
||||
template ExtMove* generate<QUIET_CHECKS>(const Position&, ExtMove*);
|
||||
template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
|
||||
|
||||
|
||||
/// generate<QUIET_CHECKS> generates all pseudo-legal non-captures.
|
||||
/// Returns a pointer to the end of the move list.
|
||||
template<>
|
||||
ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
|
||||
|
||||
assert(!pos.checkers());
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
Bitboard dc = pos.blockers_for_king(~us) & pos.pieces(us) & ~pos.pieces(PAWN);
|
||||
|
||||
while (dc)
|
||||
{
|
||||
Square from = pop_lsb(&dc);
|
||||
PieceType pt = type_of(pos.piece_on(from));
|
||||
|
||||
Bitboard b = attacks_bb(pt, from, pos.pieces()) & ~pos.pieces();
|
||||
|
||||
if (pt == KING)
|
||||
b &= ~attacks_bb<QUEEN>(pos.square<KING>(~us));
|
||||
|
||||
while (b)
|
||||
*moveList++ = make_move(from, pop_lsb(&b));
|
||||
}
|
||||
|
||||
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList)
|
||||
: generate_all<BLACK, QUIET_CHECKS>(pos, moveList);
|
||||
}
|
||||
|
||||
|
||||
/// generate<EVASIONS> generates all pseudo-legal check evasions when the side
|
||||
/// to move is in check. Returns a pointer to the end of the move list.
|
||||
template<>
|
||||
ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* moveList) {
|
||||
|
||||
assert(pos.checkers());
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
Square ksq = pos.square<KING>(us);
|
||||
Bitboard sliderAttacks = 0;
|
||||
Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN);
|
||||
|
||||
// Find all the squares attacked by slider checkers. We will remove them from
|
||||
// the king evasions in order to skip known illegal moves, which avoids any
|
||||
// useless legality checks later on.
|
||||
while (sliders)
|
||||
sliderAttacks |= line_bb(ksq, pop_lsb(&sliders)) & ~pos.checkers();
|
||||
|
||||
// Generate evasions for king, capture and non capture moves
|
||||
Bitboard b = attacks_bb<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
|
||||
while (b)
|
||||
*moveList++ = make_move(ksq, pop_lsb(&b));
|
||||
|
||||
if (more_than_one(pos.checkers()))
|
||||
return moveList; // Double check, only a king move can save the day
|
||||
|
||||
// Generate blocking evasions or captures of the checking piece
|
||||
return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList)
|
||||
: generate_all<BLACK, EVASIONS>(pos, moveList);
|
||||
}
|
||||
|
||||
|
||||
/// generate<LEGAL> generates all the legal moves in the given position
|
||||
|
||||
template<>
|
||||
@@ -264,7 +357,7 @@ ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
|
||||
moveList = pos.checkers() ? generate<EVASIONS >(pos, moveList)
|
||||
: generate<NON_EVASIONS>(pos, moveList);
|
||||
while (cur != moveList)
|
||||
if ( ((pinned && pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT)
|
||||
if ( (pinned || from_sq(*cur) == ksq || type_of(*cur) == ENPASSANT)
|
||||
&& !pos.legal(*cur))
|
||||
*cur = (--moveList)->move;
|
||||
else
|
||||
@@ -272,5 +365,3 @@ ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
|
||||
|
||||
return moveList;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+1
-5
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -23,8 +23,6 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
class Position;
|
||||
|
||||
enum GenType {
|
||||
@@ -72,6 +70,4 @@ private:
|
||||
ExtMove moveList[MAX_MOVES], *last;
|
||||
};
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef MOVEGEN_H_INCLUDED
|
||||
|
||||
+34
-76
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -18,11 +18,8 @@
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "movepick.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
namespace {
|
||||
|
||||
enum Stages {
|
||||
@@ -57,14 +54,11 @@ namespace {
|
||||
/// ordering is at the current node.
|
||||
|
||||
/// MovePicker constructor for the main search
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
|
||||
const CapturePieceToHistory* cph,
|
||||
const PieceToHistory** ch,
|
||||
Move cm,
|
||||
const Move* killers)
|
||||
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch),
|
||||
ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d)
|
||||
{
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const LowPlyHistory* lp,
|
||||
const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, const Move* killers, int pl)
|
||||
: pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch),
|
||||
ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d), ply(pl) {
|
||||
|
||||
assert(d > 0);
|
||||
|
||||
stage = (pos.checkers() ? EVASION_TT : MAIN_TT) +
|
||||
@@ -73,24 +67,21 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist
|
||||
|
||||
/// MovePicker constructor for quiescence search
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh,
|
||||
const CapturePieceToHistory* cph,
|
||||
const PieceToHistory** ch,
|
||||
Square rs)
|
||||
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d)
|
||||
{
|
||||
const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs)
|
||||
: pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d) {
|
||||
|
||||
assert(d <= 0);
|
||||
|
||||
stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) +
|
||||
!( ttm
|
||||
&& (pos.checkers() || depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare)
|
||||
&& pos.pseudo_legal(ttm));
|
||||
!(ttm && (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare)
|
||||
&& pos.pseudo_legal(ttm));
|
||||
}
|
||||
|
||||
/// MovePicker constructor for ProbCut: we generate captures with SEE greater
|
||||
/// than or equal to the given threshold.
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Value th, Depth d, const CapturePieceToHistory* cph)
|
||||
: pos(p), captureHistory(cph), ttMove(ttm), threshold(th), depth(d)
|
||||
{
|
||||
MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph)
|
||||
: pos(p), captureHistory(cph), ttMove(ttm), threshold(th) {
|
||||
|
||||
assert(!pos.checkers());
|
||||
|
||||
stage = PROBCUT_TT + !(ttm && pos.capture(ttm)
|
||||
@@ -106,48 +97,18 @@ void MovePicker::score() {
|
||||
|
||||
static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type");
|
||||
|
||||
Bitboard threatened, threatenedByPawn, threatenedByMinor, threatenedByRook;
|
||||
if constexpr (Type == QUIETS)
|
||||
{
|
||||
Color us = pos.side_to_move();
|
||||
// squares threatened by pawns
|
||||
threatenedByPawn = pos.attacks_by<PAWN>(~us);
|
||||
// squares threatened by minors or pawns
|
||||
threatenedByMinor = pos.attacks_by<KNIGHT>(~us) | pos.attacks_by<BISHOP>(~us) | threatenedByPawn;
|
||||
// squares threatened by rooks, minors or pawns
|
||||
threatenedByRook = pos.attacks_by<ROOK>(~us) | threatenedByMinor;
|
||||
|
||||
// pieces threatened by pieces of lesser material value
|
||||
threatened = (pos.pieces(us, QUEEN) & threatenedByRook)
|
||||
| (pos.pieces(us, ROOK) & threatenedByMinor)
|
||||
| (pos.pieces(us, KNIGHT, BISHOP) & threatenedByPawn);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Silence unused variable warnings
|
||||
(void) threatened;
|
||||
(void) threatenedByPawn;
|
||||
(void) threatenedByMinor;
|
||||
(void) threatenedByRook;
|
||||
}
|
||||
|
||||
for (auto& m : *this)
|
||||
if constexpr (Type == CAPTURES)
|
||||
m.value = 6 * int(PieceValue[MG][pos.piece_on(to_sq(m))])
|
||||
+ (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))];
|
||||
if (Type == CAPTURES)
|
||||
m.value = int(PieceValue[MG][pos.piece_on(to_sq(m))]) * 6
|
||||
+ (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))];
|
||||
|
||||
else if constexpr (Type == QUIETS)
|
||||
else if (Type == QUIETS)
|
||||
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
|
||||
+ 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ 2 * (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ 2 * (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)]
|
||||
+ (threatened & from_sq(m) ?
|
||||
(type_of(pos.moved_piece(m)) == QUEEN && !(to_sq(m) & threatenedByRook) ? 50000
|
||||
: type_of(pos.moved_piece(m)) == ROOK && !(to_sq(m) & threatenedByMinor) ? 25000
|
||||
: !(to_sq(m) & threatenedByPawn) ? 15000
|
||||
: 0)
|
||||
: 0);
|
||||
+ (ply < MAX_LPH ? std::min(4, depth / 3) * (*lowPlyHistory)[ply][from_to(m)] : 0);
|
||||
|
||||
else // Type == EVASIONS
|
||||
{
|
||||
@@ -155,8 +116,8 @@ void MovePicker::score() {
|
||||
m.value = PieceValue[MG][pos.piece_on(to_sq(m))]
|
||||
- Value(type_of(pos.moved_piece(m)));
|
||||
else
|
||||
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
|
||||
+ 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
|
||||
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
|
||||
+ (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
|
||||
- (1 << 28);
|
||||
}
|
||||
}
|
||||
@@ -180,7 +141,7 @@ Move MovePicker::select(Pred filter) {
|
||||
}
|
||||
|
||||
/// MovePicker::next_move() is the most important method of the MovePicker class. It
|
||||
/// returns a new pseudo-legal move every time it is called until there are no more
|
||||
/// returns a new pseudo legal move every time it is called until there are no more
|
||||
/// moves left, picking the move with the highest score from a list of generated moves.
|
||||
Move MovePicker::next_move(bool skipQuiets) {
|
||||
|
||||
@@ -201,12 +162,11 @@ top:
|
||||
endMoves = generate<CAPTURES>(pos, cur);
|
||||
|
||||
score<CAPTURES>();
|
||||
partial_insertion_sort(cur, endMoves, -3000 * depth);
|
||||
++stage;
|
||||
goto top;
|
||||
|
||||
case GOOD_CAPTURE:
|
||||
if (select<Next>([&](){
|
||||
if (select<Best>([&](){
|
||||
return pos.see_ge(*cur, Value(-69 * cur->value / 1024)) ?
|
||||
// Move losing capture to endBadCaptures to be tried later
|
||||
true : (*endBadCaptures++ = *cur, false); }))
|
||||
@@ -222,7 +182,7 @@ top:
|
||||
--endMoves;
|
||||
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
/* fallthrough */
|
||||
|
||||
case REFUTATION:
|
||||
if (select<Next>([&](){ return *cur != MOVE_NONE
|
||||
@@ -230,7 +190,7 @@ top:
|
||||
&& pos.pseudo_legal(*cur); }))
|
||||
return *(cur - 1);
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
/* fallthrough */
|
||||
|
||||
case QUIET_INIT:
|
||||
if (!skipQuiets)
|
||||
@@ -243,7 +203,7 @@ top:
|
||||
}
|
||||
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
/* fallthrough */
|
||||
|
||||
case QUIET:
|
||||
if ( !skipQuiets
|
||||
@@ -257,7 +217,7 @@ top:
|
||||
endMoves = endBadCaptures;
|
||||
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
/* fallthrough */
|
||||
|
||||
case BAD_CAPTURE:
|
||||
return select<Next>([](){ return true; });
|
||||
@@ -268,16 +228,16 @@ top:
|
||||
|
||||
score<EVASIONS>();
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
/* fallthrough */
|
||||
|
||||
case EVASION:
|
||||
return select<Best>([](){ return true; });
|
||||
|
||||
case PROBCUT:
|
||||
return select<Next>([&](){ return pos.see_ge(*cur, threshold); });
|
||||
return select<Best>([&](){ return pos.see_ge(*cur, threshold); });
|
||||
|
||||
case QCAPTURE:
|
||||
if (select<Next>([&](){ return depth > DEPTH_QS_RECAPTURES
|
||||
if (select<Best>([&](){ return depth > DEPTH_QS_RECAPTURES
|
||||
|| to_sq(*cur) == recaptureSquare; }))
|
||||
return *(cur - 1);
|
||||
|
||||
@@ -286,14 +246,14 @@ top:
|
||||
return MOVE_NONE;
|
||||
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
/* fallthrough */
|
||||
|
||||
case QCHECK_INIT:
|
||||
cur = moves;
|
||||
endMoves = generate<QUIET_CHECKS>(pos, cur);
|
||||
|
||||
++stage;
|
||||
[[fallthrough]];
|
||||
/* fallthrough */
|
||||
|
||||
case QCHECK:
|
||||
return select<Next>([](){ return true; });
|
||||
@@ -302,5 +262,3 @@ top:
|
||||
assert(false);
|
||||
return MOVE_NONE; // Silence warning
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+23
-17
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -27,8 +27,6 @@
|
||||
#include "position.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
/// StatsEntry stores the stat table value. It is usually a number but could
|
||||
/// be a move or even a nested history. We use a class instead of naked value
|
||||
/// to directly call history update operator<<() on the entry so to use stats
|
||||
@@ -86,7 +84,13 @@ enum StatsType { NoCaptures, Captures };
|
||||
/// unsuccessful during the current search, and is used for reduction and move
|
||||
/// ordering decisions. It uses 2 tables (one for each color) indexed by
|
||||
/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards
|
||||
typedef Stats<int16_t, 14365, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
|
||||
typedef Stats<int16_t, 10692, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)> ButterflyHistory;
|
||||
|
||||
/// At higher depths LowPlyHistory records successful quiet moves near the root and quiet
|
||||
/// moves which are/were in the PV (ttPv)
|
||||
/// It is cleared with each new search and filled during iterative deepening
|
||||
constexpr int MAX_LPH = 4;
|
||||
typedef Stats<int16_t, 10692, MAX_LPH, int(SQUARE_NB) * int(SQUARE_NB)> LowPlyHistory;
|
||||
|
||||
/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous
|
||||
/// move, see www.chessprogramming.org/Countermove_Heuristic
|
||||
@@ -104,12 +108,12 @@ typedef Stats<int16_t, 29952, PIECE_NB, SQUARE_NB> PieceToHistory;
|
||||
typedef Stats<PieceToHistory, NOT_USED, PIECE_NB, SQUARE_NB> ContinuationHistory;
|
||||
|
||||
|
||||
/// MovePicker class is used to pick one pseudo-legal move at a time from the
|
||||
/// MovePicker class is used to pick one pseudo legal move at a time from the
|
||||
/// current position. The most important method is next_move(), which returns a
|
||||
/// new pseudo-legal move each time it is called, until there are no moves left,
|
||||
/// when MOVE_NONE is returned. In order to improve the efficiency of the
|
||||
/// alpha-beta algorithm, MovePicker attempts to return the moves which are most
|
||||
/// likely to get a cut-off first.
|
||||
/// new pseudo legal move each time it is called, until there are no moves left,
|
||||
/// when MOVE_NONE is returned. In order to improve the efficiency of the alpha
|
||||
/// beta algorithm, MovePicker attempts to return the moves which are most likely
|
||||
/// to get a cut-off first.
|
||||
class MovePicker {
|
||||
|
||||
enum PickType { Next, Best };
|
||||
@@ -117,16 +121,18 @@ class MovePicker {
|
||||
public:
|
||||
MovePicker(const MovePicker&) = delete;
|
||||
MovePicker& operator=(const MovePicker&) = delete;
|
||||
MovePicker(const Position&, Move, Depth, const ButterflyHistory*,
|
||||
const CapturePieceToHistory*,
|
||||
const PieceToHistory**,
|
||||
Move,
|
||||
const Move*);
|
||||
MovePicker(const Position&, Move, Value, const CapturePieceToHistory*);
|
||||
MovePicker(const Position&, Move, Depth, const ButterflyHistory*,
|
||||
const CapturePieceToHistory*,
|
||||
const PieceToHistory**,
|
||||
Square);
|
||||
MovePicker(const Position&, Move, Value, Depth, const CapturePieceToHistory*);
|
||||
MovePicker(const Position&, Move, Depth, const ButterflyHistory*,
|
||||
const LowPlyHistory*,
|
||||
const CapturePieceToHistory*,
|
||||
const PieceToHistory**,
|
||||
Move,
|
||||
const Move*,
|
||||
int);
|
||||
Move next_move(bool skipQuiets = false);
|
||||
|
||||
private:
|
||||
@@ -137,6 +143,7 @@ private:
|
||||
|
||||
const Position& pos;
|
||||
const ButterflyHistory* mainHistory;
|
||||
const LowPlyHistory* lowPlyHistory;
|
||||
const CapturePieceToHistory* captureHistory;
|
||||
const PieceToHistory** continuationHistory;
|
||||
Move ttMove;
|
||||
@@ -145,9 +152,8 @@ private:
|
||||
Square recaptureSquare;
|
||||
Value threshold;
|
||||
Depth depth;
|
||||
int ply;
|
||||
ExtMove moves[MAX_MOVES];
|
||||
};
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef MOVEPICK_H_INCLUDED
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Definition of input features and network structure used in NNUE evaluation function
|
||||
|
||||
#ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED
|
||||
#define NNUE_HALFKP_256X2_32_32_H_INCLUDED
|
||||
|
||||
#include "../features/feature_set.h"
|
||||
#include "../features/half_kp.h"
|
||||
|
||||
#include "../layers/input_slice.h"
|
||||
#include "../layers/affine_transform.h"
|
||||
#include "../layers/clipped_relu.h"
|
||||
|
||||
namespace Eval::NNUE {
|
||||
|
||||
// Input features used in evaluation function
|
||||
using RawFeatures = Features::FeatureSet<
|
||||
Features::HalfKP<Features::Side::kFriend>>;
|
||||
|
||||
// Number of input feature dimensions after conversion
|
||||
constexpr IndexType kTransformedFeatureDimensions = 256;
|
||||
|
||||
namespace Layers {
|
||||
|
||||
// Define network structure
|
||||
using InputLayer = InputSlice<kTransformedFeatureDimensions * 2>;
|
||||
using HiddenLayer1 = ClippedReLU<AffineTransform<InputLayer, 32>>;
|
||||
using HiddenLayer2 = ClippedReLU<AffineTransform<HiddenLayer1, 32>>;
|
||||
using OutputLayer = AffineTransform<HiddenLayer2, 1>;
|
||||
|
||||
} // namespace Layers
|
||||
|
||||
using Network = Layers::OutputLayer;
|
||||
|
||||
} // namespace Eval::NNUE
|
||||
|
||||
#endif // #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED
|
||||
+99
-324
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -18,386 +18,161 @@
|
||||
|
||||
// Code for calculating NNUE evaluation function
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
|
||||
#include "../evaluate.h"
|
||||
#include "../position.h"
|
||||
#include "../misc.h"
|
||||
#include "../uci.h"
|
||||
#include "../types.h"
|
||||
|
||||
#include "evaluate_nnue.h"
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
ExtPieceSquare kpp_board_index[PIECE_NB] = {
|
||||
// convention: W - us, B - them
|
||||
// viewed from other side, W and B are reversed
|
||||
{ PS_NONE, PS_NONE },
|
||||
{ PS_W_PAWN, PS_B_PAWN },
|
||||
{ PS_W_KNIGHT, PS_B_KNIGHT },
|
||||
{ PS_W_BISHOP, PS_B_BISHOP },
|
||||
{ PS_W_ROOK, PS_B_ROOK },
|
||||
{ PS_W_QUEEN, PS_B_QUEEN },
|
||||
{ PS_W_KING, PS_B_KING },
|
||||
{ PS_NONE, PS_NONE },
|
||||
{ PS_NONE, PS_NONE },
|
||||
{ PS_B_PAWN, PS_W_PAWN },
|
||||
{ PS_B_KNIGHT, PS_W_KNIGHT },
|
||||
{ PS_B_BISHOP, PS_W_BISHOP },
|
||||
{ PS_B_ROOK, PS_W_ROOK },
|
||||
{ PS_B_QUEEN, PS_W_QUEEN },
|
||||
{ PS_B_KING, PS_W_KING },
|
||||
{ PS_NONE, PS_NONE }
|
||||
};
|
||||
|
||||
|
||||
namespace Eval::NNUE {
|
||||
|
||||
// Input feature converter
|
||||
LargePagePtr<FeatureTransformer> featureTransformer;
|
||||
AlignedPtr<FeatureTransformer> feature_transformer;
|
||||
|
||||
// Evaluation function
|
||||
AlignedPtr<Network> network[LayerStacks];
|
||||
AlignedPtr<Network> network;
|
||||
|
||||
// Evaluation function file name
|
||||
std::string fileName;
|
||||
std::string netDescription;
|
||||
|
||||
namespace Detail {
|
||||
|
||||
// Initialize the evaluation function parameters
|
||||
template <typename T>
|
||||
void initialize(AlignedPtr<T>& pointer) {
|
||||
void Initialize(AlignedPtr<T>& pointer) {
|
||||
|
||||
pointer.reset(reinterpret_cast<T*>(std_aligned_alloc(alignof(T), sizeof(T))));
|
||||
std::memset(pointer.get(), 0, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void initialize(LargePagePtr<T>& pointer) {
|
||||
|
||||
static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T");
|
||||
pointer.reset(reinterpret_cast<T*>(aligned_large_pages_alloc(sizeof(T))));
|
||||
std::memset(pointer.get(), 0, sizeof(T));
|
||||
}
|
||||
|
||||
// Read evaluation function parameters
|
||||
template <typename T>
|
||||
bool read_parameters(std::istream& stream, T& reference) {
|
||||
bool ReadParameters(std::istream& stream, const AlignedPtr<T>& pointer) {
|
||||
|
||||
std::uint32_t header;
|
||||
header = read_little_endian<std::uint32_t>(stream);
|
||||
if (!stream || header != T::get_hash_value()) return false;
|
||||
return reference.read_parameters(stream);
|
||||
}
|
||||
|
||||
// Write evaluation function parameters
|
||||
template <typename T>
|
||||
bool write_parameters(std::ostream& stream, const T& reference) {
|
||||
|
||||
write_little_endian<std::uint32_t>(stream, T::get_hash_value());
|
||||
return reference.write_parameters(stream);
|
||||
stream.read(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
if (!stream || header != T::GetHashValue()) return false;
|
||||
return pointer->ReadParameters(stream);
|
||||
}
|
||||
|
||||
} // namespace Detail
|
||||
|
||||
// Initialize the evaluation function parameters
|
||||
void initialize() {
|
||||
void Initialize() {
|
||||
|
||||
Detail::initialize(featureTransformer);
|
||||
for (std::size_t i = 0; i < LayerStacks; ++i)
|
||||
Detail::initialize(network[i]);
|
||||
Detail::Initialize(feature_transformer);
|
||||
Detail::Initialize(network);
|
||||
}
|
||||
|
||||
// Read network header
|
||||
bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* desc)
|
||||
{
|
||||
bool ReadHeader(std::istream& stream,
|
||||
std::uint32_t* hash_value, std::string* architecture) {
|
||||
|
||||
std::uint32_t version, size;
|
||||
|
||||
version = read_little_endian<std::uint32_t>(stream);
|
||||
*hashValue = read_little_endian<std::uint32_t>(stream);
|
||||
size = read_little_endian<std::uint32_t>(stream);
|
||||
if (!stream || version != Version) return false;
|
||||
desc->resize(size);
|
||||
stream.read(&(*desc)[0], size);
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
// Write network header
|
||||
bool write_header(std::ostream& stream, std::uint32_t hashValue, const std::string& desc)
|
||||
{
|
||||
write_little_endian<std::uint32_t>(stream, Version);
|
||||
write_little_endian<std::uint32_t>(stream, hashValue);
|
||||
write_little_endian<std::uint32_t>(stream, (std::uint32_t)desc.size());
|
||||
stream.write(&desc[0], desc.size());
|
||||
stream.read(reinterpret_cast<char*>(&version), sizeof(version));
|
||||
stream.read(reinterpret_cast<char*>(hash_value), sizeof(*hash_value));
|
||||
stream.read(reinterpret_cast<char*>(&size), sizeof(size));
|
||||
if (!stream || version != kVersion) return false;
|
||||
architecture->resize(size);
|
||||
stream.read(&(*architecture)[0], size);
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool read_parameters(std::istream& stream) {
|
||||
bool ReadParameters(std::istream& stream) {
|
||||
|
||||
std::uint32_t hashValue;
|
||||
if (!read_header(stream, &hashValue, &netDescription)) return false;
|
||||
if (hashValue != HashValue) return false;
|
||||
if (!Detail::read_parameters(stream, *featureTransformer)) return false;
|
||||
for (std::size_t i = 0; i < LayerStacks; ++i)
|
||||
if (!Detail::read_parameters(stream, *(network[i]))) return false;
|
||||
std::uint32_t hash_value;
|
||||
std::string architecture;
|
||||
if (!ReadHeader(stream, &hash_value, &architecture)) return false;
|
||||
if (hash_value != kHashValue) return false;
|
||||
if (!Detail::ReadParameters(stream, feature_transformer)) return false;
|
||||
if (!Detail::ReadParameters(stream, network)) return false;
|
||||
return stream && stream.peek() == std::ios::traits_type::eof();
|
||||
}
|
||||
|
||||
// Write network parameters
|
||||
bool write_parameters(std::ostream& stream) {
|
||||
// Proceed with the difference calculation if possible
|
||||
static void UpdateAccumulatorIfPossible(const Position& pos) {
|
||||
|
||||
if (!write_header(stream, HashValue, netDescription)) return false;
|
||||
if (!Detail::write_parameters(stream, *featureTransformer)) return false;
|
||||
for (std::size_t i = 0; i < LayerStacks; ++i)
|
||||
if (!Detail::write_parameters(stream, *(network[i]))) return false;
|
||||
return (bool)stream;
|
||||
feature_transformer->UpdateAccumulatorIfPossible(pos);
|
||||
}
|
||||
|
||||
// Calculate the evaluation value
|
||||
static Value ComputeScore(const Position& pos, bool refresh) {
|
||||
|
||||
auto& accumulator = pos.state()->accumulator;
|
||||
if (!refresh && accumulator.computed_score) {
|
||||
return accumulator.score;
|
||||
}
|
||||
|
||||
alignas(kCacheLineSize) TransformedFeatureType
|
||||
transformed_features[FeatureTransformer::kBufferSize];
|
||||
feature_transformer->Transform(pos, transformed_features, refresh);
|
||||
alignas(kCacheLineSize) char buffer[Network::kBufferSize];
|
||||
const auto output = network->Propagate(transformed_features, buffer);
|
||||
|
||||
auto score = static_cast<Value>(output[0] / FV_SCALE);
|
||||
|
||||
accumulator.score = score;
|
||||
accumulator.computed_score = true;
|
||||
return accumulator.score;
|
||||
}
|
||||
|
||||
// Load the evaluation function file
|
||||
bool load_eval_file(const std::string& evalFile) {
|
||||
|
||||
Initialize();
|
||||
fileName = evalFile;
|
||||
|
||||
std::ifstream stream(evalFile, std::ios::binary);
|
||||
|
||||
const bool result = ReadParameters(stream);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Evaluation function. Perform differential calculation.
|
||||
Value evaluate(const Position& pos, bool adjusted) {
|
||||
Value evaluate(const Position& pos) {
|
||||
Value v = ComputeScore(pos, false);
|
||||
v = Utility::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
|
||||
|
||||
// We manually align the arrays on the stack because with gcc < 9.3
|
||||
// overaligning stack variables with alignas() doesn't work correctly.
|
||||
|
||||
constexpr uint64_t alignment = CacheLineSize;
|
||||
int delta = 10 - pos.non_pawn_material() / 1515;
|
||||
|
||||
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
|
||||
TransformedFeatureType transformedFeaturesUnaligned[
|
||||
FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)];
|
||||
|
||||
auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
|
||||
#else
|
||||
alignas(alignment)
|
||||
TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize];
|
||||
#endif
|
||||
|
||||
ASSERT_ALIGNED(transformedFeatures, alignment);
|
||||
|
||||
const int bucket = (pos.count<ALL_PIECES>() - 1) / 4;
|
||||
const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket);
|
||||
const auto positional = network[bucket]->propagate(transformedFeatures);
|
||||
|
||||
// Give more value to positional evaluation when adjusted flag is set
|
||||
if (adjusted)
|
||||
return static_cast<Value>(((128 - delta) * psqt + (128 + delta) * positional) / 128 / OutputScale);
|
||||
else
|
||||
return static_cast<Value>((psqt + positional) / OutputScale);
|
||||
return v;
|
||||
}
|
||||
|
||||
struct NnueEvalTrace {
|
||||
static_assert(LayerStacks == PSQTBuckets);
|
||||
|
||||
Value psqt[LayerStacks];
|
||||
Value positional[LayerStacks];
|
||||
std::size_t correctBucket;
|
||||
};
|
||||
|
||||
static NnueEvalTrace trace_evaluate(const Position& pos) {
|
||||
|
||||
// We manually align the arrays on the stack because with gcc < 9.3
|
||||
// overaligning stack variables with alignas() doesn't work correctly.
|
||||
|
||||
constexpr uint64_t alignment = CacheLineSize;
|
||||
|
||||
#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN)
|
||||
TransformedFeatureType transformedFeaturesUnaligned[
|
||||
FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)];
|
||||
|
||||
auto* transformedFeatures = align_ptr_up<alignment>(&transformedFeaturesUnaligned[0]);
|
||||
#else
|
||||
alignas(alignment)
|
||||
TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize];
|
||||
#endif
|
||||
|
||||
ASSERT_ALIGNED(transformedFeatures, alignment);
|
||||
|
||||
NnueEvalTrace t{};
|
||||
t.correctBucket = (pos.count<ALL_PIECES>() - 1) / 4;
|
||||
for (IndexType bucket = 0; bucket < LayerStacks; ++bucket) {
|
||||
const auto materialist = featureTransformer->transform(pos, transformedFeatures, bucket);
|
||||
const auto positional = network[bucket]->propagate(transformedFeatures);
|
||||
|
||||
t.psqt[bucket] = static_cast<Value>( materialist / OutputScale );
|
||||
t.positional[bucket] = static_cast<Value>( positional / OutputScale );
|
||||
}
|
||||
|
||||
return t;
|
||||
// Evaluation function. Perform full calculation.
|
||||
Value compute_eval(const Position& pos) {
|
||||
return ComputeScore(pos, true);
|
||||
}
|
||||
|
||||
static const std::string PieceToChar(" PNBRQK pnbrqk");
|
||||
|
||||
|
||||
// format_cp_compact() converts a Value into (centi)pawns and writes it in a buffer.
|
||||
// The buffer must have capacity for at least 5 chars.
|
||||
static void format_cp_compact(Value v, char* buffer) {
|
||||
|
||||
buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' ');
|
||||
|
||||
int cp = std::abs(100 * v / PawnValueEg);
|
||||
if (cp >= 10000)
|
||||
{
|
||||
buffer[1] = '0' + cp / 10000; cp %= 10000;
|
||||
buffer[2] = '0' + cp / 1000; cp %= 1000;
|
||||
buffer[3] = '0' + cp / 100;
|
||||
buffer[4] = ' ';
|
||||
}
|
||||
else if (cp >= 1000)
|
||||
{
|
||||
buffer[1] = '0' + cp / 1000; cp %= 1000;
|
||||
buffer[2] = '0' + cp / 100; cp %= 100;
|
||||
buffer[3] = '.';
|
||||
buffer[4] = '0' + cp / 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[1] = '0' + cp / 100; cp %= 100;
|
||||
buffer[2] = '.';
|
||||
buffer[3] = '0' + cp / 10; cp %= 10;
|
||||
buffer[4] = '0' + cp / 1;
|
||||
}
|
||||
// Proceed with the difference calculation if possible
|
||||
void update_eval(const Position& pos) {
|
||||
UpdateAccumulatorIfPossible(pos);
|
||||
}
|
||||
|
||||
|
||||
// format_cp_aligned_dot() converts a Value into (centi)pawns and writes it in a buffer,
|
||||
// always keeping two decimals. The buffer must have capacity for at least 7 chars.
|
||||
static void format_cp_aligned_dot(Value v, char* buffer) {
|
||||
|
||||
buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' ');
|
||||
|
||||
double cp = 1.0 * std::abs(int(v)) / PawnValueEg;
|
||||
sprintf(&buffer[1], "%6.2f", cp);
|
||||
}
|
||||
|
||||
|
||||
// trace() returns a string with the value of each piece on a board,
|
||||
// and a table for (PSQT, Layers) values bucket by bucket.
|
||||
|
||||
std::string trace(Position& pos) {
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
char board[3*8+1][8*8+2];
|
||||
std::memset(board, ' ', sizeof(board));
|
||||
for (int row = 0; row < 3*8+1; ++row)
|
||||
board[row][8*8+1] = '\0';
|
||||
|
||||
// A lambda to output one box of the board
|
||||
auto writeSquare = [&board](File file, Rank rank, Piece pc, Value value) {
|
||||
|
||||
const int x = ((int)file) * 8;
|
||||
const int y = (7 - (int)rank) * 3;
|
||||
for (int i = 1; i < 8; ++i)
|
||||
board[y][x+i] = board[y+3][x+i] = '-';
|
||||
for (int i = 1; i < 3; ++i)
|
||||
board[y+i][x] = board[y+i][x+8] = '|';
|
||||
board[y][x] = board[y][x+8] = board[y+3][x+8] = board[y+3][x] = '+';
|
||||
if (pc != NO_PIECE)
|
||||
board[y+1][x+4] = PieceToChar[pc];
|
||||
if (value != VALUE_NONE)
|
||||
format_cp_compact(value, &board[y+2][x+2]);
|
||||
};
|
||||
|
||||
// We estimate the value of each piece by doing a differential evaluation from
|
||||
// the current base eval, simulating the removal of the piece from its square.
|
||||
Value base = evaluate(pos);
|
||||
base = pos.side_to_move() == WHITE ? base : -base;
|
||||
|
||||
for (File f = FILE_A; f <= FILE_H; ++f)
|
||||
for (Rank r = RANK_1; r <= RANK_8; ++r)
|
||||
{
|
||||
Square sq = make_square(f, r);
|
||||
Piece pc = pos.piece_on(sq);
|
||||
Value v = VALUE_NONE;
|
||||
|
||||
if (pc != NO_PIECE && type_of(pc) != KING)
|
||||
{
|
||||
auto st = pos.state();
|
||||
|
||||
pos.remove_piece(sq);
|
||||
st->accumulator.computed[WHITE] = false;
|
||||
st->accumulator.computed[BLACK] = false;
|
||||
|
||||
Value eval = evaluate(pos);
|
||||
eval = pos.side_to_move() == WHITE ? eval : -eval;
|
||||
v = base - eval;
|
||||
|
||||
pos.put_piece(pc, sq);
|
||||
st->accumulator.computed[WHITE] = false;
|
||||
st->accumulator.computed[BLACK] = false;
|
||||
}
|
||||
|
||||
writeSquare(f, r, pc, v);
|
||||
}
|
||||
|
||||
ss << " NNUE derived piece values:\n";
|
||||
for (int row = 0; row < 3*8+1; ++row)
|
||||
ss << board[row] << '\n';
|
||||
ss << '\n';
|
||||
|
||||
auto t = trace_evaluate(pos);
|
||||
|
||||
ss << " NNUE network contributions "
|
||||
<< (pos.side_to_move() == WHITE ? "(White to move)" : "(Black to move)") << std::endl
|
||||
<< "+------------+------------+------------+------------+\n"
|
||||
<< "| Bucket | Material | Positional | Total |\n"
|
||||
<< "| | (PSQT) | (Layers) | |\n"
|
||||
<< "+------------+------------+------------+------------+\n";
|
||||
|
||||
for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket)
|
||||
{
|
||||
char buffer[3][8];
|
||||
std::memset(buffer, '\0', sizeof(buffer));
|
||||
|
||||
format_cp_aligned_dot(t.psqt[bucket], buffer[0]);
|
||||
format_cp_aligned_dot(t.positional[bucket], buffer[1]);
|
||||
format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], buffer[2]);
|
||||
|
||||
ss << "| " << bucket << " "
|
||||
<< " | " << buffer[0] << " "
|
||||
<< " | " << buffer[1] << " "
|
||||
<< " | " << buffer[2] << " "
|
||||
<< " |";
|
||||
if (bucket == t.correctBucket)
|
||||
ss << " <-- this bucket is used";
|
||||
ss << '\n';
|
||||
}
|
||||
|
||||
ss << "+------------+------------+------------+------------+\n";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
// Load eval, from a file stream or a memory stream
|
||||
bool load_eval(std::string name, std::istream& stream) {
|
||||
|
||||
initialize();
|
||||
fileName = name;
|
||||
return read_parameters(stream);
|
||||
}
|
||||
|
||||
// Save eval, to a file stream or a memory stream
|
||||
bool save_eval(std::ostream& stream) {
|
||||
|
||||
if (fileName.empty())
|
||||
return false;
|
||||
|
||||
return write_parameters(stream);
|
||||
}
|
||||
|
||||
/// Save eval, to a file given by its name
|
||||
bool save_eval(const std::optional<std::string>& filename) {
|
||||
|
||||
std::string actualFilename;
|
||||
std::string msg;
|
||||
|
||||
if (filename.has_value())
|
||||
actualFilename = filename.value();
|
||||
else
|
||||
{
|
||||
if (currentEvalFileName != EvalFileDefaultName)
|
||||
{
|
||||
msg = "Failed to export a net. A non-embedded net can only be saved if the filename is specified";
|
||||
|
||||
sync_cout << msg << sync_endl;
|
||||
return false;
|
||||
}
|
||||
actualFilename = EvalFileDefaultName;
|
||||
}
|
||||
|
||||
std::ofstream stream(actualFilename, std::ios_base::binary);
|
||||
bool saved = save_eval(stream);
|
||||
|
||||
msg = saved ? "Network saved successfully to " + actualFilename
|
||||
: "Failed to export a net";
|
||||
|
||||
sync_cout << msg << sync_endl;
|
||||
return saved;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
} // namespace Eval::NNUE
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -25,11 +25,11 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
namespace Eval::NNUE {
|
||||
|
||||
// Hash value of evaluation function structure
|
||||
constexpr std::uint32_t HashValue =
|
||||
FeatureTransformer::get_hash_value() ^ Network::get_hash_value();
|
||||
constexpr std::uint32_t kHashValue =
|
||||
FeatureTransformer::GetHashValue() ^ Network::GetHashValue();
|
||||
|
||||
// Deleter for automating release of memory area
|
||||
template <typename T>
|
||||
@@ -40,20 +40,9 @@ namespace Stockfish::Eval::NNUE {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct LargePageDeleter {
|
||||
void operator()(T* ptr) const {
|
||||
ptr->~T();
|
||||
aligned_large_pages_free(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using AlignedPtr = std::unique_ptr<T, AlignedDeleter<T>>;
|
||||
|
||||
template <typename T>
|
||||
using LargePagePtr = std::unique_ptr<T, LargePageDeleter<T>>;
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
} // namespace Eval::NNUE
|
||||
|
||||
#endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// A class template that represents the input feature set of the NNUE evaluation function
|
||||
|
||||
#ifndef NNUE_FEATURE_SET_H_INCLUDED
|
||||
#define NNUE_FEATURE_SET_H_INCLUDED
|
||||
|
||||
#include "features_common.h"
|
||||
#include <array>
|
||||
|
||||
namespace Eval::NNUE::Features {
|
||||
|
||||
// Class template that represents a list of values
|
||||
template <typename T, T... Values>
|
||||
struct CompileTimeList;
|
||||
|
||||
template <typename T, T First, T... Remaining>
|
||||
struct CompileTimeList<T, First, Remaining...> {
|
||||
static constexpr bool Contains(T value) {
|
||||
return value == First || CompileTimeList<T, Remaining...>::Contains(value);
|
||||
}
|
||||
static constexpr std::array<T, sizeof...(Remaining) + 1>
|
||||
kValues = {{First, Remaining...}};
|
||||
};
|
||||
|
||||
// Base class of feature set
|
||||
template <typename Derived>
|
||||
class FeatureSetBase {
|
||||
|
||||
public:
|
||||
// Get a list of indices for active features
|
||||
template <typename IndexListType>
|
||||
static void AppendActiveIndices(
|
||||
const Position& pos, TriggerEvent trigger, IndexListType active[2]) {
|
||||
|
||||
for (Color perspective : { WHITE, BLACK }) {
|
||||
Derived::CollectActiveIndices(
|
||||
pos, trigger, perspective, &active[perspective]);
|
||||
}
|
||||
}
|
||||
|
||||
// Get a list of indices for recently changed features
|
||||
template <typename PositionType, typename IndexListType>
|
||||
static void AppendChangedIndices(
|
||||
const PositionType& pos, TriggerEvent trigger,
|
||||
IndexListType removed[2], IndexListType added[2], bool reset[2]) {
|
||||
|
||||
const auto& dp = pos.state()->dirtyPiece;
|
||||
if (dp.dirty_num == 0) return;
|
||||
|
||||
for (Color perspective : { WHITE, BLACK }) {
|
||||
reset[perspective] = false;
|
||||
switch (trigger) {
|
||||
case TriggerEvent::kFriendKingMoved:
|
||||
reset[perspective] =
|
||||
dp.pieceId[0] == PIECE_ID_KING + perspective;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
if (reset[perspective]) {
|
||||
Derived::CollectActiveIndices(
|
||||
pos, trigger, perspective, &added[perspective]);
|
||||
} else {
|
||||
Derived::CollectChangedIndices(
|
||||
pos, trigger, perspective,
|
||||
&removed[perspective], &added[perspective]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Class template that represents the feature set
|
||||
template <typename FeatureType>
|
||||
class FeatureSet<FeatureType> : public FeatureSetBase<FeatureSet<FeatureType>> {
|
||||
|
||||
public:
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t kHashValue = FeatureType::kHashValue;
|
||||
// Number of feature dimensions
|
||||
static constexpr IndexType kDimensions = FeatureType::kDimensions;
|
||||
// Maximum number of simultaneously active features
|
||||
static constexpr IndexType kMaxActiveDimensions =
|
||||
FeatureType::kMaxActiveDimensions;
|
||||
// Trigger for full calculation instead of difference calculation
|
||||
using SortedTriggerSet =
|
||||
CompileTimeList<TriggerEvent, FeatureType::kRefreshTrigger>;
|
||||
static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues;
|
||||
|
||||
private:
|
||||
// Get a list of indices for active features
|
||||
static void CollectActiveIndices(
|
||||
const Position& pos, const TriggerEvent trigger, const Color perspective,
|
||||
IndexList* const active) {
|
||||
if (FeatureType::kRefreshTrigger == trigger) {
|
||||
FeatureType::AppendActiveIndices(pos, perspective, active);
|
||||
}
|
||||
}
|
||||
|
||||
// Get a list of indices for recently changed features
|
||||
static void CollectChangedIndices(
|
||||
const Position& pos, const TriggerEvent trigger, const Color perspective,
|
||||
IndexList* const removed, IndexList* const added) {
|
||||
|
||||
if (FeatureType::kRefreshTrigger == trigger) {
|
||||
FeatureType::AppendChangedIndices(pos, perspective, removed, added);
|
||||
}
|
||||
}
|
||||
|
||||
// Make the base class and the class template that recursively uses itself a friend
|
||||
friend class FeatureSetBase<FeatureSet>;
|
||||
template <typename... FeatureTypes>
|
||||
friend class FeatureSet;
|
||||
};
|
||||
|
||||
} // namespace Eval::NNUE::Features
|
||||
|
||||
#endif // #ifndef NNUE_FEATURE_SET_H_INCLUDED
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,23 +16,30 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//Common header of input features of NNUE evaluation function
|
||||
|
||||
#ifndef PSQT_H_INCLUDED
|
||||
#define PSQT_H_INCLUDED
|
||||
#ifndef NNUE_FEATURES_COMMON_H_INCLUDED
|
||||
#define NNUE_FEATURES_COMMON_H_INCLUDED
|
||||
|
||||
#include "../../evaluate.h"
|
||||
#include "../nnue_common.h"
|
||||
|
||||
#include "types.h"
|
||||
namespace Eval::NNUE::Features {
|
||||
|
||||
class IndexList;
|
||||
|
||||
namespace Stockfish::PSQT
|
||||
{
|
||||
template <typename... FeatureTypes>
|
||||
class FeatureSet;
|
||||
|
||||
extern Score psq[PIECE_NB][SQUARE_NB];
|
||||
// Trigger to perform full calculations instead of difference only
|
||||
enum class TriggerEvent {
|
||||
kFriendKingMoved // calculate full evaluation when own king moves
|
||||
};
|
||||
|
||||
// Fill psqt array from a set of internally linked parameters
|
||||
extern void init();
|
||||
enum class Side {
|
||||
kFriend // side to move
|
||||
};
|
||||
|
||||
} // namespace Stockfish::PSQT
|
||||
} // namespace Eval::NNUE::Features
|
||||
|
||||
|
||||
#endif // PSQT_H_INCLUDED
|
||||
#endif // #ifndef NNUE_FEATURES_COMMON_H_INCLUDED
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//Definition of input features HalfKAv2_hm of NNUE evaluation function
|
||||
|
||||
#include "half_ka_v2_hm.h"
|
||||
|
||||
#include "../../position.h"
|
||||
|
||||
namespace Stockfish::Eval::NNUE::Features {
|
||||
|
||||
// Orient a square according to perspective (rotates by 180 for black)
|
||||
inline Square HalfKAv2_hm::orient(Color perspective, Square s, Square ksq) {
|
||||
return Square(int(s) ^ (bool(perspective) * SQ_A8) ^ ((file_of(ksq) < FILE_E) * SQ_H1));
|
||||
}
|
||||
|
||||
// Index of a feature for a given king position and another piece on some square
|
||||
inline IndexType HalfKAv2_hm::make_index(Color perspective, Square s, Piece pc, Square ksq) {
|
||||
Square o_ksq = orient(perspective, ksq, ksq);
|
||||
return IndexType(orient(perspective, s, ksq) + PieceSquareIndex[perspective][pc] + PS_NB * KingBuckets[o_ksq]);
|
||||
}
|
||||
|
||||
// Get a list of indices for active features
|
||||
void HalfKAv2_hm::append_active_indices(
|
||||
const Position& pos,
|
||||
Color perspective,
|
||||
IndexList& active
|
||||
) {
|
||||
Square ksq = pos.square<KING>(perspective);
|
||||
Bitboard bb = pos.pieces();
|
||||
while (bb)
|
||||
{
|
||||
Square s = pop_lsb(bb);
|
||||
active.push_back(make_index(perspective, s, pos.piece_on(s), ksq));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// append_changed_indices() : get a list of indices for recently changed features
|
||||
|
||||
void HalfKAv2_hm::append_changed_indices(
|
||||
Square ksq,
|
||||
const DirtyPiece& dp,
|
||||
Color perspective,
|
||||
IndexList& removed,
|
||||
IndexList& added
|
||||
) {
|
||||
for (int i = 0; i < dp.dirty_num; ++i) {
|
||||
if (dp.from[i] != SQ_NONE)
|
||||
removed.push_back(make_index(perspective, dp.from[i], dp.piece[i], ksq));
|
||||
if (dp.to[i] != SQ_NONE)
|
||||
added.push_back(make_index(perspective, dp.to[i], dp.piece[i], ksq));
|
||||
}
|
||||
}
|
||||
|
||||
int HalfKAv2_hm::update_cost(const StateInfo* st) {
|
||||
return st->dirtyPiece.dirty_num;
|
||||
}
|
||||
|
||||
int HalfKAv2_hm::refresh_cost(const Position& pos) {
|
||||
return pos.count<ALL_PIECES>();
|
||||
}
|
||||
|
||||
bool HalfKAv2_hm::requires_refresh(const StateInfo* st, Color perspective) {
|
||||
return st->dirtyPiece.piece[0] == make_piece(perspective, KING);
|
||||
}
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE::Features
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//Definition of input features HalfKP of NNUE evaluation function
|
||||
|
||||
#ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED
|
||||
#define NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED
|
||||
|
||||
#include "../nnue_common.h"
|
||||
|
||||
#include "../../evaluate.h"
|
||||
#include "../../misc.h"
|
||||
|
||||
namespace Stockfish {
|
||||
struct StateInfo;
|
||||
}
|
||||
|
||||
namespace Stockfish::Eval::NNUE::Features {
|
||||
|
||||
// Feature HalfKAv2_hm: Combination of the position of own king
|
||||
// and the position of pieces. Position mirrored such that king always on e..h files.
|
||||
class HalfKAv2_hm {
|
||||
|
||||
// unique number for each piece type on each square
|
||||
enum {
|
||||
PS_NONE = 0,
|
||||
PS_W_PAWN = 0,
|
||||
PS_B_PAWN = 1 * SQUARE_NB,
|
||||
PS_W_KNIGHT = 2 * SQUARE_NB,
|
||||
PS_B_KNIGHT = 3 * SQUARE_NB,
|
||||
PS_W_BISHOP = 4 * SQUARE_NB,
|
||||
PS_B_BISHOP = 5 * SQUARE_NB,
|
||||
PS_W_ROOK = 6 * SQUARE_NB,
|
||||
PS_B_ROOK = 7 * SQUARE_NB,
|
||||
PS_W_QUEEN = 8 * SQUARE_NB,
|
||||
PS_B_QUEEN = 9 * SQUARE_NB,
|
||||
PS_KING = 10 * SQUARE_NB,
|
||||
PS_NB = 11 * SQUARE_NB
|
||||
};
|
||||
|
||||
static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = {
|
||||
// convention: W - us, B - them
|
||||
// viewed from other side, W and B are reversed
|
||||
{ PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE,
|
||||
PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE },
|
||||
{ PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE,
|
||||
PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE }
|
||||
};
|
||||
|
||||
// Orient a square according to perspective (rotates by 180 for black)
|
||||
static Square orient(Color perspective, Square s, Square ksq);
|
||||
|
||||
// Index of a feature for a given king position and another piece on some square
|
||||
static IndexType make_index(Color perspective, Square s, Piece pc, Square ksq);
|
||||
|
||||
public:
|
||||
// Feature name
|
||||
static constexpr const char* Name = "HalfKAv2_hm(Friend)";
|
||||
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t HashValue = 0x7f234cb8u;
|
||||
|
||||
// Number of feature dimensions
|
||||
static constexpr IndexType Dimensions =
|
||||
static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_NB) / 2;
|
||||
|
||||
static constexpr int KingBuckets[64] = {
|
||||
-1, -1, -1, -1, 31, 30, 29, 28,
|
||||
-1, -1, -1, -1, 27, 26, 25, 24,
|
||||
-1, -1, -1, -1, 23, 22, 21, 20,
|
||||
-1, -1, -1, -1, 19, 18, 17, 16,
|
||||
-1, -1, -1, -1, 15, 14, 13, 12,
|
||||
-1, -1, -1, -1, 11, 10, 9, 8,
|
||||
-1, -1, -1, -1, 7, 6, 5, 4,
|
||||
-1, -1, -1, -1, 3, 2, 1, 0
|
||||
};
|
||||
|
||||
// Maximum number of simultaneously active features.
|
||||
static constexpr IndexType MaxActiveDimensions = 32;
|
||||
using IndexList = ValueList<IndexType, MaxActiveDimensions>;
|
||||
|
||||
// Get a list of indices for active features
|
||||
static void append_active_indices(
|
||||
const Position& pos,
|
||||
Color perspective,
|
||||
IndexList& active);
|
||||
|
||||
// Get a list of indices for recently changed features
|
||||
static void append_changed_indices(
|
||||
Square ksq,
|
||||
const DirtyPiece& dp,
|
||||
Color perspective,
|
||||
IndexList& removed,
|
||||
IndexList& added
|
||||
);
|
||||
|
||||
// Returns the cost of updating one perspective, the most costly one.
|
||||
// Assumes no refresh needed.
|
||||
static int update_cost(const StateInfo* st);
|
||||
static int refresh_cost(const Position& pos);
|
||||
|
||||
// Returns whether the change stored in this StateInfo means that
|
||||
// a full accumulator refresh is required.
|
||||
static bool requires_refresh(const StateInfo* st, Color perspective);
|
||||
};
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE::Features
|
||||
|
||||
#endif // #ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//Definition of input features HalfKP of NNUE evaluation function
|
||||
|
||||
#include "half_kp.h"
|
||||
#include "index_list.h"
|
||||
|
||||
namespace Eval::NNUE::Features {
|
||||
|
||||
// Find the index of the feature quantity from the king position and PieceSquare
|
||||
template <Side AssociatedKing>
|
||||
inline IndexType HalfKP<AssociatedKing>::MakeIndex(Square sq_k, PieceSquare p) {
|
||||
return static_cast<IndexType>(PS_END) * static_cast<IndexType>(sq_k) + p;
|
||||
}
|
||||
|
||||
// Get pieces information
|
||||
template <Side AssociatedKing>
|
||||
inline void HalfKP<AssociatedKing>::GetPieces(
|
||||
const Position& pos, Color perspective,
|
||||
PieceSquare** pieces, Square* sq_target_k) {
|
||||
|
||||
*pieces = (perspective == BLACK) ?
|
||||
pos.eval_list()->piece_list_fb() :
|
||||
pos.eval_list()->piece_list_fw();
|
||||
const PieceId target = (AssociatedKing == Side::kFriend) ?
|
||||
static_cast<PieceId>(PIECE_ID_KING + perspective) :
|
||||
static_cast<PieceId>(PIECE_ID_KING + ~perspective);
|
||||
*sq_target_k = static_cast<Square>(((*pieces)[target] - PS_W_KING) % SQUARE_NB);
|
||||
}
|
||||
|
||||
// Get a list of indices for active features
|
||||
template <Side AssociatedKing>
|
||||
void HalfKP<AssociatedKing>::AppendActiveIndices(
|
||||
const Position& pos, Color perspective, IndexList* active) {
|
||||
|
||||
// Do nothing if array size is small to avoid compiler warning
|
||||
if (RawFeatures::kMaxActiveDimensions < kMaxActiveDimensions) return;
|
||||
|
||||
PieceSquare* pieces;
|
||||
Square sq_target_k;
|
||||
GetPieces(pos, perspective, &pieces, &sq_target_k);
|
||||
for (PieceId i = PIECE_ID_ZERO; i < PIECE_ID_KING; ++i) {
|
||||
if (pieces[i] != PS_NONE) {
|
||||
active->push_back(MakeIndex(sq_target_k, pieces[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get a list of indices for recently changed features
|
||||
template <Side AssociatedKing>
|
||||
void HalfKP<AssociatedKing>::AppendChangedIndices(
|
||||
const Position& pos, Color perspective,
|
||||
IndexList* removed, IndexList* added) {
|
||||
|
||||
PieceSquare* pieces;
|
||||
Square sq_target_k;
|
||||
GetPieces(pos, perspective, &pieces, &sq_target_k);
|
||||
const auto& dp = pos.state()->dirtyPiece;
|
||||
for (int i = 0; i < dp.dirty_num; ++i) {
|
||||
if (dp.pieceId[i] >= PIECE_ID_KING) continue;
|
||||
const auto old_p = static_cast<PieceSquare>(
|
||||
dp.old_piece[i].from[perspective]);
|
||||
if (old_p != PS_NONE) {
|
||||
removed->push_back(MakeIndex(sq_target_k, old_p));
|
||||
}
|
||||
const auto new_p = static_cast<PieceSquare>(
|
||||
dp.new_piece[i].from[perspective]);
|
||||
if (new_p != PS_NONE) {
|
||||
added->push_back(MakeIndex(sq_target_k, new_p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template class HalfKP<Side::kFriend>;
|
||||
|
||||
} // namespace Eval::NNUE::Features
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//Definition of input features HalfKP of NNUE evaluation function
|
||||
|
||||
#ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED
|
||||
#define NNUE_FEATURES_HALF_KP_H_INCLUDED
|
||||
|
||||
#include "../../evaluate.h"
|
||||
#include "features_common.h"
|
||||
|
||||
namespace Eval::NNUE::Features {
|
||||
|
||||
// Feature HalfKP: Combination of the position of own king
|
||||
// and the position of pieces other than kings
|
||||
template <Side AssociatedKing>
|
||||
class HalfKP {
|
||||
|
||||
public:
|
||||
// Feature name
|
||||
static constexpr const char* kName = "HalfKP(Friend)";
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t kHashValue =
|
||||
0x5D69D5B9u ^ (AssociatedKing == Side::kFriend);
|
||||
// Number of feature dimensions
|
||||
static constexpr IndexType kDimensions =
|
||||
static_cast<IndexType>(SQUARE_NB) * static_cast<IndexType>(PS_END);
|
||||
// Maximum number of simultaneously active features
|
||||
static constexpr IndexType kMaxActiveDimensions = PIECE_ID_KING;
|
||||
// Trigger for full calculation instead of difference calculation
|
||||
static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved;
|
||||
|
||||
// Get a list of indices for active features
|
||||
static void AppendActiveIndices(const Position& pos, Color perspective,
|
||||
IndexList* active);
|
||||
|
||||
// Get a list of indices for recently changed features
|
||||
static void AppendChangedIndices(const Position& pos, Color perspective,
|
||||
IndexList* removed, IndexList* added);
|
||||
|
||||
// Index of a feature for a given king position and another piece on some square
|
||||
static IndexType MakeIndex(Square sq_k, PieceSquare p);
|
||||
|
||||
private:
|
||||
// Get pieces information
|
||||
static void GetPieces(const Position& pos, Color perspective,
|
||||
PieceSquare** pieces, Square* sq_target_k);
|
||||
};
|
||||
|
||||
} // namespace Eval::NNUE::Features
|
||||
|
||||
#endif // #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Definition of index list of input features
|
||||
|
||||
#ifndef NNUE_FEATURES_INDEX_LIST_H_INCLUDED
|
||||
#define NNUE_FEATURES_INDEX_LIST_H_INCLUDED
|
||||
|
||||
#include "../../position.h"
|
||||
#include "../nnue_architecture.h"
|
||||
|
||||
namespace Eval::NNUE::Features {
|
||||
|
||||
// Class template used for feature index list
|
||||
template <typename T, std::size_t MaxSize>
|
||||
class ValueList {
|
||||
|
||||
public:
|
||||
std::size_t size() const { return size_; }
|
||||
void resize(std::size_t size) { size_ = size; }
|
||||
void push_back(const T& value) { values_[size_++] = value; }
|
||||
T& operator[](std::size_t index) { return values_[index]; }
|
||||
T* begin() { return values_; }
|
||||
T* end() { return values_ + size_; }
|
||||
const T& operator[](std::size_t index) const { return values_[index]; }
|
||||
const T* begin() const { return values_; }
|
||||
const T* end() const { return values_ + size_; }
|
||||
|
||||
void swap(ValueList& other) {
|
||||
const std::size_t max_size = std::max(size_, other.size_);
|
||||
for (std::size_t i = 0; i < max_size; ++i) {
|
||||
std::swap(values_[i], other.values_[i]);
|
||||
}
|
||||
std::swap(size_, other.size_);
|
||||
}
|
||||
|
||||
private:
|
||||
T values_[MaxSize];
|
||||
std::size_t size_ = 0;
|
||||
};
|
||||
|
||||
//Type of feature index list
|
||||
class IndexList
|
||||
: public ValueList<IndexType, RawFeatures::kMaxActiveDimensions> {
|
||||
};
|
||||
|
||||
} // namespace Eval::NNUE::Features
|
||||
|
||||
#endif // NNUE_FEATURES_INDEX_LIST_H_INCLUDED
|
||||
+150
-474
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -22,341 +22,180 @@
|
||||
#define NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include "../nnue_common.h"
|
||||
#include "../../simd.h"
|
||||
|
||||
/*
|
||||
This file contains the definition for a fully connected layer (aka affine transform).
|
||||
Two approaches are employed, depending on the sizes of the transform.
|
||||
namespace Eval::NNUE::Layers {
|
||||
|
||||
Approach 1:
|
||||
- used when the PaddedInputDimensions >= 128
|
||||
- uses AVX512 if possible
|
||||
- processes inputs in batches of 2*InputSimdWidth
|
||||
- so in batches of 128 for AVX512
|
||||
- the weight blocks of size InputSimdWidth are transposed such that
|
||||
access is sequential
|
||||
- N columns of the weight matrix are processed a time, where N
|
||||
depends on the architecture (the amount of registers)
|
||||
- accumulate + hadd is used
|
||||
|
||||
Approach 2:
|
||||
- used when the PaddedInputDimensions < 128
|
||||
- does not use AVX512
|
||||
- expected use-case is for when PaddedInputDimensions == 32 and InputDimensions <= 32.
|
||||
- that's why AVX512 is hard to implement
|
||||
- expected use-case is small layers
|
||||
- not optimized as well as the approach 1
|
||||
- inputs are processed in chunks of 4, weights are respectively transposed
|
||||
- accumulation happens directly to int32s
|
||||
*/
|
||||
|
||||
namespace Stockfish::Eval::NNUE::Layers {
|
||||
|
||||
// Fallback implementation for older/other architectures.
|
||||
// Identical for both approaches. Requires the input to be padded to at least 16 values.
|
||||
#if !defined(USE_SSSE3)
|
||||
template <IndexType InputDimensions, IndexType PaddedInputDimensions, IndexType OutputDimensions>
|
||||
static void affine_transform_non_ssse3(std::int32_t* output, const std::int8_t* weights, const std::int32_t* biases, const std::uint8_t* input)
|
||||
{
|
||||
# if defined(USE_SSE2)
|
||||
// At least a multiple of 16, with SSE2.
|
||||
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 16) / 16;
|
||||
const __m128i Zeros = _mm_setzero_si128();
|
||||
const auto inputVector = reinterpret_cast<const __m128i*>(input);
|
||||
|
||||
# elif defined(USE_MMX)
|
||||
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 8) / 8;
|
||||
const __m64 Zeros = _mm_setzero_si64();
|
||||
const auto inputVector = reinterpret_cast<const __m64*>(input);
|
||||
|
||||
# elif defined(USE_NEON)
|
||||
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 16) / 16;
|
||||
const auto inputVector = reinterpret_cast<const int8x8_t*>(input);
|
||||
# endif
|
||||
|
||||
for (IndexType i = 0; i < OutputDimensions; ++i) {
|
||||
const IndexType offset = i * PaddedInputDimensions;
|
||||
|
||||
# if defined(USE_SSE2)
|
||||
__m128i sumLo = _mm_cvtsi32_si128(biases[i]);
|
||||
__m128i sumHi = Zeros;
|
||||
const auto row = reinterpret_cast<const __m128i*>(&weights[offset]);
|
||||
for (IndexType j = 0; j < NumChunks; ++j) {
|
||||
__m128i row_j = _mm_load_si128(&row[j]);
|
||||
__m128i input_j = _mm_load_si128(&inputVector[j]);
|
||||
__m128i extendedRowLo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8);
|
||||
__m128i extendedRowHi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8);
|
||||
__m128i extendedInputLo = _mm_unpacklo_epi8(input_j, Zeros);
|
||||
__m128i extendedInputHi = _mm_unpackhi_epi8(input_j, Zeros);
|
||||
__m128i productLo = _mm_madd_epi16(extendedRowLo, extendedInputLo);
|
||||
__m128i productHi = _mm_madd_epi16(extendedRowHi, extendedInputHi);
|
||||
sumLo = _mm_add_epi32(sumLo, productLo);
|
||||
sumHi = _mm_add_epi32(sumHi, productHi);
|
||||
}
|
||||
__m128i sum = _mm_add_epi32(sumLo, sumHi);
|
||||
__m128i sumHigh_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
sum = _mm_add_epi32(sum, sumHigh_64);
|
||||
__m128i sum_second_32 = _mm_shufflelo_epi16(sum, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
sum = _mm_add_epi32(sum, sum_second_32);
|
||||
output[i] = _mm_cvtsi128_si32(sum);
|
||||
|
||||
# elif defined(USE_MMX)
|
||||
__m64 sumLo = _mm_cvtsi32_si64(biases[i]);
|
||||
__m64 sumHi = Zeros;
|
||||
const auto row = reinterpret_cast<const __m64*>(&weights[offset]);
|
||||
for (IndexType j = 0; j < NumChunks; ++j) {
|
||||
__m64 row_j = row[j];
|
||||
__m64 input_j = inputVector[j];
|
||||
__m64 extendedRowLo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8);
|
||||
__m64 extendedRowHi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8);
|
||||
__m64 extendedInputLo = _mm_unpacklo_pi8(input_j, Zeros);
|
||||
__m64 extendedInputHi = _mm_unpackhi_pi8(input_j, Zeros);
|
||||
__m64 productLo = _mm_madd_pi16(extendedRowLo, extendedInputLo);
|
||||
__m64 productHi = _mm_madd_pi16(extendedRowHi, extendedInputHi);
|
||||
sumLo = _mm_add_pi32(sumLo, productLo);
|
||||
sumHi = _mm_add_pi32(sumHi, productHi);
|
||||
}
|
||||
__m64 sum = _mm_add_pi32(sumLo, sumHi);
|
||||
sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum));
|
||||
output[i] = _mm_cvtsi64_si32(sum);
|
||||
|
||||
# elif defined(USE_NEON)
|
||||
int32x4_t sum = {biases[i]};
|
||||
const auto row = reinterpret_cast<const int8x8_t*>(&weights[offset]);
|
||||
for (IndexType j = 0; j < NumChunks; ++j) {
|
||||
int16x8_t product = vmull_s8(inputVector[j * 2], row[j * 2]);
|
||||
product = vmlal_s8(product, inputVector[j * 2 + 1], row[j * 2 + 1]);
|
||||
sum = vpadalq_s16(sum, product);
|
||||
}
|
||||
output[i] = sum[0] + sum[1] + sum[2] + sum[3];
|
||||
|
||||
# else
|
||||
std::int32_t sum = biases[i];
|
||||
for (IndexType j = 0; j < InputDimensions; ++j) {
|
||||
sum += weights[offset + j] * input[j];
|
||||
}
|
||||
output[i] = sum;
|
||||
# endif
|
||||
}
|
||||
|
||||
# if defined(USE_MMX)
|
||||
_mm_empty();
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
template <IndexType InDims, IndexType OutDims, typename Enabled = void>
|
||||
class AffineTransform;
|
||||
|
||||
// A specialization for large inputs.
|
||||
template <IndexType InDims, IndexType OutDims>
|
||||
class AffineTransform<InDims, OutDims, std::enable_if_t<(ceil_to_multiple<IndexType>(InDims, MaxSimdWidth) >= 2*64)>> {
|
||||
// Affine transformation layer
|
||||
template <typename PreviousLayer, IndexType OutputDimensions>
|
||||
class AffineTransform {
|
||||
public:
|
||||
// Input/output type
|
||||
using InputType = std::uint8_t;
|
||||
using InputType = typename PreviousLayer::OutputType;
|
||||
using OutputType = std::int32_t;
|
||||
static_assert(std::is_same<InputType, std::uint8_t>::value, "");
|
||||
|
||||
// Number of input/output dimensions
|
||||
static constexpr IndexType InputDimensions = InDims;
|
||||
static constexpr IndexType OutputDimensions = OutDims;
|
||||
static constexpr IndexType kInputDimensions =
|
||||
PreviousLayer::kOutputDimensions;
|
||||
static constexpr IndexType kOutputDimensions = OutputDimensions;
|
||||
static constexpr IndexType kPaddedInputDimensions =
|
||||
CeilToMultiple<IndexType>(kInputDimensions, kMaxSimdWidth);
|
||||
|
||||
static constexpr IndexType PaddedInputDimensions =
|
||||
ceil_to_multiple<IndexType>(InputDimensions, MaxSimdWidth);
|
||||
static constexpr IndexType PaddedOutputDimensions =
|
||||
ceil_to_multiple<IndexType>(OutputDimensions, MaxSimdWidth);
|
||||
// Size of forward propagation buffer used in this layer
|
||||
static constexpr std::size_t kSelfBufferSize =
|
||||
CeilToMultiple(kOutputDimensions * sizeof(OutputType), kCacheLineSize);
|
||||
|
||||
using OutputBuffer = OutputType[PaddedOutputDimensions];
|
||||
|
||||
static_assert(PaddedInputDimensions >= 128, "Something went wrong. This specialization should not have been chosen.");
|
||||
|
||||
#if defined (USE_AVX512)
|
||||
static constexpr const IndexType InputSimdWidth = 64;
|
||||
static constexpr const IndexType MaxNumOutputRegs = 16;
|
||||
#elif defined (USE_AVX2)
|
||||
static constexpr const IndexType InputSimdWidth = 32;
|
||||
static constexpr const IndexType MaxNumOutputRegs = 8;
|
||||
#elif defined (USE_SSSE3)
|
||||
static constexpr const IndexType InputSimdWidth = 16;
|
||||
static constexpr const IndexType MaxNumOutputRegs = 8;
|
||||
#elif defined (USE_NEON)
|
||||
static constexpr const IndexType InputSimdWidth = 8;
|
||||
static constexpr const IndexType MaxNumOutputRegs = 8;
|
||||
#else
|
||||
// The fallback implementation will not have permuted weights.
|
||||
// We define these to avoid a lot of ifdefs later.
|
||||
static constexpr const IndexType InputSimdWidth = 1;
|
||||
static constexpr const IndexType MaxNumOutputRegs = 1;
|
||||
#endif
|
||||
|
||||
// A big block is a region in the weight matrix of the size [PaddedInputDimensions, NumOutputRegs].
|
||||
// A small block is a region of size [InputSimdWidth, 1]
|
||||
|
||||
static constexpr const IndexType NumOutputRegs = std::min(MaxNumOutputRegs, OutputDimensions);
|
||||
static constexpr const IndexType SmallBlockSize = InputSimdWidth;
|
||||
static constexpr const IndexType BigBlockSize = NumOutputRegs * PaddedInputDimensions;
|
||||
static constexpr const IndexType NumSmallBlocksInBigBlock = BigBlockSize / SmallBlockSize;
|
||||
static constexpr const IndexType NumSmallBlocksPerOutput = PaddedInputDimensions / SmallBlockSize;
|
||||
static constexpr const IndexType NumBigBlocks = OutputDimensions / NumOutputRegs;
|
||||
|
||||
static_assert(OutputDimensions % NumOutputRegs == 0);
|
||||
// Size of the forward propagation buffer used from the input layer to this layer
|
||||
static constexpr std::size_t kBufferSize =
|
||||
PreviousLayer::kBufferSize + kSelfBufferSize;
|
||||
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) {
|
||||
std::uint32_t hashValue = 0xCC03DAE4u;
|
||||
hashValue += OutputDimensions;
|
||||
hashValue ^= prevHash >> 1;
|
||||
hashValue ^= prevHash << 31;
|
||||
return hashValue;
|
||||
static constexpr std::uint32_t GetHashValue() {
|
||||
std::uint32_t hash_value = 0xCC03DAE4u;
|
||||
hash_value += kOutputDimensions;
|
||||
hash_value ^= PreviousLayer::GetHashValue() >> 1;
|
||||
hash_value ^= PreviousLayer::GetHashValue() << 31;
|
||||
return hash_value;
|
||||
}
|
||||
|
||||
/*
|
||||
Transposes the small blocks within a block.
|
||||
Effectively means that weights can be traversed sequentially during inference.
|
||||
*/
|
||||
static IndexType get_weight_index(IndexType i)
|
||||
{
|
||||
const IndexType smallBlock = (i / SmallBlockSize) % NumSmallBlocksInBigBlock;
|
||||
const IndexType smallBlockCol = smallBlock / NumSmallBlocksPerOutput;
|
||||
const IndexType smallBlockRow = smallBlock % NumSmallBlocksPerOutput;
|
||||
const IndexType bigBlock = i / BigBlockSize;
|
||||
const IndexType rest = i % SmallBlockSize;
|
||||
|
||||
const IndexType idx =
|
||||
bigBlock * BigBlockSize
|
||||
+ smallBlockRow * SmallBlockSize * NumOutputRegs
|
||||
+ smallBlockCol * SmallBlockSize
|
||||
+ rest;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool read_parameters(std::istream& stream) {
|
||||
for (IndexType i = 0; i < OutputDimensions; ++i)
|
||||
biases[i] = read_little_endian<BiasType>(stream);
|
||||
|
||||
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
|
||||
weights[get_weight_index(i)] = read_little_endian<WeightType>(stream);
|
||||
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
// Write network parameters
|
||||
bool write_parameters(std::ostream& stream) const {
|
||||
for (IndexType i = 0; i < OutputDimensions; ++i)
|
||||
write_little_endian<BiasType>(stream, biases[i]);
|
||||
|
||||
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
|
||||
write_little_endian<WeightType>(stream, weights[get_weight_index(i)]);
|
||||
|
||||
// Read network parameters
|
||||
bool ReadParameters(std::istream& stream) {
|
||||
if (!previous_layer_.ReadParameters(stream)) return false;
|
||||
stream.read(reinterpret_cast<char*>(biases_),
|
||||
kOutputDimensions * sizeof(BiasType));
|
||||
stream.read(reinterpret_cast<char*>(weights_),
|
||||
kOutputDimensions * kPaddedInputDimensions *
|
||||
sizeof(WeightType));
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
// Forward propagation
|
||||
const OutputType* propagate(
|
||||
const InputType* input, OutputType* output) const {
|
||||
const OutputType* Propagate(
|
||||
const TransformedFeatureType* transformed_features, char* buffer) const {
|
||||
const auto input = previous_layer_.Propagate(
|
||||
transformed_features, buffer + kSelfBufferSize);
|
||||
const auto output = reinterpret_cast<OutputType*>(buffer);
|
||||
|
||||
#if defined (USE_AVX512)
|
||||
using acc_vec_t = __m512i;
|
||||
using bias_vec_t = __m128i;
|
||||
using weight_vec_t = __m512i;
|
||||
using in_vec_t = __m512i;
|
||||
#define vec_zero _mm512_setzero_si512()
|
||||
#define vec_add_dpbusd_32x2 Simd::m512_add_dpbusd_epi32x2
|
||||
#define vec_hadd Simd::m512_hadd
|
||||
#define vec_haddx4 Simd::m512_haddx4
|
||||
#elif defined (USE_AVX2)
|
||||
using acc_vec_t = __m256i;
|
||||
using bias_vec_t = __m128i;
|
||||
using weight_vec_t = __m256i;
|
||||
using in_vec_t = __m256i;
|
||||
#define vec_zero _mm256_setzero_si256()
|
||||
#define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2
|
||||
#define vec_hadd Simd::m256_hadd
|
||||
#define vec_haddx4 Simd::m256_haddx4
|
||||
#elif defined (USE_SSSE3)
|
||||
using acc_vec_t = __m128i;
|
||||
using bias_vec_t = __m128i;
|
||||
using weight_vec_t = __m128i;
|
||||
using in_vec_t = __m128i;
|
||||
#define vec_zero _mm_setzero_si128()
|
||||
#define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2
|
||||
#define vec_hadd Simd::m128_hadd
|
||||
#define vec_haddx4 Simd::m128_haddx4
|
||||
#elif defined (USE_NEON)
|
||||
using acc_vec_t = int32x4_t;
|
||||
using bias_vec_t = int32x4_t;
|
||||
using weight_vec_t = int8x8_t;
|
||||
using in_vec_t = int8x8_t;
|
||||
#define vec_zero {0}
|
||||
#define vec_add_dpbusd_32x2 Simd::neon_m128_add_dpbusd_epi32x2
|
||||
#define vec_hadd Simd::neon_m128_hadd
|
||||
#define vec_haddx4 Simd::neon_m128_haddx4
|
||||
#endif
|
||||
#if defined(USE_AVX512)
|
||||
constexpr IndexType kNumChunks = kPaddedInputDimensions / (kSimdWidth * 2);
|
||||
const __m512i kOnes = _mm512_set1_epi16(1);
|
||||
const auto input_vector = reinterpret_cast<const __m512i*>(input);
|
||||
|
||||
#if defined (USE_SSSE3) || defined (USE_NEON)
|
||||
const in_vec_t* invec = reinterpret_cast<const in_vec_t*>(input);
|
||||
#elif defined(USE_AVX2)
|
||||
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
|
||||
const __m256i kOnes = _mm256_set1_epi16(1);
|
||||
const auto input_vector = reinterpret_cast<const __m256i*>(input);
|
||||
|
||||
// Perform accumulation to registers for each big block
|
||||
for (IndexType bigBlock = 0; bigBlock < NumBigBlocks; ++bigBlock)
|
||||
{
|
||||
acc_vec_t acc[NumOutputRegs] = { vec_zero };
|
||||
#elif defined(USE_SSSE3)
|
||||
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
|
||||
const __m128i kOnes = _mm_set1_epi16(1);
|
||||
const auto input_vector = reinterpret_cast<const __m128i*>(input);
|
||||
|
||||
// Each big block has NumOutputRegs small blocks in each "row", one per register.
|
||||
// We process two small blocks at a time to save on one addition without VNNI.
|
||||
for (IndexType smallBlock = 0; smallBlock < NumSmallBlocksPerOutput; smallBlock += 2)
|
||||
#elif defined(USE_NEON)
|
||||
constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth;
|
||||
const auto input_vector = reinterpret_cast<const int8x8_t*>(input);
|
||||
#endif
|
||||
|
||||
for (IndexType i = 0; i < kOutputDimensions; ++i) {
|
||||
const IndexType offset = i * kPaddedInputDimensions;
|
||||
|
||||
#if defined(USE_AVX512)
|
||||
__m512i sum = _mm512_setzero_si512();
|
||||
const auto row = reinterpret_cast<const __m512i*>(&weights_[offset]);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
__m512i product = _mm512_maddubs_epi16(_mm512_loadu_si512(&input_vector[j]), _mm512_load_si512(&row[j]));
|
||||
#else
|
||||
__m512i product = _mm512_maddubs_epi16(_mm512_load_si512(&input_vector[j]), _mm512_load_si512(&row[j]));
|
||||
#endif
|
||||
|
||||
product = _mm512_madd_epi16(product, kOnes);
|
||||
sum = _mm512_add_epi32(sum, product);
|
||||
}
|
||||
output[i] = _mm512_reduce_add_epi32(sum) + biases_[i];
|
||||
|
||||
// Note: Changing kMaxSimdWidth from 32 to 64 breaks loading existing networks.
|
||||
// As a result kPaddedInputDimensions may not be an even multiple of 64(512bit)
|
||||
// and we have to do one more 256bit chunk.
|
||||
if (kPaddedInputDimensions != kNumChunks * kSimdWidth * 2)
|
||||
{
|
||||
const weight_vec_t* weightvec =
|
||||
reinterpret_cast<const weight_vec_t*>(
|
||||
weights
|
||||
+ bigBlock * BigBlockSize
|
||||
+ smallBlock * SmallBlockSize * NumOutputRegs);
|
||||
const auto iv_256 = reinterpret_cast<const __m256i*>(input);
|
||||
const auto row_256 = reinterpret_cast<const __m256i*>(&weights_[offset]);
|
||||
int j = kNumChunks * 2;
|
||||
|
||||
const in_vec_t in0 = invec[smallBlock + 0];
|
||||
const in_vec_t in1 = invec[smallBlock + 1];
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__) // See HACK comment below in AVX2.
|
||||
__m256i sum256 = _mm256_maddubs_epi16(_mm256_loadu_si256(&iv_256[j]), _mm256_load_si256(&row_256[j]));
|
||||
#else
|
||||
__m256i sum256 = _mm256_maddubs_epi16(_mm256_load_si256(&iv_256[j]), _mm256_load_si256(&row_256[j]));
|
||||
#endif
|
||||
|
||||
for (IndexType k = 0; k < NumOutputRegs; ++k)
|
||||
vec_add_dpbusd_32x2(acc[k], in0, weightvec[k], in1, weightvec[k + NumOutputRegs]);
|
||||
sum256 = _mm256_madd_epi16(sum256, _mm256_set1_epi16(1));
|
||||
sum256 = _mm256_hadd_epi32(sum256, sum256);
|
||||
sum256 = _mm256_hadd_epi32(sum256, sum256);
|
||||
const __m128i lo = _mm256_extracti128_si256(sum256, 0);
|
||||
const __m128i hi = _mm256_extracti128_si256(sum256, 1);
|
||||
output[i] += _mm_cvtsi128_si32(lo) + _mm_cvtsi128_si32(hi);
|
||||
}
|
||||
|
||||
// Horizontally add all accumulators.
|
||||
if constexpr (NumOutputRegs % 4 == 0)
|
||||
{
|
||||
bias_vec_t* outputvec = reinterpret_cast<bias_vec_t*>(output);
|
||||
const bias_vec_t* biasvec = reinterpret_cast<const bias_vec_t*>(biases);
|
||||
#elif defined(USE_AVX2)
|
||||
__m256i sum = _mm256_setzero_si256();
|
||||
const auto row = reinterpret_cast<const __m256i*>(&weights_[offset]);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
__m256i product = _mm256_maddubs_epi16(
|
||||
|
||||
for (IndexType k = 0; k < NumOutputRegs; k += 4)
|
||||
{
|
||||
const IndexType idx = (bigBlock * NumOutputRegs + k) / 4;
|
||||
outputvec[idx] = vec_haddx4(acc[k+0], acc[k+1], acc[k+2], acc[k+3], biasvec[idx]);
|
||||
}
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
// HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary
|
||||
// compiled with g++ in MSYS2 crashes here because the output memory is not aligned
|
||||
// even though alignas is specified.
|
||||
_mm256_loadu_si256
|
||||
#else
|
||||
_mm256_load_si256
|
||||
#endif
|
||||
|
||||
(&input_vector[j]), _mm256_load_si256(&row[j]));
|
||||
product = _mm256_madd_epi16(product, kOnes);
|
||||
sum = _mm256_add_epi32(sum, product);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (IndexType k = 0; k < NumOutputRegs; ++k)
|
||||
{
|
||||
const IndexType idx = (bigBlock * NumOutputRegs + k);
|
||||
output[idx] = vec_hadd(acc[k], biases[idx]);
|
||||
}
|
||||
sum = _mm256_hadd_epi32(sum, sum);
|
||||
sum = _mm256_hadd_epi32(sum, sum);
|
||||
const __m128i lo = _mm256_extracti128_si256(sum, 0);
|
||||
const __m128i hi = _mm256_extracti128_si256(sum, 1);
|
||||
output[i] = _mm_cvtsi128_si32(lo) + _mm_cvtsi128_si32(hi) + biases_[i];
|
||||
|
||||
#elif defined(USE_SSSE3)
|
||||
__m128i sum = _mm_cvtsi32_si128(biases_[i]);
|
||||
const auto row = reinterpret_cast<const __m128i*>(&weights_[offset]);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
__m128i product = _mm_maddubs_epi16(
|
||||
_mm_load_si128(&input_vector[j]), _mm_load_si128(&row[j]));
|
||||
product = _mm_madd_epi16(product, kOnes);
|
||||
sum = _mm_add_epi32(sum, product);
|
||||
}
|
||||
sum = _mm_hadd_epi32(sum, sum);
|
||||
sum = _mm_hadd_epi32(sum, sum);
|
||||
output[i] = _mm_cvtsi128_si32(sum);
|
||||
|
||||
#elif defined(USE_NEON)
|
||||
int32x4_t sum = {biases_[i]};
|
||||
const auto row = reinterpret_cast<const int8x8_t*>(&weights_[offset]);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
int16x8_t product = vmull_s8(input_vector[j * 2], row[j * 2]);
|
||||
product = vmlal_s8(product, input_vector[j * 2 + 1], row[j * 2 + 1]);
|
||||
sum = vpadalq_s16(sum, product);
|
||||
}
|
||||
output[i] = sum[0] + sum[1] + sum[2] + sum[3];
|
||||
|
||||
#else
|
||||
OutputType sum = biases_[i];
|
||||
for (IndexType j = 0; j < kInputDimensions; ++j) {
|
||||
sum += weights_[offset + j] * input[j];
|
||||
}
|
||||
output[i] = sum;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
# undef vec_zero
|
||||
# undef vec_add_dpbusd_32x2
|
||||
# undef vec_hadd
|
||||
# undef vec_haddx4
|
||||
#else
|
||||
// Use old implementation for the other architectures.
|
||||
affine_transform_non_ssse3<
|
||||
InputDimensions,
|
||||
PaddedInputDimensions,
|
||||
OutputDimensions>(output, weights, biases, input);
|
||||
|
||||
#endif
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -364,176 +203,13 @@ namespace Stockfish::Eval::NNUE::Layers {
|
||||
using BiasType = OutputType;
|
||||
using WeightType = std::int8_t;
|
||||
|
||||
alignas(CacheLineSize) BiasType biases[OutputDimensions];
|
||||
alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions];
|
||||
PreviousLayer previous_layer_;
|
||||
|
||||
alignas(kCacheLineSize) BiasType biases_[kOutputDimensions];
|
||||
alignas(kCacheLineSize)
|
||||
WeightType weights_[kOutputDimensions * kPaddedInputDimensions];
|
||||
};
|
||||
|
||||
template <IndexType InDims, IndexType OutDims>
|
||||
class AffineTransform<InDims, OutDims, std::enable_if_t<(ceil_to_multiple<IndexType>(InDims, MaxSimdWidth) < 2*64)>> {
|
||||
public:
|
||||
// Input/output type
|
||||
// Input/output type
|
||||
using InputType = std::uint8_t;
|
||||
using OutputType = std::int32_t;
|
||||
|
||||
// Number of input/output dimensions
|
||||
static constexpr IndexType InputDimensions = InDims;
|
||||
static constexpr IndexType OutputDimensions = OutDims;
|
||||
|
||||
static constexpr IndexType PaddedInputDimensions =
|
||||
ceil_to_multiple<IndexType>(InputDimensions, MaxSimdWidth);
|
||||
static constexpr IndexType PaddedOutputDimensions =
|
||||
ceil_to_multiple<IndexType>(OutputDimensions, MaxSimdWidth);
|
||||
|
||||
using OutputBuffer = OutputType[PaddedOutputDimensions];
|
||||
|
||||
static_assert(PaddedInputDimensions < 128, "Something went wrong. This specialization should not have been chosen.");
|
||||
|
||||
#if defined (USE_SSSE3)
|
||||
static constexpr const IndexType OutputSimdWidth = SimdWidth / 4;
|
||||
static constexpr const IndexType InputSimdWidth = SimdWidth;
|
||||
#endif
|
||||
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) {
|
||||
std::uint32_t hashValue = 0xCC03DAE4u;
|
||||
hashValue += OutputDimensions;
|
||||
hashValue ^= prevHash >> 1;
|
||||
hashValue ^= prevHash << 31;
|
||||
return hashValue;
|
||||
}
|
||||
|
||||
static IndexType get_weight_index_scrambled(IndexType i)
|
||||
{
|
||||
return
|
||||
(i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 +
|
||||
i / PaddedInputDimensions * 4 +
|
||||
i % 4;
|
||||
}
|
||||
|
||||
static IndexType get_weight_index(IndexType i)
|
||||
{
|
||||
#if defined (USE_SSSE3)
|
||||
return get_weight_index_scrambled(i);
|
||||
#else
|
||||
return i;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool read_parameters(std::istream& stream) {
|
||||
for (IndexType i = 0; i < OutputDimensions; ++i)
|
||||
biases[i] = read_little_endian<BiasType>(stream);
|
||||
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
|
||||
weights[get_weight_index(i)] = read_little_endian<WeightType>(stream);
|
||||
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
// Write network parameters
|
||||
bool write_parameters(std::ostream& stream) const {
|
||||
for (IndexType i = 0; i < OutputDimensions; ++i)
|
||||
write_little_endian<BiasType>(stream, biases[i]);
|
||||
|
||||
for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i)
|
||||
write_little_endian<WeightType>(stream, weights[get_weight_index(i)]);
|
||||
|
||||
return !stream.fail();
|
||||
}
|
||||
// Forward propagation
|
||||
const OutputType* propagate(
|
||||
const InputType* input, OutputType* output) const {
|
||||
|
||||
#if defined (USE_AVX2)
|
||||
using vec_t = __m256i;
|
||||
#define vec_setzero _mm256_setzero_si256
|
||||
#define vec_set_32 _mm256_set1_epi32
|
||||
#define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32
|
||||
#define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2
|
||||
#define vec_add_dpbusd_32x4 Simd::m256_add_dpbusd_epi32x4
|
||||
#define vec_hadd Simd::m256_hadd
|
||||
#define vec_haddx4 Simd::m256_haddx4
|
||||
#elif defined (USE_SSSE3)
|
||||
using vec_t = __m128i;
|
||||
#define vec_setzero _mm_setzero_si128
|
||||
#define vec_set_32 _mm_set1_epi32
|
||||
#define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32
|
||||
#define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2
|
||||
#define vec_add_dpbusd_32x4 Simd::m128_add_dpbusd_epi32x4
|
||||
#define vec_hadd Simd::m128_hadd
|
||||
#define vec_haddx4 Simd::m128_haddx4
|
||||
#endif
|
||||
|
||||
#if defined (USE_SSSE3)
|
||||
const auto inputVector = reinterpret_cast<const vec_t*>(input);
|
||||
|
||||
static_assert(OutputDimensions % OutputSimdWidth == 0 || OutputDimensions == 1);
|
||||
|
||||
if constexpr (OutputDimensions % OutputSimdWidth == 0)
|
||||
{
|
||||
constexpr IndexType NumChunks = ceil_to_multiple<IndexType>(InputDimensions, 8) / 4;
|
||||
constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth;
|
||||
|
||||
const auto input32 = reinterpret_cast<const std::int32_t*>(input);
|
||||
const vec_t* biasvec = reinterpret_cast<const vec_t*>(biases);
|
||||
vec_t acc[NumRegs];
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
acc[k] = biasvec[k];
|
||||
|
||||
for (IndexType i = 0; i < NumChunks; i += 2)
|
||||
{
|
||||
const vec_t in0 = vec_set_32(input32[i + 0]);
|
||||
const vec_t in1 = vec_set_32(input32[i + 1]);
|
||||
const auto col0 = reinterpret_cast<const vec_t*>(&weights[(i + 0) * OutputDimensions * 4]);
|
||||
const auto col1 = reinterpret_cast<const vec_t*>(&weights[(i + 1) * OutputDimensions * 4]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
vec_add_dpbusd_32x2(acc[k], in0, col0[k], in1, col1[k]);
|
||||
}
|
||||
|
||||
vec_t* outptr = reinterpret_cast<vec_t*>(output);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
outptr[k] = acc[k];
|
||||
}
|
||||
else if constexpr (OutputDimensions == 1)
|
||||
{
|
||||
constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth;
|
||||
vec_t sum0 = vec_setzero();
|
||||
const auto row0 = reinterpret_cast<const vec_t*>(&weights[0]);
|
||||
|
||||
for (int j = 0; j < (int)NumChunks; ++j)
|
||||
{
|
||||
const vec_t in = inputVector[j];
|
||||
vec_add_dpbusd_32(sum0, in, row0[j]);
|
||||
}
|
||||
output[0] = vec_hadd(sum0, biases[0]);
|
||||
}
|
||||
|
||||
# undef vec_setzero
|
||||
# undef vec_set_32
|
||||
# undef vec_add_dpbusd_32
|
||||
# undef vec_add_dpbusd_32x2
|
||||
# undef vec_add_dpbusd_32x4
|
||||
# undef vec_hadd
|
||||
# undef vec_haddx4
|
||||
#else
|
||||
// Use old implementation for the other architectures.
|
||||
affine_transform_non_ssse3<
|
||||
InputDimensions,
|
||||
PaddedInputDimensions,
|
||||
OutputDimensions>(output, weights, biases, input);
|
||||
#endif
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private:
|
||||
using BiasType = OutputType;
|
||||
using WeightType = std::int8_t;
|
||||
|
||||
alignas(CacheLineSize) BiasType biases[OutputDimensions];
|
||||
alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions];
|
||||
};
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE::Layers
|
||||
} // namespace Eval::NNUE::Layers
|
||||
|
||||
#endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED
|
||||
|
||||
+102
-96
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -23,158 +23,164 @@
|
||||
|
||||
#include "../nnue_common.h"
|
||||
|
||||
namespace Stockfish::Eval::NNUE::Layers {
|
||||
namespace Eval::NNUE::Layers {
|
||||
|
||||
// Clipped ReLU
|
||||
template <IndexType InDims>
|
||||
template <typename PreviousLayer>
|
||||
class ClippedReLU {
|
||||
public:
|
||||
// Input/output type
|
||||
using InputType = std::int32_t;
|
||||
using InputType = typename PreviousLayer::OutputType;
|
||||
using OutputType = std::uint8_t;
|
||||
static_assert(std::is_same<InputType, std::int32_t>::value, "");
|
||||
|
||||
// Number of input/output dimensions
|
||||
static constexpr IndexType InputDimensions = InDims;
|
||||
static constexpr IndexType OutputDimensions = InputDimensions;
|
||||
static constexpr IndexType PaddedOutputDimensions =
|
||||
ceil_to_multiple<IndexType>(OutputDimensions, 32);
|
||||
static constexpr IndexType kInputDimensions =
|
||||
PreviousLayer::kOutputDimensions;
|
||||
static constexpr IndexType kOutputDimensions = kInputDimensions;
|
||||
|
||||
using OutputBuffer = OutputType[PaddedOutputDimensions];
|
||||
// Size of forward propagation buffer used in this layer
|
||||
static constexpr std::size_t kSelfBufferSize =
|
||||
CeilToMultiple(kOutputDimensions * sizeof(OutputType), kCacheLineSize);
|
||||
|
||||
// Size of the forward propagation buffer used from the input layer to this layer
|
||||
static constexpr std::size_t kBufferSize =
|
||||
PreviousLayer::kBufferSize + kSelfBufferSize;
|
||||
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) {
|
||||
std::uint32_t hashValue = 0x538D24C7u;
|
||||
hashValue += prevHash;
|
||||
return hashValue;
|
||||
static constexpr std::uint32_t GetHashValue() {
|
||||
std::uint32_t hash_value = 0x538D24C7u;
|
||||
hash_value += PreviousLayer::GetHashValue();
|
||||
return hash_value;
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool read_parameters(std::istream&) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Write network parameters
|
||||
bool write_parameters(std::ostream&) const {
|
||||
return true;
|
||||
bool ReadParameters(std::istream& stream) {
|
||||
return previous_layer_.ReadParameters(stream);
|
||||
}
|
||||
|
||||
// Forward propagation
|
||||
const OutputType* propagate(
|
||||
const InputType* input, OutputType* output) const {
|
||||
const OutputType* Propagate(
|
||||
const TransformedFeatureType* transformed_features, char* buffer) const {
|
||||
const auto input = previous_layer_.Propagate(
|
||||
transformed_features, buffer + kSelfBufferSize);
|
||||
const auto output = reinterpret_cast<OutputType*>(buffer);
|
||||
|
||||
#if defined(USE_AVX2)
|
||||
if constexpr (InputDimensions % SimdWidth == 0) {
|
||||
constexpr IndexType NumChunks = InputDimensions / SimdWidth;
|
||||
const __m256i Zero = _mm256_setzero_si256();
|
||||
const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0);
|
||||
const auto in = reinterpret_cast<const __m256i*>(input);
|
||||
const auto out = reinterpret_cast<__m256i*>(output);
|
||||
for (IndexType i = 0; i < NumChunks; ++i) {
|
||||
const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32(
|
||||
_mm256_load_si256(&in[i * 4 + 0]),
|
||||
_mm256_load_si256(&in[i * 4 + 1])), WeightScaleBits);
|
||||
const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32(
|
||||
_mm256_load_si256(&in[i * 4 + 2]),
|
||||
_mm256_load_si256(&in[i * 4 + 3])), WeightScaleBits);
|
||||
_mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8(
|
||||
_mm256_packs_epi16(words0, words1), Zero), Offsets));
|
||||
}
|
||||
} else {
|
||||
constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2);
|
||||
const __m128i Zero = _mm_setzero_si128();
|
||||
const auto in = reinterpret_cast<const __m128i*>(input);
|
||||
const auto out = reinterpret_cast<__m128i*>(output);
|
||||
for (IndexType i = 0; i < NumChunks; ++i) {
|
||||
const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32(
|
||||
_mm_load_si128(&in[i * 4 + 0]),
|
||||
_mm_load_si128(&in[i * 4 + 1])), WeightScaleBits);
|
||||
const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32(
|
||||
_mm_load_si128(&in[i * 4 + 2]),
|
||||
_mm_load_si128(&in[i * 4 + 3])), WeightScaleBits);
|
||||
const __m128i packedbytes = _mm_packs_epi16(words0, words1);
|
||||
_mm_store_si128(&out[i], _mm_max_epi8(packedbytes, Zero));
|
||||
}
|
||||
}
|
||||
constexpr IndexType Start =
|
||||
InputDimensions % SimdWidth == 0
|
||||
? InputDimensions / SimdWidth * SimdWidth
|
||||
: InputDimensions / (SimdWidth / 2) * (SimdWidth / 2);
|
||||
constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth;
|
||||
const __m256i kZero = _mm256_setzero_si256();
|
||||
const __m256i kOffsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0);
|
||||
const auto in = reinterpret_cast<const __m256i*>(input);
|
||||
const auto out = reinterpret_cast<__m256i*>(output);
|
||||
for (IndexType i = 0; i < kNumChunks; ++i) {
|
||||
const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32(
|
||||
|
||||
#elif defined(USE_SSE2)
|
||||
constexpr IndexType NumChunks = InputDimensions / SimdWidth;
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
// HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary
|
||||
// compiled with g++ in MSYS2 crashes here because the output memory is not aligned
|
||||
// even though alignas is specified.
|
||||
_mm256_loadu_si256
|
||||
#else
|
||||
_mm256_load_si256
|
||||
#endif
|
||||
|
||||
(&in[i * 4 + 0]),
|
||||
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
_mm256_loadu_si256
|
||||
#else
|
||||
_mm256_load_si256
|
||||
#endif
|
||||
|
||||
(&in[i * 4 + 1])), kWeightScaleBits);
|
||||
const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32(
|
||||
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
_mm256_loadu_si256
|
||||
#else
|
||||
_mm256_load_si256
|
||||
#endif
|
||||
|
||||
(&in[i * 4 + 2]),
|
||||
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
_mm256_loadu_si256
|
||||
#else
|
||||
_mm256_load_si256
|
||||
#endif
|
||||
|
||||
(&in[i * 4 + 3])), kWeightScaleBits);
|
||||
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
_mm256_storeu_si256
|
||||
#else
|
||||
_mm256_store_si256
|
||||
#endif
|
||||
|
||||
(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8(
|
||||
_mm256_packs_epi16(words0, words1), kZero), kOffsets));
|
||||
}
|
||||
constexpr IndexType kStart = kNumChunks * kSimdWidth;
|
||||
|
||||
#elif defined(USE_SSSE3)
|
||||
constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth;
|
||||
|
||||
#ifdef USE_SSE41
|
||||
const __m128i Zero = _mm_setzero_si128();
|
||||
const __m128i kZero = _mm_setzero_si128();
|
||||
#else
|
||||
const __m128i k0x80s = _mm_set1_epi8(-128);
|
||||
#endif
|
||||
|
||||
const auto in = reinterpret_cast<const __m128i*>(input);
|
||||
const auto out = reinterpret_cast<__m128i*>(output);
|
||||
for (IndexType i = 0; i < NumChunks; ++i) {
|
||||
for (IndexType i = 0; i < kNumChunks; ++i) {
|
||||
const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32(
|
||||
_mm_load_si128(&in[i * 4 + 0]),
|
||||
_mm_load_si128(&in[i * 4 + 1])), WeightScaleBits);
|
||||
_mm_load_si128(&in[i * 4 + 1])), kWeightScaleBits);
|
||||
const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32(
|
||||
_mm_load_si128(&in[i * 4 + 2]),
|
||||
_mm_load_si128(&in[i * 4 + 3])), WeightScaleBits);
|
||||
_mm_load_si128(&in[i * 4 + 3])), kWeightScaleBits);
|
||||
const __m128i packedbytes = _mm_packs_epi16(words0, words1);
|
||||
_mm_store_si128(&out[i],
|
||||
|
||||
#ifdef USE_SSE41
|
||||
_mm_max_epi8(packedbytes, Zero)
|
||||
_mm_max_epi8(packedbytes, kZero)
|
||||
#else
|
||||
_mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)
|
||||
#endif
|
||||
|
||||
);
|
||||
}
|
||||
constexpr IndexType Start = NumChunks * SimdWidth;
|
||||
|
||||
#elif defined(USE_MMX)
|
||||
constexpr IndexType NumChunks = InputDimensions / SimdWidth;
|
||||
const __m64 k0x80s = _mm_set1_pi8(-128);
|
||||
const auto in = reinterpret_cast<const __m64*>(input);
|
||||
const auto out = reinterpret_cast<__m64*>(output);
|
||||
for (IndexType i = 0; i < NumChunks; ++i) {
|
||||
const __m64 words0 = _mm_srai_pi16(
|
||||
_mm_packs_pi32(in[i * 4 + 0], in[i * 4 + 1]),
|
||||
WeightScaleBits);
|
||||
const __m64 words1 = _mm_srai_pi16(
|
||||
_mm_packs_pi32(in[i * 4 + 2], in[i * 4 + 3]),
|
||||
WeightScaleBits);
|
||||
const __m64 packedbytes = _mm_packs_pi16(words0, words1);
|
||||
out[i] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s);
|
||||
}
|
||||
_mm_empty();
|
||||
constexpr IndexType Start = NumChunks * SimdWidth;
|
||||
constexpr IndexType kStart = kNumChunks * kSimdWidth;
|
||||
|
||||
#elif defined(USE_NEON)
|
||||
constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2);
|
||||
const int8x8_t Zero = {0};
|
||||
constexpr IndexType kNumChunks = kInputDimensions / (kSimdWidth / 2);
|
||||
const int8x8_t kZero = {0};
|
||||
const auto in = reinterpret_cast<const int32x4_t*>(input);
|
||||
const auto out = reinterpret_cast<int8x8_t*>(output);
|
||||
for (IndexType i = 0; i < NumChunks; ++i) {
|
||||
for (IndexType i = 0; i < kNumChunks; ++i) {
|
||||
int16x8_t shifted;
|
||||
const auto pack = reinterpret_cast<int16x4_t*>(&shifted);
|
||||
pack[0] = vqshrn_n_s32(in[i * 2 + 0], WeightScaleBits);
|
||||
pack[1] = vqshrn_n_s32(in[i * 2 + 1], WeightScaleBits);
|
||||
out[i] = vmax_s8(vqmovn_s16(shifted), Zero);
|
||||
pack[0] = vqshrn_n_s32(in[i * 2 + 0], kWeightScaleBits);
|
||||
pack[1] = vqshrn_n_s32(in[i * 2 + 1], kWeightScaleBits);
|
||||
out[i] = vmax_s8(vqmovn_s16(shifted), kZero);
|
||||
}
|
||||
constexpr IndexType Start = NumChunks * (SimdWidth / 2);
|
||||
constexpr IndexType kStart = kNumChunks * (kSimdWidth / 2);
|
||||
#else
|
||||
constexpr IndexType Start = 0;
|
||||
constexpr IndexType kStart = 0;
|
||||
#endif
|
||||
|
||||
for (IndexType i = Start; i < InputDimensions; ++i) {
|
||||
for (IndexType i = kStart; i < kInputDimensions; ++i) {
|
||||
output[i] = static_cast<OutputType>(
|
||||
std::max(0, std::min(127, input[i] >> WeightScaleBits)));
|
||||
std::max(0, std::min(127, input[i] >> kWeightScaleBits)));
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private:
|
||||
PreviousLayer previous_layer_;
|
||||
};
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE::Layers
|
||||
} // namespace Eval::NNUE::Layers
|
||||
|
||||
#endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// NNUE evaluation function layer InputSlice definition
|
||||
|
||||
#ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED
|
||||
#define NNUE_LAYERS_INPUT_SLICE_H_INCLUDED
|
||||
|
||||
#include "../nnue_common.h"
|
||||
|
||||
namespace Eval::NNUE::Layers {
|
||||
|
||||
// Input layer
|
||||
template <IndexType OutputDimensions, IndexType Offset = 0>
|
||||
class InputSlice {
|
||||
public:
|
||||
// Need to maintain alignment
|
||||
static_assert(Offset % kMaxSimdWidth == 0, "");
|
||||
|
||||
// Output type
|
||||
using OutputType = TransformedFeatureType;
|
||||
|
||||
// Output dimensionality
|
||||
static constexpr IndexType kOutputDimensions = OutputDimensions;
|
||||
|
||||
// Size of forward propagation buffer used from the input layer to this layer
|
||||
static constexpr std::size_t kBufferSize = 0;
|
||||
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t GetHashValue() {
|
||||
std::uint32_t hash_value = 0xEC42E90Du;
|
||||
hash_value ^= kOutputDimensions ^ (Offset << 10);
|
||||
return hash_value;
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool ReadParameters(std::istream& /*stream*/) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Forward propagation
|
||||
const OutputType* Propagate(
|
||||
const TransformedFeatureType* transformed_features,
|
||||
char* /*buffer*/) const {
|
||||
return transformed_features + Offset;
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace Layers
|
||||
|
||||
#endif // #ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -23,15 +23,17 @@
|
||||
|
||||
#include "nnue_architecture.h"
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
namespace Eval::NNUE {
|
||||
|
||||
// Class that holds the result of affine transformation of input features
|
||||
struct alignas(CacheLineSize) Accumulator {
|
||||
std::int16_t accumulation[2][TransformedFeatureDimensions];
|
||||
std::int32_t psqtAccumulation[2][PSQTBuckets];
|
||||
bool computed[2];
|
||||
struct alignas(32) Accumulator {
|
||||
std::int16_t
|
||||
accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions];
|
||||
Value score;
|
||||
bool computed_accumulation;
|
||||
bool computed_score;
|
||||
};
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
} // namespace Eval::NNUE
|
||||
|
||||
#endif // NNUE_ACCUMULATOR_H_INCLUDED
|
||||
|
||||
+10
-105
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -21,113 +21,18 @@
|
||||
#ifndef NNUE_ARCHITECTURE_H_INCLUDED
|
||||
#define NNUE_ARCHITECTURE_H_INCLUDED
|
||||
|
||||
#include <memory>
|
||||
// Defines the network structure
|
||||
#include "architectures/halfkp_256x2-32-32.h"
|
||||
|
||||
#include "nnue_common.h"
|
||||
namespace Eval::NNUE {
|
||||
|
||||
#include "features/half_ka_v2_hm.h"
|
||||
static_assert(kTransformedFeatureDimensions % kMaxSimdWidth == 0, "");
|
||||
static_assert(Network::kOutputDimensions == 1, "");
|
||||
static_assert(std::is_same<Network::OutputType, std::int32_t>::value, "");
|
||||
|
||||
#include "layers/affine_transform.h"
|
||||
#include "layers/clipped_relu.h"
|
||||
// Trigger for full calculation instead of difference calculation
|
||||
constexpr auto kRefreshTriggers = RawFeatures::kRefreshTriggers;
|
||||
|
||||
#include "../misc.h"
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
|
||||
// Input features used in evaluation function
|
||||
using FeatureSet = Features::HalfKAv2_hm;
|
||||
|
||||
// Number of input feature dimensions after conversion
|
||||
constexpr IndexType TransformedFeatureDimensions = 1024;
|
||||
constexpr IndexType PSQTBuckets = 8;
|
||||
constexpr IndexType LayerStacks = 8;
|
||||
|
||||
struct Network
|
||||
{
|
||||
static constexpr int FC_0_OUTPUTS = 15;
|
||||
static constexpr int FC_1_OUTPUTS = 32;
|
||||
|
||||
Layers::AffineTransform<TransformedFeatureDimensions, FC_0_OUTPUTS + 1> fc_0;
|
||||
Layers::ClippedReLU<FC_0_OUTPUTS + 1> ac_0;
|
||||
Layers::AffineTransform<FC_0_OUTPUTS, FC_1_OUTPUTS> fc_1;
|
||||
Layers::ClippedReLU<FC_1_OUTPUTS> ac_1;
|
||||
Layers::AffineTransform<FC_1_OUTPUTS, 1> fc_2;
|
||||
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t get_hash_value() {
|
||||
// input slice hash
|
||||
std::uint32_t hashValue = 0xEC42E90Du;
|
||||
hashValue ^= TransformedFeatureDimensions * 2;
|
||||
|
||||
hashValue = decltype(fc_0)::get_hash_value(hashValue);
|
||||
hashValue = decltype(ac_0)::get_hash_value(hashValue);
|
||||
hashValue = decltype(fc_1)::get_hash_value(hashValue);
|
||||
hashValue = decltype(ac_1)::get_hash_value(hashValue);
|
||||
hashValue = decltype(fc_2)::get_hash_value(hashValue);
|
||||
|
||||
return hashValue;
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool read_parameters(std::istream& stream) {
|
||||
if (!fc_0.read_parameters(stream)) return false;
|
||||
if (!ac_0.read_parameters(stream)) return false;
|
||||
if (!fc_1.read_parameters(stream)) return false;
|
||||
if (!ac_1.read_parameters(stream)) return false;
|
||||
if (!fc_2.read_parameters(stream)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool write_parameters(std::ostream& stream) const {
|
||||
if (!fc_0.write_parameters(stream)) return false;
|
||||
if (!ac_0.write_parameters(stream)) return false;
|
||||
if (!fc_1.write_parameters(stream)) return false;
|
||||
if (!ac_1.write_parameters(stream)) return false;
|
||||
if (!fc_2.write_parameters(stream)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::int32_t propagate(const TransformedFeatureType* transformedFeatures)
|
||||
{
|
||||
struct alignas(CacheLineSize) Buffer
|
||||
{
|
||||
alignas(CacheLineSize) decltype(fc_0)::OutputBuffer fc_0_out;
|
||||
alignas(CacheLineSize) decltype(ac_0)::OutputBuffer ac_0_out;
|
||||
alignas(CacheLineSize) decltype(fc_1)::OutputBuffer fc_1_out;
|
||||
alignas(CacheLineSize) decltype(ac_1)::OutputBuffer ac_1_out;
|
||||
alignas(CacheLineSize) decltype(fc_2)::OutputBuffer fc_2_out;
|
||||
|
||||
Buffer()
|
||||
{
|
||||
std::memset(this, 0, sizeof(*this));
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(__clang__) && (__APPLE__)
|
||||
// workaround for a bug reported with xcode 12
|
||||
static thread_local auto tlsBuffer = std::make_unique<Buffer>();
|
||||
// Access TLS only once, cache result.
|
||||
Buffer& buffer = *tlsBuffer;
|
||||
#else
|
||||
alignas(CacheLineSize) static thread_local Buffer buffer;
|
||||
#endif
|
||||
|
||||
fc_0.propagate(transformedFeatures, buffer.fc_0_out);
|
||||
ac_0.propagate(buffer.fc_0_out, buffer.ac_0_out);
|
||||
fc_1.propagate(buffer.ac_0_out, buffer.fc_1_out);
|
||||
ac_1.propagate(buffer.fc_1_out, buffer.ac_1_out);
|
||||
fc_2.propagate(buffer.ac_1_out, buffer.fc_2_out);
|
||||
|
||||
// buffer.fc_0_out[FC_0_OUTPUTS] is such that 1.0 is equal to 127*(1<<WeightScaleBits) in quantized form
|
||||
// but we want 1.0 to be equal to 600*OutputScale
|
||||
std::int32_t fwdOut = int(buffer.fc_0_out[FC_0_OUTPUTS]) * (600*OutputScale) / (127*(1<<WeightScaleBits));
|
||||
std::int32_t outputValue = buffer.fc_2_out[0] + fwdOut;
|
||||
|
||||
return outputValue;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
} // namespace Eval::NNUE
|
||||
|
||||
#endif // #ifndef NNUE_ARCHITECTURE_H_INCLUDED
|
||||
|
||||
+13
-100
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -21,11 +21,6 @@
|
||||
#ifndef NNUE_COMMON_H_INCLUDED
|
||||
#define NNUE_COMMON_H_INCLUDED
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include "../misc.h" // for IsLittleEndian
|
||||
|
||||
#if defined(USE_AVX2)
|
||||
#include <immintrin.h>
|
||||
|
||||
@@ -38,40 +33,34 @@
|
||||
#elif defined(USE_SSE2)
|
||||
#include <emmintrin.h>
|
||||
|
||||
#elif defined(USE_MMX)
|
||||
#include <mmintrin.h>
|
||||
|
||||
#elif defined(USE_NEON)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
namespace Eval::NNUE {
|
||||
|
||||
// Version of the evaluation file
|
||||
constexpr std::uint32_t Version = 0x7AF32F20u;
|
||||
constexpr std::uint32_t kVersion = 0x7AF32F16u;
|
||||
|
||||
// Constant used in evaluation value calculation
|
||||
constexpr int OutputScale = 16;
|
||||
constexpr int WeightScaleBits = 6;
|
||||
constexpr int FV_SCALE = 16;
|
||||
constexpr int kWeightScaleBits = 6;
|
||||
|
||||
// Size of cache line (in bytes)
|
||||
constexpr std::size_t CacheLineSize = 64;
|
||||
constexpr std::size_t kCacheLineSize = 64;
|
||||
|
||||
// SIMD width (in bytes)
|
||||
#if defined(USE_AVX2)
|
||||
constexpr std::size_t SimdWidth = 32;
|
||||
constexpr std::size_t kSimdWidth = 32;
|
||||
|
||||
#elif defined(USE_SSE2)
|
||||
constexpr std::size_t SimdWidth = 16;
|
||||
|
||||
#elif defined(USE_MMX)
|
||||
constexpr std::size_t SimdWidth = 8;
|
||||
constexpr std::size_t kSimdWidth = 16;
|
||||
|
||||
#elif defined(USE_NEON)
|
||||
constexpr std::size_t SimdWidth = 16;
|
||||
constexpr std::size_t kSimdWidth = 16;
|
||||
#endif
|
||||
|
||||
constexpr std::size_t MaxSimdWidth = 32;
|
||||
constexpr std::size_t kMaxSimdWidth = 32;
|
||||
|
||||
// Type of input feature after conversion
|
||||
using TransformedFeatureType = std::uint8_t;
|
||||
@@ -79,86 +68,10 @@ namespace Stockfish::Eval::NNUE {
|
||||
|
||||
// Round n up to be a multiple of base
|
||||
template <typename IntType>
|
||||
constexpr IntType ceil_to_multiple(IntType n, IntType base) {
|
||||
return (n + base - 1) / base * base;
|
||||
constexpr IntType CeilToMultiple(IntType n, IntType base) {
|
||||
return (n + base - 1) / base * base;
|
||||
}
|
||||
|
||||
// read_little_endian() is our utility to read an integer (signed or unsigned, any size)
|
||||
// from a stream in little-endian order. We swap the byte order after the read if
|
||||
// necessary to return a result with the byte ordering of the compiling machine.
|
||||
template <typename IntType>
|
||||
inline IntType read_little_endian(std::istream& stream) {
|
||||
IntType result;
|
||||
|
||||
if (IsLittleEndian)
|
||||
stream.read(reinterpret_cast<char*>(&result), sizeof(IntType));
|
||||
else
|
||||
{
|
||||
std::uint8_t u[sizeof(IntType)];
|
||||
typename std::make_unsigned<IntType>::type v = 0;
|
||||
|
||||
stream.read(reinterpret_cast<char*>(u), sizeof(IntType));
|
||||
for (std::size_t i = 0; i < sizeof(IntType); ++i)
|
||||
v = (v << 8) | u[sizeof(IntType) - i - 1];
|
||||
|
||||
std::memcpy(&result, &v, sizeof(IntType));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// write_little_endian() is our utility to write an integer (signed or unsigned, any size)
|
||||
// to a stream in little-endian order. We swap the byte order before the write if
|
||||
// necessary to always write in little endian order, independently of the byte
|
||||
// ordering of the compiling machine.
|
||||
template <typename IntType>
|
||||
inline void write_little_endian(std::ostream& stream, IntType value) {
|
||||
|
||||
if (IsLittleEndian)
|
||||
stream.write(reinterpret_cast<const char*>(&value), sizeof(IntType));
|
||||
else
|
||||
{
|
||||
std::uint8_t u[sizeof(IntType)];
|
||||
typename std::make_unsigned<IntType>::type v = value;
|
||||
|
||||
std::size_t i = 0;
|
||||
// if constexpr to silence the warning about shift by 8
|
||||
if constexpr (sizeof(IntType) > 1)
|
||||
{
|
||||
for (; i + 1 < sizeof(IntType); ++i)
|
||||
{
|
||||
u[i] = (std::uint8_t)v;
|
||||
v >>= 8;
|
||||
}
|
||||
}
|
||||
u[i] = (std::uint8_t)v;
|
||||
|
||||
stream.write(reinterpret_cast<char*>(u), sizeof(IntType));
|
||||
}
|
||||
}
|
||||
|
||||
// read_little_endian(s, out, N) : read integers in bulk from a little indian stream.
|
||||
// This reads N integers from stream s and put them in array out.
|
||||
template <typename IntType>
|
||||
inline void read_little_endian(std::istream& stream, IntType* out, std::size_t count) {
|
||||
if (IsLittleEndian)
|
||||
stream.read(reinterpret_cast<char*>(out), sizeof(IntType) * count);
|
||||
else
|
||||
for (std::size_t i = 0; i < count; ++i)
|
||||
out[i] = read_little_endian<IntType>(stream);
|
||||
}
|
||||
|
||||
// write_little_endian(s, values, N) : write integers in bulk to a little indian stream.
|
||||
// This takes N integers from array values and writes them on stream s.
|
||||
template <typename IntType>
|
||||
inline void write_little_endian(std::ostream& stream, const IntType* values, std::size_t count) {
|
||||
if (IsLittleEndian)
|
||||
stream.write(reinterpret_cast<const char*>(values), sizeof(IntType) * count);
|
||||
else
|
||||
for (std::size_t i = 0; i < count; ++i)
|
||||
write_little_endian<IntType>(stream, values[i]);
|
||||
}
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
} // namespace Eval::NNUE
|
||||
|
||||
#endif // #ifndef NNUE_COMMON_H_INCLUDED
|
||||
|
||||
+276
-509
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -23,566 +23,333 @@
|
||||
|
||||
#include "nnue_common.h"
|
||||
#include "nnue_architecture.h"
|
||||
#include "features/index_list.h"
|
||||
|
||||
#include <cstring> // std::memset()
|
||||
|
||||
namespace Stockfish::Eval::NNUE {
|
||||
|
||||
using BiasType = std::int16_t;
|
||||
using WeightType = std::int16_t;
|
||||
using PSQTWeightType = std::int32_t;
|
||||
|
||||
// If vector instructions are enabled, we update and refresh the
|
||||
// accumulator tile by tile such that each tile fits in the CPU's
|
||||
// vector registers.
|
||||
#define VECTOR
|
||||
|
||||
static_assert(PSQTBuckets % 8 == 0,
|
||||
"Per feature PSQT values cannot be processed at granularity lower than 8 at a time.");
|
||||
|
||||
#ifdef USE_AVX512
|
||||
typedef __m512i vec_t;
|
||||
typedef __m256i psqt_vec_t;
|
||||
#define vec_load(a) _mm512_load_si512(a)
|
||||
#define vec_store(a,b) _mm512_store_si512(a,b)
|
||||
#define vec_add_16(a,b) _mm512_add_epi16(a,b)
|
||||
#define vec_sub_16(a,b) _mm512_sub_epi16(a,b)
|
||||
#define vec_mul_16(a,b) _mm512_mullo_epi16(a,b)
|
||||
#define vec_zero() _mm512_setzero_epi32()
|
||||
#define vec_set_16(a) _mm512_set1_epi16(a)
|
||||
#define vec_max_16(a,b) _mm512_max_epi16(a,b)
|
||||
#define vec_min_16(a,b) _mm512_min_epi16(a,b)
|
||||
inline vec_t vec_msb_pack_16(vec_t a, vec_t b){
|
||||
vec_t compacted = _mm512_packs_epi16(_mm512_srli_epi16(a,7),_mm512_srli_epi16(b,7));
|
||||
return _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7), compacted);
|
||||
}
|
||||
#define vec_load_psqt(a) _mm256_load_si256(a)
|
||||
#define vec_store_psqt(a,b) _mm256_store_si256(a,b)
|
||||
#define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b)
|
||||
#define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b)
|
||||
#define vec_zero_psqt() _mm256_setzero_si256()
|
||||
#define NumRegistersSIMD 32
|
||||
#define MaxChunkSize 64
|
||||
|
||||
#elif USE_AVX2
|
||||
typedef __m256i vec_t;
|
||||
typedef __m256i psqt_vec_t;
|
||||
#define vec_load(a) _mm256_load_si256(a)
|
||||
#define vec_store(a,b) _mm256_store_si256(a,b)
|
||||
#define vec_add_16(a,b) _mm256_add_epi16(a,b)
|
||||
#define vec_sub_16(a,b) _mm256_sub_epi16(a,b)
|
||||
#define vec_mul_16(a,b) _mm256_mullo_epi16(a,b)
|
||||
#define vec_zero() _mm256_setzero_si256()
|
||||
#define vec_set_16(a) _mm256_set1_epi16(a)
|
||||
#define vec_max_16(a,b) _mm256_max_epi16(a,b)
|
||||
#define vec_min_16(a,b) _mm256_min_epi16(a,b)
|
||||
inline vec_t vec_msb_pack_16(vec_t a, vec_t b){
|
||||
vec_t compacted = _mm256_packs_epi16(_mm256_srli_epi16(a,7), _mm256_srli_epi16(b,7));
|
||||
return _mm256_permute4x64_epi64(compacted, 0b11011000);
|
||||
}
|
||||
#define vec_load_psqt(a) _mm256_load_si256(a)
|
||||
#define vec_store_psqt(a,b) _mm256_store_si256(a,b)
|
||||
#define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b)
|
||||
#define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b)
|
||||
#define vec_zero_psqt() _mm256_setzero_si256()
|
||||
#define NumRegistersSIMD 16
|
||||
#define MaxChunkSize 32
|
||||
|
||||
#elif USE_SSE2
|
||||
typedef __m128i vec_t;
|
||||
typedef __m128i psqt_vec_t;
|
||||
#define vec_load(a) (*(a))
|
||||
#define vec_store(a,b) *(a)=(b)
|
||||
#define vec_add_16(a,b) _mm_add_epi16(a,b)
|
||||
#define vec_sub_16(a,b) _mm_sub_epi16(a,b)
|
||||
#define vec_mul_16(a,b) _mm_mullo_epi16(a,b)
|
||||
#define vec_zero() _mm_setzero_si128()
|
||||
#define vec_set_16(a) _mm_set1_epi16(a)
|
||||
#define vec_max_16(a,b) _mm_max_epi16(a,b)
|
||||
#define vec_min_16(a,b) _mm_min_epi16(a,b)
|
||||
#define vec_msb_pack_16(a,b) _mm_packs_epi16(_mm_srli_epi16(a,7),_mm_srli_epi16(b,7))
|
||||
#define vec_load_psqt(a) (*(a))
|
||||
#define vec_store_psqt(a,b) *(a)=(b)
|
||||
#define vec_add_psqt_32(a,b) _mm_add_epi32(a,b)
|
||||
#define vec_sub_psqt_32(a,b) _mm_sub_epi32(a,b)
|
||||
#define vec_zero_psqt() _mm_setzero_si128()
|
||||
#define NumRegistersSIMD (Is64Bit ? 16 : 8)
|
||||
#define MaxChunkSize 16
|
||||
|
||||
#elif USE_MMX
|
||||
typedef __m64 vec_t;
|
||||
typedef __m64 psqt_vec_t;
|
||||
#define vec_load(a) (*(a))
|
||||
#define vec_store(a,b) *(a)=(b)
|
||||
#define vec_add_16(a,b) _mm_add_pi16(a,b)
|
||||
#define vec_sub_16(a,b) _mm_sub_pi16(a,b)
|
||||
#define vec_mul_16(a,b) _mm_mullo_pi16(a,b)
|
||||
#define vec_zero() _mm_setzero_si64()
|
||||
#define vec_set_16(a) _mm_set1_pi16(a)
|
||||
inline vec_t vec_max_16(vec_t a,vec_t b){
|
||||
vec_t comparison = _mm_cmpgt_pi16(a,b);
|
||||
return _mm_or_si64(_mm_and_si64(comparison, a), _mm_andnot_si64(comparison, b));
|
||||
}
|
||||
inline vec_t vec_min_16(vec_t a,vec_t b){
|
||||
vec_t comparison = _mm_cmpgt_pi16(a,b);
|
||||
return _mm_or_si64(_mm_and_si64(comparison, b), _mm_andnot_si64(comparison, a));
|
||||
}
|
||||
#define vec_msb_pack_16(a,b) _mm_packs_pi16(_mm_srli_pi16(a,7),_mm_srli_pi16(b,7))
|
||||
#define vec_load_psqt(a) (*(a))
|
||||
#define vec_store_psqt(a,b) *(a)=(b)
|
||||
#define vec_add_psqt_32(a,b) _mm_add_pi32(a,b)
|
||||
#define vec_sub_psqt_32(a,b) _mm_sub_pi32(a,b)
|
||||
#define vec_zero_psqt() _mm_setzero_si64()
|
||||
#define vec_cleanup() _mm_empty()
|
||||
#define NumRegistersSIMD 8
|
||||
#define MaxChunkSize 8
|
||||
|
||||
#elif USE_NEON
|
||||
typedef int16x8_t vec_t;
|
||||
typedef int32x4_t psqt_vec_t;
|
||||
#define vec_load(a) (*(a))
|
||||
#define vec_store(a,b) *(a)=(b)
|
||||
#define vec_add_16(a,b) vaddq_s16(a,b)
|
||||
#define vec_sub_16(a,b) vsubq_s16(a,b)
|
||||
#define vec_mul_16(a,b) vmulq_s16(a,b)
|
||||
#define vec_zero() vec_t{0}
|
||||
#define vec_set_16(a) vdupq_n_s16(a)
|
||||
#define vec_max_16(a,b) vmaxq_s16(a,b)
|
||||
#define vec_min_16(a,b) vminq_s16(a,b)
|
||||
inline vec_t vec_msb_pack_16(vec_t a, vec_t b){
|
||||
const int8x8_t shifta = vshrn_n_s16(a, 7);
|
||||
const int8x8_t shiftb = vshrn_n_s16(b, 7);
|
||||
const int8x16_t compacted = vcombine_s8(shifta,shiftb);
|
||||
return *reinterpret_cast<const vec_t*> (&compacted);
|
||||
}
|
||||
#define vec_load_psqt(a) (*(a))
|
||||
#define vec_store_psqt(a,b) *(a)=(b)
|
||||
#define vec_add_psqt_32(a,b) vaddq_s32(a,b)
|
||||
#define vec_sub_psqt_32(a,b) vsubq_s32(a,b)
|
||||
#define vec_zero_psqt() psqt_vec_t{0}
|
||||
#define NumRegistersSIMD 16
|
||||
#define MaxChunkSize 16
|
||||
|
||||
#else
|
||||
#undef VECTOR
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef VECTOR
|
||||
|
||||
// Compute optimal SIMD register count for feature transformer accumulation.
|
||||
|
||||
// We use __m* types as template arguments, which causes GCC to emit warnings
|
||||
// about losing some attribute information. This is irrelevant to us as we
|
||||
// only take their size, so the following pragma are harmless.
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wignored-attributes"
|
||||
#endif
|
||||
|
||||
template <typename SIMDRegisterType,
|
||||
typename LaneType,
|
||||
int NumLanes,
|
||||
int MaxRegisters>
|
||||
static constexpr int BestRegisterCount()
|
||||
{
|
||||
#define RegisterSize sizeof(SIMDRegisterType)
|
||||
#define LaneSize sizeof(LaneType)
|
||||
|
||||
static_assert(RegisterSize >= LaneSize);
|
||||
static_assert(MaxRegisters <= NumRegistersSIMD);
|
||||
static_assert(MaxRegisters > 0);
|
||||
static_assert(NumRegistersSIMD > 0);
|
||||
static_assert(RegisterSize % LaneSize == 0);
|
||||
static_assert((NumLanes * LaneSize) % RegisterSize == 0);
|
||||
|
||||
const int ideal = (NumLanes * LaneSize) / RegisterSize;
|
||||
if (ideal <= MaxRegisters)
|
||||
return ideal;
|
||||
|
||||
// Look for the largest divisor of the ideal register count that is smaller than MaxRegisters
|
||||
for (int divisor = MaxRegisters; divisor > 1; --divisor)
|
||||
if (ideal % divisor == 0)
|
||||
return divisor;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static constexpr int NumRegs = BestRegisterCount<vec_t, WeightType, TransformedFeatureDimensions, NumRegistersSIMD>();
|
||||
static constexpr int NumPsqtRegs = BestRegisterCount<psqt_vec_t, PSQTWeightType, PSQTBuckets, NumRegistersSIMD>();
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
namespace Eval::NNUE {
|
||||
|
||||
// Input feature converter
|
||||
class FeatureTransformer {
|
||||
|
||||
private:
|
||||
// Number of output dimensions for one side
|
||||
static constexpr IndexType HalfDimensions = TransformedFeatureDimensions;
|
||||
|
||||
#ifdef VECTOR
|
||||
static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2;
|
||||
static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4;
|
||||
static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions");
|
||||
static_assert(PSQTBuckets % PsqtTileHeight == 0, "PsqtTileHeight must divide PSQTBuckets");
|
||||
#endif
|
||||
static constexpr IndexType kHalfDimensions = kTransformedFeatureDimensions;
|
||||
|
||||
public:
|
||||
// Output type
|
||||
using OutputType = TransformedFeatureType;
|
||||
|
||||
// Number of input/output dimensions
|
||||
static constexpr IndexType InputDimensions = FeatureSet::Dimensions;
|
||||
static constexpr IndexType OutputDimensions = HalfDimensions;
|
||||
static constexpr IndexType kInputDimensions = RawFeatures::kDimensions;
|
||||
static constexpr IndexType kOutputDimensions = kHalfDimensions * 2;
|
||||
|
||||
// Size of forward propagation buffer
|
||||
static constexpr std::size_t BufferSize =
|
||||
OutputDimensions * sizeof(OutputType);
|
||||
static constexpr std::size_t kBufferSize =
|
||||
kOutputDimensions * sizeof(OutputType);
|
||||
|
||||
// Hash value embedded in the evaluation file
|
||||
static constexpr std::uint32_t get_hash_value() {
|
||||
return FeatureSet::HashValue ^ (OutputDimensions * 2);
|
||||
static constexpr std::uint32_t GetHashValue() {
|
||||
return RawFeatures::kHashValue ^ kOutputDimensions;
|
||||
}
|
||||
|
||||
// Read network parameters
|
||||
bool read_parameters(std::istream& stream) {
|
||||
|
||||
read_little_endian<BiasType >(stream, biases , HalfDimensions );
|
||||
read_little_endian<WeightType >(stream, weights , HalfDimensions * InputDimensions);
|
||||
read_little_endian<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
|
||||
|
||||
bool ReadParameters(std::istream& stream) {
|
||||
stream.read(reinterpret_cast<char*>(biases_),
|
||||
kHalfDimensions * sizeof(BiasType));
|
||||
stream.read(reinterpret_cast<char*>(weights_),
|
||||
kHalfDimensions * kInputDimensions * sizeof(WeightType));
|
||||
return !stream.fail();
|
||||
}
|
||||
|
||||
// Write network parameters
|
||||
bool write_parameters(std::ostream& stream) const {
|
||||
|
||||
write_little_endian<BiasType >(stream, biases , HalfDimensions );
|
||||
write_little_endian<WeightType >(stream, weights , HalfDimensions * InputDimensions);
|
||||
write_little_endian<PSQTWeightType>(stream, psqtWeights, PSQTBuckets * InputDimensions);
|
||||
|
||||
return !stream.fail();
|
||||
// Proceed with the difference calculation if possible
|
||||
bool UpdateAccumulatorIfPossible(const Position& pos) const {
|
||||
const auto now = pos.state();
|
||||
if (now->accumulator.computed_accumulation) {
|
||||
return true;
|
||||
}
|
||||
const auto prev = now->previous;
|
||||
if (prev && prev->accumulator.computed_accumulation) {
|
||||
UpdateAccumulator(pos);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert input features
|
||||
std::int32_t transform(const Position& pos, OutputType* output, int bucket) const {
|
||||
update_accumulator(pos, WHITE);
|
||||
update_accumulator(pos, BLACK);
|
||||
void Transform(const Position& pos, OutputType* output, bool refresh) const {
|
||||
if (refresh || !UpdateAccumulatorIfPossible(pos)) {
|
||||
RefreshAccumulator(pos);
|
||||
}
|
||||
const auto& accumulation = pos.state()->accumulator.accumulation;
|
||||
|
||||
#if defined(USE_AVX2)
|
||||
constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth;
|
||||
constexpr int kControl = 0b11011000;
|
||||
const __m256i kZero = _mm256_setzero_si256();
|
||||
|
||||
#elif defined(USE_SSSE3)
|
||||
constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth;
|
||||
|
||||
#ifdef USE_SSE41
|
||||
const __m128i kZero = _mm_setzero_si128();
|
||||
#else
|
||||
const __m128i k0x80s = _mm_set1_epi8(-128);
|
||||
#endif
|
||||
|
||||
#elif defined(USE_NEON)
|
||||
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||
const int8x8_t kZero = {0};
|
||||
#endif
|
||||
|
||||
const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()};
|
||||
const auto& accumulation = pos.state()->accumulator.accumulation;
|
||||
const auto& psqtAccumulation = pos.state()->accumulator.psqtAccumulation;
|
||||
for (IndexType p = 0; p < 2; ++p) {
|
||||
const IndexType offset = kHalfDimensions * p;
|
||||
|
||||
const auto psqt = (
|
||||
psqtAccumulation[perspectives[0]][bucket]
|
||||
- psqtAccumulation[perspectives[1]][bucket]
|
||||
) / 2;
|
||||
#if defined(USE_AVX2)
|
||||
auto out = reinterpret_cast<__m256i*>(&output[offset]);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
__m256i sum0 =
|
||||
|
||||
|
||||
for (IndexType p = 0; p < 2; ++p)
|
||||
{
|
||||
const IndexType offset = (HalfDimensions / 2) * p;
|
||||
|
||||
#if defined(VECTOR)
|
||||
|
||||
constexpr IndexType OutputChunkSize = MaxChunkSize;
|
||||
static_assert((HalfDimensions / 2) % OutputChunkSize == 0);
|
||||
constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize;
|
||||
|
||||
vec_t Zero = vec_zero();
|
||||
vec_t One = vec_set_16(127);
|
||||
|
||||
const vec_t* in0 = reinterpret_cast<const vec_t*>(&(accumulation[perspectives[p]][0]));
|
||||
const vec_t* in1 = reinterpret_cast<const vec_t*>(&(accumulation[perspectives[p]][HalfDimensions / 2]));
|
||||
vec_t* out = reinterpret_cast< vec_t*>(output + offset);
|
||||
|
||||
for (IndexType j = 0; j < NumOutputChunks; j += 1)
|
||||
{
|
||||
const vec_t sum0a = vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero);
|
||||
const vec_t sum0b = vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero);
|
||||
const vec_t sum1a = vec_max_16(vec_min_16(in1[j * 2 + 0], One), Zero);
|
||||
const vec_t sum1b = vec_max_16(vec_min_16(in1[j * 2 + 1], One), Zero);
|
||||
|
||||
const vec_t pa = vec_mul_16(sum0a, sum1a);
|
||||
const vec_t pb = vec_mul_16(sum0b, sum1b);
|
||||
|
||||
out[j] = vec_msb_pack_16(pa, pb);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
for (IndexType j = 0; j < HalfDimensions / 2; ++j) {
|
||||
BiasType sum0 = accumulation[static_cast<int>(perspectives[p])][j + 0];
|
||||
BiasType sum1 = accumulation[static_cast<int>(perspectives[p])][j + HalfDimensions / 2];
|
||||
sum0 = std::max<int>(0, std::min<int>(127, sum0));
|
||||
sum1 = std::max<int>(0, std::min<int>(127, sum1));
|
||||
output[offset + j] = static_cast<OutputType>(sum0 * sum1 / 128);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(vec_cleanup)
|
||||
vec_cleanup();
|
||||
#endif
|
||||
|
||||
return psqt;
|
||||
|
||||
} // end of function transform()
|
||||
|
||||
|
||||
|
||||
private:
|
||||
void update_accumulator(const Position& pos, const Color perspective) const {
|
||||
|
||||
// The size must be enough to contain the largest possible update.
|
||||
// That might depend on the feature set and generally relies on the
|
||||
// feature set's update cost calculation to be correct and never
|
||||
// allow updates with more added/removed features than MaxActiveDimensions.
|
||||
|
||||
#ifdef VECTOR
|
||||
// Gcc-10.2 unnecessarily spills AVX2 registers if this array
|
||||
// is defined in the VECTOR code below, once in each branch
|
||||
vec_t acc[NumRegs];
|
||||
psqt_vec_t psqt[NumPsqtRegs];
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
// HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary
|
||||
// compiled with g++ in MSYS2 crashes here because the output memory is not aligned
|
||||
// even though alignas is specified.
|
||||
_mm256_loadu_si256
|
||||
#else
|
||||
_mm256_load_si256
|
||||
#endif
|
||||
|
||||
// Look for a usable accumulator of an earlier position. We keep track
|
||||
// of the estimated gain in terms of features to be added/subtracted.
|
||||
StateInfo *st = pos.state(), *next = nullptr;
|
||||
int gain = FeatureSet::refresh_cost(pos);
|
||||
while (st->previous && !st->accumulator.computed[perspective])
|
||||
{
|
||||
// This governs when a full feature refresh is needed and how many
|
||||
// updates are better than just one full refresh.
|
||||
if ( FeatureSet::requires_refresh(st, perspective)
|
||||
|| (gain -= FeatureSet::update_cost(st) + 1) < 0)
|
||||
break;
|
||||
next = st;
|
||||
st = st->previous;
|
||||
}
|
||||
(&reinterpret_cast<const __m256i*>(
|
||||
accumulation[perspectives[p]][0])[j * 2 + 0]);
|
||||
__m256i sum1 =
|
||||
|
||||
if (st->accumulator.computed[perspective])
|
||||
{
|
||||
if (next == nullptr)
|
||||
return;
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
_mm256_loadu_si256
|
||||
#else
|
||||
_mm256_load_si256
|
||||
#endif
|
||||
|
||||
// Update incrementally in two steps. First, we update the "next"
|
||||
// accumulator. Then, we update the current accumulator (pos.state()).
|
||||
(&reinterpret_cast<const __m256i*>(
|
||||
accumulation[perspectives[p]][0])[j * 2 + 1]);
|
||||
|
||||
// Gather all features to be updated.
|
||||
const Square ksq = pos.square<KING>(perspective);
|
||||
FeatureSet::IndexList removed[2], added[2];
|
||||
FeatureSet::append_changed_indices(
|
||||
ksq, next->dirtyPiece, perspective, removed[0], added[0]);
|
||||
for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous)
|
||||
FeatureSet::append_changed_indices(
|
||||
ksq, st2->dirtyPiece, perspective, removed[1], added[1]);
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
_mm256_storeu_si256
|
||||
#else
|
||||
_mm256_store_si256
|
||||
#endif
|
||||
|
||||
// Mark the accumulators as computed.
|
||||
next->accumulator.computed[perspective] = true;
|
||||
pos.state()->accumulator.computed[perspective] = true;
|
||||
|
||||
// Now update the accumulators listed in states_to_update[], where the last element is a sentinel.
|
||||
StateInfo *states_to_update[3] =
|
||||
{ next, next == pos.state() ? nullptr : pos.state(), nullptr };
|
||||
#ifdef VECTOR
|
||||
for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
|
||||
{
|
||||
// Load accumulator
|
||||
auto accTile = reinterpret_cast<vec_t*>(
|
||||
&st->accumulator.accumulation[perspective][j * TileHeight]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
acc[k] = vec_load(&accTile[k]);
|
||||
|
||||
for (IndexType i = 0; states_to_update[i]; ++i)
|
||||
{
|
||||
// Difference calculation for the deactivated features
|
||||
for (const auto index : removed[i])
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index + j * TileHeight;
|
||||
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
acc[k] = vec_sub_16(acc[k], column[k]);
|
||||
}
|
||||
|
||||
// Difference calculation for the activated features
|
||||
for (const auto index : added[i])
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index + j * TileHeight;
|
||||
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
acc[k] = vec_add_16(acc[k], column[k]);
|
||||
}
|
||||
|
||||
// Store accumulator
|
||||
accTile = reinterpret_cast<vec_t*>(
|
||||
&states_to_update[i]->accumulator.accumulation[perspective][j * TileHeight]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
vec_store(&accTile[k], acc[k]);
|
||||
}
|
||||
(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8(
|
||||
_mm256_packs_epi16(sum0, sum1), kZero), kControl));
|
||||
}
|
||||
|
||||
for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j)
|
||||
{
|
||||
// Load accumulator
|
||||
auto accTilePsqt = reinterpret_cast<psqt_vec_t*>(
|
||||
&st->accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]);
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
psqt[k] = vec_load_psqt(&accTilePsqt[k]);
|
||||
#elif defined(USE_SSSE3)
|
||||
auto out = reinterpret_cast<__m128i*>(&output[offset]);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
__m128i sum0 = _mm_load_si128(&reinterpret_cast<const __m128i*>(
|
||||
accumulation[perspectives[p]][0])[j * 2 + 0]);
|
||||
__m128i sum1 = _mm_load_si128(&reinterpret_cast<const __m128i*>(
|
||||
accumulation[perspectives[p]][0])[j * 2 + 1]);
|
||||
const __m128i packedbytes = _mm_packs_epi16(sum0, sum1);
|
||||
|
||||
for (IndexType i = 0; states_to_update[i]; ++i)
|
||||
{
|
||||
// Difference calculation for the deactivated features
|
||||
for (const auto index : removed[i])
|
||||
{
|
||||
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
|
||||
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]);
|
||||
}
|
||||
_mm_store_si128(&out[j],
|
||||
|
||||
// Difference calculation for the activated features
|
||||
for (const auto index : added[i])
|
||||
{
|
||||
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
|
||||
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
|
||||
}
|
||||
#ifdef USE_SSE41
|
||||
_mm_max_epi8(packedbytes, kZero)
|
||||
#else
|
||||
_mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)
|
||||
#endif
|
||||
|
||||
// Store accumulator
|
||||
accTilePsqt = reinterpret_cast<psqt_vec_t*>(
|
||||
&states_to_update[i]->accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]);
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
vec_store_psqt(&accTilePsqt[k], psqt[k]);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#elif defined(USE_NEON)
|
||||
const auto out = reinterpret_cast<int8x8_t*>(&output[offset]);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
int16x8_t sum = reinterpret_cast<const int16x8_t*>(
|
||||
accumulation[perspectives[p]][0])[j];
|
||||
out[j] = vmax_s8(vqmovn_s16(sum), kZero);
|
||||
}
|
||||
|
||||
#else
|
||||
for (IndexType i = 0; states_to_update[i]; ++i)
|
||||
{
|
||||
std::memcpy(states_to_update[i]->accumulator.accumulation[perspective],
|
||||
st->accumulator.accumulation[perspective],
|
||||
HalfDimensions * sizeof(BiasType));
|
||||
|
||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||
states_to_update[i]->accumulator.psqtAccumulation[perspective][k] = st->accumulator.psqtAccumulation[perspective][k];
|
||||
|
||||
st = states_to_update[i];
|
||||
|
||||
// Difference calculation for the deactivated features
|
||||
for (const auto index : removed[i])
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index;
|
||||
|
||||
for (IndexType j = 0; j < HalfDimensions; ++j)
|
||||
st->accumulator.accumulation[perspective][j] -= weights[offset + j];
|
||||
|
||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||
st->accumulator.psqtAccumulation[perspective][k] -= psqtWeights[index * PSQTBuckets + k];
|
||||
}
|
||||
|
||||
// Difference calculation for the activated features
|
||||
for (const auto index : added[i])
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index;
|
||||
|
||||
for (IndexType j = 0; j < HalfDimensions; ++j)
|
||||
st->accumulator.accumulation[perspective][j] += weights[offset + j];
|
||||
|
||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||
st->accumulator.psqtAccumulation[perspective][k] += psqtWeights[index * PSQTBuckets + k];
|
||||
}
|
||||
for (IndexType j = 0; j < kHalfDimensions; ++j) {
|
||||
BiasType sum = accumulation[static_cast<int>(perspectives[p])][0][j];
|
||||
output[offset + j] = static_cast<OutputType>(
|
||||
std::max<int>(0, std::min<int>(127, sum)));
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Refresh the accumulator
|
||||
auto& accumulator = pos.state()->accumulator;
|
||||
accumulator.computed[perspective] = true;
|
||||
FeatureSet::IndexList active;
|
||||
FeatureSet::append_active_indices(pos, perspective, active);
|
||||
|
||||
#ifdef VECTOR
|
||||
for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j)
|
||||
{
|
||||
auto biasesTile = reinterpret_cast<const vec_t*>(
|
||||
&biases[j * TileHeight]);
|
||||
for (IndexType k = 0; k < NumRegs; ++k)
|
||||
acc[k] = biasesTile[k];
|
||||
|
||||
for (const auto index : active)
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index + j * TileHeight;
|
||||
auto column = reinterpret_cast<const vec_t*>(&weights[offset]);
|
||||
|
||||
for (unsigned k = 0; k < NumRegs; ++k)
|
||||
acc[k] = vec_add_16(acc[k], column[k]);
|
||||
}
|
||||
|
||||
auto accTile = reinterpret_cast<vec_t*>(
|
||||
&accumulator.accumulation[perspective][j * TileHeight]);
|
||||
for (unsigned k = 0; k < NumRegs; k++)
|
||||
vec_store(&accTile[k], acc[k]);
|
||||
}
|
||||
|
||||
for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j)
|
||||
{
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
psqt[k] = vec_zero_psqt();
|
||||
|
||||
for (const auto index : active)
|
||||
{
|
||||
const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight;
|
||||
auto columnPsqt = reinterpret_cast<const psqt_vec_t*>(&psqtWeights[offset]);
|
||||
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
|
||||
}
|
||||
|
||||
auto accTilePsqt = reinterpret_cast<psqt_vec_t*>(
|
||||
&accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]);
|
||||
for (std::size_t k = 0; k < NumPsqtRegs; ++k)
|
||||
vec_store_psqt(&accTilePsqt[k], psqt[k]);
|
||||
}
|
||||
|
||||
#else
|
||||
std::memcpy(accumulator.accumulation[perspective], biases,
|
||||
HalfDimensions * sizeof(BiasType));
|
||||
|
||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||
accumulator.psqtAccumulation[perspective][k] = 0;
|
||||
|
||||
for (const auto index : active)
|
||||
{
|
||||
const IndexType offset = HalfDimensions * index;
|
||||
|
||||
for (IndexType j = 0; j < HalfDimensions; ++j)
|
||||
accumulator.accumulation[perspective][j] += weights[offset + j];
|
||||
|
||||
for (std::size_t k = 0; k < PSQTBuckets; ++k)
|
||||
accumulator.psqtAccumulation[perspective][k] += psqtWeights[index * PSQTBuckets + k];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(USE_MMX)
|
||||
_mm_empty();
|
||||
#endif
|
||||
}
|
||||
|
||||
alignas(CacheLineSize) BiasType biases[HalfDimensions];
|
||||
alignas(CacheLineSize) WeightType weights[HalfDimensions * InputDimensions];
|
||||
alignas(CacheLineSize) PSQTWeightType psqtWeights[InputDimensions * PSQTBuckets];
|
||||
private:
|
||||
// Calculate cumulative value without using difference calculation
|
||||
void RefreshAccumulator(const Position& pos) const {
|
||||
auto& accumulator = pos.state()->accumulator;
|
||||
IndexType i = 0;
|
||||
Features::IndexList active_indices[2];
|
||||
RawFeatures::AppendActiveIndices(pos, kRefreshTriggers[i],
|
||||
active_indices);
|
||||
for (Color perspective : { WHITE, BLACK }) {
|
||||
std::memcpy(accumulator.accumulation[perspective][i], biases_,
|
||||
kHalfDimensions * sizeof(BiasType));
|
||||
for (const auto index : active_indices[perspective]) {
|
||||
const IndexType offset = kHalfDimensions * index;
|
||||
|
||||
#if defined(USE_AVX2)
|
||||
auto accumulation = reinterpret_cast<__m256i*>(
|
||||
&accumulator.accumulation[perspective][i][0]);
|
||||
auto column = reinterpret_cast<const __m256i*>(&weights_[offset]);
|
||||
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
_mm256_storeu_si256(&accumulation[j], _mm256_add_epi16(_mm256_loadu_si256(&accumulation[j]), column[j]));
|
||||
#else
|
||||
accumulation[j] = _mm256_add_epi16(accumulation[j], column[j]);
|
||||
#endif
|
||||
}
|
||||
|
||||
#elif defined(USE_SSE2)
|
||||
auto accumulation = reinterpret_cast<__m128i*>(
|
||||
&accumulator.accumulation[perspective][i][0]);
|
||||
auto column = reinterpret_cast<const __m128i*>(&weights_[offset]);
|
||||
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
accumulation[j] = _mm_add_epi16(accumulation[j], column[j]);
|
||||
}
|
||||
|
||||
#elif defined(USE_NEON)
|
||||
auto accumulation = reinterpret_cast<int16x8_t*>(
|
||||
&accumulator.accumulation[perspective][i][0]);
|
||||
auto column = reinterpret_cast<const int16x8_t*>(&weights_[offset]);
|
||||
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
accumulation[j] = vaddq_s16(accumulation[j], column[j]);
|
||||
}
|
||||
|
||||
#else
|
||||
for (IndexType j = 0; j < kHalfDimensions; ++j) {
|
||||
accumulator.accumulation[perspective][i][j] += weights_[offset + j];
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
accumulator.computed_accumulation = true;
|
||||
accumulator.computed_score = false;
|
||||
}
|
||||
|
||||
// Calculate cumulative value using difference calculation
|
||||
void UpdateAccumulator(const Position& pos) const {
|
||||
const auto prev_accumulator = pos.state()->previous->accumulator;
|
||||
auto& accumulator = pos.state()->accumulator;
|
||||
IndexType i = 0;
|
||||
Features::IndexList removed_indices[2], added_indices[2];
|
||||
bool reset[2];
|
||||
RawFeatures::AppendChangedIndices(pos, kRefreshTriggers[i],
|
||||
removed_indices, added_indices, reset);
|
||||
for (Color perspective : { WHITE, BLACK }) {
|
||||
|
||||
#if defined(USE_AVX2)
|
||||
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||
auto accumulation = reinterpret_cast<__m256i*>(
|
||||
&accumulator.accumulation[perspective][i][0]);
|
||||
|
||||
#elif defined(USE_SSE2)
|
||||
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||
auto accumulation = reinterpret_cast<__m128i*>(
|
||||
&accumulator.accumulation[perspective][i][0]);
|
||||
|
||||
#elif defined(USE_NEON)
|
||||
constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2);
|
||||
auto accumulation = reinterpret_cast<int16x8_t*>(
|
||||
&accumulator.accumulation[perspective][i][0]);
|
||||
#endif
|
||||
|
||||
if (reset[perspective]) {
|
||||
std::memcpy(accumulator.accumulation[perspective][i], biases_,
|
||||
kHalfDimensions * sizeof(BiasType));
|
||||
} else {
|
||||
std::memcpy(accumulator.accumulation[perspective][i],
|
||||
prev_accumulator.accumulation[perspective][i],
|
||||
kHalfDimensions * sizeof(BiasType));
|
||||
// Difference calculation for the deactivated features
|
||||
for (const auto index : removed_indices[perspective]) {
|
||||
const IndexType offset = kHalfDimensions * index;
|
||||
|
||||
#if defined(USE_AVX2)
|
||||
auto column = reinterpret_cast<const __m256i*>(&weights_[offset]);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
accumulation[j] = _mm256_sub_epi16(accumulation[j], column[j]);
|
||||
}
|
||||
|
||||
#elif defined(USE_SSE2)
|
||||
auto column = reinterpret_cast<const __m128i*>(&weights_[offset]);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
accumulation[j] = _mm_sub_epi16(accumulation[j], column[j]);
|
||||
}
|
||||
|
||||
#elif defined(USE_NEON)
|
||||
auto column = reinterpret_cast<const int16x8_t*>(&weights_[offset]);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
accumulation[j] = vsubq_s16(accumulation[j], column[j]);
|
||||
}
|
||||
|
||||
#else
|
||||
for (IndexType j = 0; j < kHalfDimensions; ++j) {
|
||||
accumulator.accumulation[perspective][i][j] -=
|
||||
weights_[offset + j];
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
{ // Difference calculation for the activated features
|
||||
for (const auto index : added_indices[perspective]) {
|
||||
const IndexType offset = kHalfDimensions * index;
|
||||
|
||||
#if defined(USE_AVX2)
|
||||
auto column = reinterpret_cast<const __m256i*>(&weights_[offset]);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
accumulation[j] = _mm256_add_epi16(accumulation[j], column[j]);
|
||||
}
|
||||
|
||||
#elif defined(USE_SSE2)
|
||||
auto column = reinterpret_cast<const __m128i*>(&weights_[offset]);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
accumulation[j] = _mm_add_epi16(accumulation[j], column[j]);
|
||||
}
|
||||
|
||||
#elif defined(USE_NEON)
|
||||
auto column = reinterpret_cast<const int16x8_t*>(&weights_[offset]);
|
||||
for (IndexType j = 0; j < kNumChunks; ++j) {
|
||||
accumulation[j] = vaddq_s16(accumulation[j], column[j]);
|
||||
}
|
||||
|
||||
#else
|
||||
for (IndexType j = 0; j < kHalfDimensions; ++j) {
|
||||
accumulator.accumulation[perspective][i][j] +=
|
||||
weights_[offset + j];
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
accumulator.computed_accumulation = true;
|
||||
accumulator.computed_score = false;
|
||||
}
|
||||
|
||||
using BiasType = std::int16_t;
|
||||
using WeightType = std::int16_t;
|
||||
|
||||
alignas(kCacheLineSize) BiasType biases_[kHalfDimensions];
|
||||
alignas(kCacheLineSize)
|
||||
WeightType weights_[kHalfDimensions * kInputDimensions];
|
||||
};
|
||||
|
||||
} // namespace Stockfish::Eval::NNUE
|
||||
} // namespace Eval::NNUE
|
||||
|
||||
#endif // #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED
|
||||
|
||||
+25
-49
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -24,38 +24,35 @@
|
||||
#include "position.h"
|
||||
#include "thread.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
namespace {
|
||||
|
||||
#define V Value
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
|
||||
// Pawn penalties
|
||||
constexpr Score Backward = S( 6, 19);
|
||||
constexpr Score Doubled = S(11, 51);
|
||||
constexpr Score DoubledEarly = S(17, 7);
|
||||
constexpr Score Isolated = S( 1, 20);
|
||||
constexpr Score WeakLever = S( 2, 57);
|
||||
constexpr Score WeakUnopposed = S(15, 18);
|
||||
constexpr Score Backward = S( 9, 24);
|
||||
constexpr Score Doubled = S(11, 56);
|
||||
constexpr Score Isolated = S( 5, 15);
|
||||
constexpr Score WeakLever = S( 0, 56);
|
||||
constexpr Score WeakUnopposed = S(13, 27);
|
||||
|
||||
// Bonus for blocked pawns at 5th or 6th rank
|
||||
constexpr Score BlockedPawn[2] = { S(-19, -8), S(-7, 3) };
|
||||
constexpr Score BlockedPawn[2] = { S(-11, -4), S(-3, 4) };
|
||||
|
||||
constexpr Score BlockedStorm[RANK_NB] = {
|
||||
S(0, 0), S(0, 0), S(64, 75), S(-3, 14), S(-12, 19), S(-7, 4), S(-10, 5)
|
||||
S(0, 0), S(0, 0), S(76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2)
|
||||
};
|
||||
|
||||
// Connected pawn bonus
|
||||
constexpr int Connected[RANK_NB] = { 0, 3, 7, 7, 15, 54, 86 };
|
||||
constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 };
|
||||
|
||||
// Strength of pawn shelter for our king by [distance from edge][rank].
|
||||
// RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king.
|
||||
constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = {
|
||||
{ V(-2), V(85), V(95), V(53), V(39), V(23), V(25) },
|
||||
{ V(-55), V(64), V(32), V(-55), V(-30), V(-11), V(-61) },
|
||||
{ V(-11), V(75), V(19), V(-6), V(26), V(9), V(-47) },
|
||||
{ V(-41), V(-11), V(-27), V(-58), V(-42), V(-66), V(-163) }
|
||||
{ V( -6), V( 81), V( 93), V( 58), V( 39), V( 18), V( 25) },
|
||||
{ V(-43), V( 61), V( 35), V(-49), V(-29), V(-11), V( -63) },
|
||||
{ V(-10), V( 75), V( 23), V( -2), V( 32), V( 3), V( -45) },
|
||||
{ V(-39), V(-13), V(-29), V(-52), V(-48), V(-67), V(-166) }
|
||||
};
|
||||
|
||||
// Danger of enemy pawns moving toward our king by [distance from edge][rank].
|
||||
@@ -63,18 +60,12 @@ namespace {
|
||||
// is behind our king. Note that UnblockedStorm[0][1-2] accommodate opponent pawn
|
||||
// on edge, likely blocked by our king.
|
||||
constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = {
|
||||
{ V(94), V(-280), V(-170), V(90), V(59), V(47), V(53) },
|
||||
{ V(43), V(-17), V(128), V(39), V(26), V(-17), V(15) },
|
||||
{ V(-9), V(62), V(170), V(34), V(-5), V(-20), V(-11) },
|
||||
{ V(-27), V(-19), V(106), V(10), V(2), V(-13), V(-24) }
|
||||
{ V( 85), V(-289), V(-166), V(97), V(50), V( 45), V( 50) },
|
||||
{ V( 46), V( -25), V( 122), V(45), V(37), V(-10), V( 20) },
|
||||
{ V( -6), V( 51), V( 168), V(34), V(-2), V(-22), V(-14) },
|
||||
{ V(-15), V( -11), V( 101), V( 4), V(11), V(-15), V(-29) }
|
||||
};
|
||||
|
||||
|
||||
// KingOnFile[semi-open Us][semi-open Them] contains bonuses/penalties
|
||||
// for king when the king is on a semi-open or open file.
|
||||
constexpr Score KingOnFile[2][2] = {{ S(-18,11), S(-6,-3) },
|
||||
{ S( 0, 0), S( 5,-4) }};
|
||||
|
||||
#undef S
|
||||
#undef V
|
||||
|
||||
@@ -89,14 +80,13 @@ namespace {
|
||||
|
||||
constexpr Color Them = ~Us;
|
||||
constexpr Direction Up = pawn_push(Us);
|
||||
constexpr Direction Down = -Up;
|
||||
|
||||
Bitboard neighbours, stoppers, support, phalanx, opposed;
|
||||
Bitboard lever, leverPush, blocked;
|
||||
Square s;
|
||||
bool backward, passed, doubled;
|
||||
Score score = SCORE_ZERO;
|
||||
Bitboard b = pos.pieces(Us, PAWN);
|
||||
const Square* pl = pos.squares<PAWN>(Us);
|
||||
|
||||
Bitboard ourPawns = pos.pieces( Us, PAWN);
|
||||
Bitboard theirPawns = pos.pieces(Them, PAWN);
|
||||
@@ -109,10 +99,8 @@ namespace {
|
||||
e->blockedCount += popcount(shift<Up>(ourPawns) & (theirPawns | doubleAttackThem));
|
||||
|
||||
// Loop through all pawns of the current color and score each pawn
|
||||
while (b)
|
||||
while ((s = *pl++) != SQ_NONE)
|
||||
{
|
||||
s = pop_lsb(b);
|
||||
|
||||
assert(pos.piece_on(s) == make_piece(Us, PAWN));
|
||||
|
||||
Rank r = relative_rank(Us, s);
|
||||
@@ -128,13 +116,6 @@ namespace {
|
||||
phalanx = neighbours & rank_bb(s);
|
||||
support = neighbours & rank_bb(s - Up);
|
||||
|
||||
if (doubled)
|
||||
{
|
||||
// Additional doubled penalty if none of their pawns is fixed
|
||||
if (!(ourPawns & shift<Down>(theirPawns | pawn_attacks_bb<Them>(theirPawns))))
|
||||
score -= DoubledEarly;
|
||||
}
|
||||
|
||||
// A pawn is backward when it is behind all pawns of the same color on
|
||||
// the adjacent files and cannot safely advance.
|
||||
backward = !(neighbours & forward_ranks_bb(Them, s + Up))
|
||||
@@ -166,7 +147,7 @@ namespace {
|
||||
if (support | phalanx)
|
||||
{
|
||||
int v = Connected[r] * (2 + bool(phalanx) - bool(opposed))
|
||||
+ 22 * popcount(support);
|
||||
+ 21 * popcount(support);
|
||||
|
||||
score += make_score(v, v * (r - 2) / 4);
|
||||
}
|
||||
@@ -184,14 +165,14 @@ namespace {
|
||||
|
||||
else if (backward)
|
||||
score -= Backward
|
||||
+ WeakUnopposed * !opposed * bool(~(FileABB | FileHBB) & s);
|
||||
+ WeakUnopposed * !opposed;
|
||||
|
||||
if (!support)
|
||||
score -= Doubled * doubled
|
||||
+ WeakLever * more_than_one(lever);
|
||||
|
||||
if (blocked && r >= RANK_5)
|
||||
score += BlockedPawn[r - RANK_5];
|
||||
if (blocked && r > RANK_4)
|
||||
score += BlockedPawn[r-4];
|
||||
}
|
||||
|
||||
return score;
|
||||
@@ -238,7 +219,7 @@ Score Entry::evaluate_shelter(const Position& pos, Square ksq) const {
|
||||
|
||||
Score bonus = make_score(5, 5);
|
||||
|
||||
File center = std::clamp(file_of(ksq), FILE_B, FILE_G);
|
||||
File center = Utility::clamp(file_of(ksq), FILE_B, FILE_G);
|
||||
for (File f = File(center - 1); f <= File(center + 1); ++f)
|
||||
{
|
||||
b = ourPawns & file_bb(f);
|
||||
@@ -256,9 +237,6 @@ Score Entry::evaluate_shelter(const Position& pos, Square ksq) const {
|
||||
bonus -= make_score(UnblockedStorm[d][theirRank], 0);
|
||||
}
|
||||
|
||||
// King On File
|
||||
bonus -= KingOnFile[pos.is_on_semiopen_file(Us, ksq)][pos.is_on_semiopen_file(Them, ksq)];
|
||||
|
||||
return bonus;
|
||||
}
|
||||
|
||||
@@ -291,7 +269,7 @@ Score Entry::do_king_safety(const Position& pos) {
|
||||
if (pawns & attacks_bb<KING>(ksq))
|
||||
minPawnDist = 1;
|
||||
else while (pawns)
|
||||
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(pawns)));
|
||||
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns)));
|
||||
|
||||
return shelter - make_score(0, 16 * minPawnDist);
|
||||
}
|
||||
@@ -301,5 +279,3 @@ template Score Entry::do_king_safety<WHITE>(const Position& pos);
|
||||
template Score Entry::do_king_safety<BLACK>(const Position& pos);
|
||||
|
||||
} // namespace Pawns
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "position.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish::Pawns {
|
||||
namespace Pawns {
|
||||
|
||||
/// Pawns::Entry contains various information about a pawn structure. A lookup
|
||||
/// to the pawn hash table (performed by calling the probe function) returns a
|
||||
@@ -65,6 +65,6 @@ typedef HashTable<Entry, 131072> Table;
|
||||
|
||||
Entry* probe(const Position& pos);
|
||||
|
||||
} // namespace Stockfish::Pawns
|
||||
} // namespace Pawns
|
||||
|
||||
#endif // #ifndef PAWNS_H_INCLUDED
|
||||
|
||||
+137
-88
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -34,8 +34,6 @@
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
namespace Zobrist {
|
||||
|
||||
Key psq[PIECE_NB][SQUARE_NB];
|
||||
@@ -73,14 +71,12 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) {
|
||||
<< std::setfill(' ') << std::dec << "\nCheckers: ";
|
||||
|
||||
for (Bitboard b = pos.checkers(); b; )
|
||||
os << UCI::square(pop_lsb(b)) << " ";
|
||||
os << UCI::square(pop_lsb(&b)) << " ";
|
||||
|
||||
if ( int(Tablebases::MaxCardinality) >= popcount(pos.pieces())
|
||||
&& !pos.can_castle(ANY_CASTLING))
|
||||
{
|
||||
StateInfo st;
|
||||
ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize);
|
||||
|
||||
Position p;
|
||||
p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread());
|
||||
Tablebases::ProbeState s1, s2;
|
||||
@@ -199,8 +195,12 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
|
||||
|
||||
std::memset(this, 0, sizeof(Position));
|
||||
std::memset(si, 0, sizeof(StateInfo));
|
||||
std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE);
|
||||
st = si;
|
||||
|
||||
// Each piece on board gets a unique ID used to track the piece later
|
||||
PieceId piece_id, next_piece_id = PIECE_ID_ZERO;
|
||||
|
||||
ss >> std::noskipws;
|
||||
|
||||
// 1. Piece placement
|
||||
@@ -212,8 +212,21 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th
|
||||
else if (token == '/')
|
||||
sq += 2 * SOUTH;
|
||||
|
||||
else if ((idx = PieceToChar.find(token)) != string::npos) {
|
||||
put_piece(Piece(idx), sq);
|
||||
else if ((idx = PieceToChar.find(token)) != string::npos)
|
||||
{
|
||||
auto pc = Piece(idx);
|
||||
put_piece(pc, sq);
|
||||
|
||||
if (Eval::useNNUE)
|
||||
{
|
||||
// Kings get a fixed ID, other pieces get ID in order of placement
|
||||
piece_id =
|
||||
(idx == W_KING) ? PIECE_ID_WKING :
|
||||
(idx == B_KING) ? PIECE_ID_BKING :
|
||||
next_piece_id++;
|
||||
evalList.put_piece(piece_id, sq, pc);
|
||||
}
|
||||
|
||||
++sq;
|
||||
}
|
||||
}
|
||||
@@ -305,7 +318,7 @@ void Position::set_castling_right(Color c, Square rfrom) {
|
||||
Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1);
|
||||
Square rto = relative_square(c, cr & KING_SIDE ? SQ_F1 : SQ_D1);
|
||||
|
||||
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto))
|
||||
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
|
||||
& ~(kfrom | rfrom);
|
||||
}
|
||||
|
||||
@@ -344,7 +357,7 @@ void Position::set_state(StateInfo* si) const {
|
||||
|
||||
for (Bitboard b = pieces(); b; )
|
||||
{
|
||||
Square s = pop_lsb(b);
|
||||
Square s = pop_lsb(&b);
|
||||
Piece pc = piece_on(s);
|
||||
si->key ^= Zobrist::psq[pc][s];
|
||||
|
||||
@@ -395,7 +408,7 @@ Position& Position::set(const string& code, Color c, StateInfo* si) {
|
||||
/// Position::fen() returns a FEN representation of the position. In case of
|
||||
/// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function.
|
||||
|
||||
string Position::fen() const {
|
||||
const string Position::fen() const {
|
||||
|
||||
int emptyCnt;
|
||||
std::ostringstream ss;
|
||||
@@ -461,7 +474,7 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners
|
||||
|
||||
while (snipers)
|
||||
{
|
||||
Square sniperSq = pop_lsb(snipers);
|
||||
Square sniperSq = pop_lsb(&snipers);
|
||||
Bitboard b = between_bb(s, sniperSq) & occupancy;
|
||||
|
||||
if (b && !more_than_one(b))
|
||||
@@ -505,7 +518,7 @@ bool Position::legal(Move m) const {
|
||||
// En passant captures are a tricky special case. Because they are rather
|
||||
// uncommon, we do it simply by testing whether the king is attacked after
|
||||
// the move is made.
|
||||
if (type_of(m) == EN_PASSANT)
|
||||
if (type_of(m) == ENPASSANT)
|
||||
{
|
||||
Square ksq = square<KING>(us);
|
||||
Square capsq = to - pawn_push(us);
|
||||
@@ -533,20 +546,22 @@ bool Position::legal(Move m) const {
|
||||
if (attackers_to(s) & pieces(~us))
|
||||
return false;
|
||||
|
||||
// In case of Chess960, verify if the Rook blocks some checks
|
||||
// In case of Chess960, verify that when moving the castling rook we do
|
||||
// not discover some hidden checker.
|
||||
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
|
||||
return !chess960 || !(blockers_for_king(us) & to_sq(m));
|
||||
return !chess960
|
||||
|| !(attacks_bb<ROOK>(to, pieces() ^ to_sq(m)) & pieces(~us, ROOK, QUEEN));
|
||||
}
|
||||
|
||||
// If the moving piece is a king, check whether the destination square is
|
||||
// attacked by the opponent.
|
||||
if (type_of(piece_on(from)) == KING)
|
||||
return !(attackers_to(to, pieces() ^ from) & pieces(~us));
|
||||
return !(attackers_to(to) & pieces(~us));
|
||||
|
||||
// A non-king move is legal if and only if it is not pinned or it
|
||||
// is moving along the ray towards or away from the king.
|
||||
return !(blockers_for_king(us) & from)
|
||||
|| aligned(from, to, square<KING>(us));
|
||||
return !(blockers_for_king(us) & from)
|
||||
|| aligned(from, to, square<KING>(us));
|
||||
}
|
||||
|
||||
|
||||
@@ -562,10 +577,8 @@ bool Position::pseudo_legal(const Move m) const {
|
||||
Piece pc = moved_piece(m);
|
||||
|
||||
// Use a slower but simpler function for uncommon cases
|
||||
// yet we skip the legality check of MoveList<LEGAL>().
|
||||
if (type_of(m) != NORMAL)
|
||||
return checkers() ? MoveList< EVASIONS>(*this).contains(m)
|
||||
: MoveList<NON_EVASIONS>(*this).contains(m);
|
||||
return MoveList<LEGAL>(*this).contains(m);
|
||||
|
||||
// Is not a promotion, so promotion piece must be empty
|
||||
if (promotion_type(m) - KNIGHT != NO_PIECE_TYPE)
|
||||
@@ -610,8 +623,8 @@ bool Position::pseudo_legal(const Move m) const {
|
||||
if (more_than_one(checkers()))
|
||||
return false;
|
||||
|
||||
// Our move must be a blocking interposition or a capture of the checking piece
|
||||
if (!(between_bb(square<KING>(us), lsb(checkers())) & to))
|
||||
// Our move must be a blocking evasion or a capture of the checking piece
|
||||
if (!((between_bb(lsb(checkers()), square<KING>(us)) | checkers()) & to))
|
||||
return false;
|
||||
}
|
||||
// In case of king moves under check we have to remove king so as to catch
|
||||
@@ -655,7 +668,7 @@ bool Position::gives_check(Move m) const {
|
||||
// of direct checks and ordinary discovered check, so the only case we
|
||||
// need to handle is the unusual case of a discovered check through
|
||||
// the captured pawn.
|
||||
case EN_PASSANT:
|
||||
case ENPASSANT:
|
||||
{
|
||||
Square capsq = make_square(file_of(to), rank_of(from));
|
||||
Bitboard b = (pieces() ^ from ^ capsq) | to;
|
||||
@@ -663,15 +676,19 @@ bool Position::gives_check(Move m) const {
|
||||
return (attacks_bb< ROOK>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK))
|
||||
| (attacks_bb<BISHOP>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP));
|
||||
}
|
||||
default: //CASTLING
|
||||
case CASTLING:
|
||||
{
|
||||
// Castling is encoded as 'king captures the rook'
|
||||
Square ksq = square<KING>(~sideToMove);
|
||||
Square rto = relative_square(sideToMove, to > from ? SQ_F1 : SQ_D1);
|
||||
Square kfrom = from;
|
||||
Square rfrom = to; // Castling is encoded as 'king captures the rook'
|
||||
Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1);
|
||||
Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1);
|
||||
|
||||
return (attacks_bb<ROOK>(rto) & ksq)
|
||||
&& (attacks_bb<ROOK>(rto, pieces() ^ from ^ to) & ksq);
|
||||
return (attacks_bb<ROOK>(rto) & square<KING>(~sideToMove))
|
||||
&& (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -702,8 +719,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
++st->pliesFromNull;
|
||||
|
||||
// Used by NNUE
|
||||
st->accumulator.computed[WHITE] = false;
|
||||
st->accumulator.computed[BLACK] = false;
|
||||
st->accumulator.computed_accumulation = false;
|
||||
st->accumulator.computed_score = false;
|
||||
PieceId dp0 = PIECE_ID_NONE;
|
||||
PieceId dp1 = PIECE_ID_NONE;
|
||||
auto& dp = st->dirtyPiece;
|
||||
dp.dirty_num = 1;
|
||||
|
||||
@@ -712,7 +731,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
Piece pc = piece_on(from);
|
||||
Piece captured = type_of(m) == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to);
|
||||
Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to);
|
||||
|
||||
assert(color_of(pc) == us);
|
||||
assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us));
|
||||
@@ -738,7 +757,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
// update non-pawn material.
|
||||
if (type_of(captured) == PAWN)
|
||||
{
|
||||
if (type_of(m) == EN_PASSANT)
|
||||
if (type_of(m) == ENPASSANT)
|
||||
{
|
||||
capsq -= pawn_push(us);
|
||||
|
||||
@@ -756,16 +775,18 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
|
||||
if (Eval::useNNUE)
|
||||
{
|
||||
dp.dirty_num = 2; // 1 piece moved, 1 piece captured
|
||||
dp.piece[1] = captured;
|
||||
dp.from[1] = capsq;
|
||||
dp.to[1] = SQ_NONE;
|
||||
dp.dirty_num = 2; // 2 pieces moved
|
||||
dp1 = piece_id_on(capsq);
|
||||
dp.pieceId[1] = dp1;
|
||||
dp.old_piece[1] = evalList.piece_with_id(dp1);
|
||||
evalList.put_piece(dp1, capsq, NO_PIECE);
|
||||
dp.new_piece[1] = evalList.piece_with_id(dp1);
|
||||
}
|
||||
|
||||
// Update board and piece lists
|
||||
remove_piece(capsq);
|
||||
|
||||
if (type_of(m) == EN_PASSANT)
|
||||
if (type_of(m) == ENPASSANT)
|
||||
board[capsq] = NO_PIECE;
|
||||
|
||||
// Update material hash key and prefetch access to materialTable
|
||||
@@ -800,9 +821,11 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
{
|
||||
if (Eval::useNNUE)
|
||||
{
|
||||
dp.piece[0] = pc;
|
||||
dp.from[0] = from;
|
||||
dp.to[0] = to;
|
||||
dp0 = piece_id_on(from);
|
||||
dp.pieceId[0] = dp0;
|
||||
dp.old_piece[0] = evalList.piece_with_id(dp0);
|
||||
evalList.put_piece(dp0, to, pc);
|
||||
dp.new_piece[0] = evalList.piece_with_id(dp0);
|
||||
}
|
||||
|
||||
move_piece(from, to);
|
||||
@@ -811,7 +834,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
// If the moving piece is a pawn do some special extra work
|
||||
if (type_of(pc) == PAWN)
|
||||
{
|
||||
// Set en passant square if the moved pawn can be captured
|
||||
// Set en-passant square if the moved pawn can be captured
|
||||
if ( (int(to) ^ int(from)) == 16
|
||||
&& (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN)))
|
||||
{
|
||||
@@ -831,12 +854,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
|
||||
if (Eval::useNNUE)
|
||||
{
|
||||
// Promoting pawn to SQ_NONE, promoted piece from SQ_NONE
|
||||
dp.to[0] = SQ_NONE;
|
||||
dp.piece[dp.dirty_num] = promotion;
|
||||
dp.from[dp.dirty_num] = SQ_NONE;
|
||||
dp.to[dp.dirty_num] = to;
|
||||
dp.dirty_num++;
|
||||
dp0 = piece_id_on(to);
|
||||
evalList.put_piece(dp0, to, promotion);
|
||||
dp.new_piece[0] = evalList.piece_with_id(dp0);
|
||||
}
|
||||
|
||||
// Update hash keys
|
||||
@@ -930,11 +950,17 @@ void Position::undo_move(Move m) {
|
||||
{
|
||||
move_piece(to, from); // Put the piece back at the source square
|
||||
|
||||
if (Eval::useNNUE)
|
||||
{
|
||||
PieceId dp0 = st->dirtyPiece.pieceId[0];
|
||||
evalList.put_piece(dp0, from, pc);
|
||||
}
|
||||
|
||||
if (st->capturedPiece)
|
||||
{
|
||||
Square capsq = to;
|
||||
|
||||
if (type_of(m) == EN_PASSANT)
|
||||
if (type_of(m) == ENPASSANT)
|
||||
{
|
||||
capsq -= pawn_push(us);
|
||||
|
||||
@@ -946,6 +972,14 @@ void Position::undo_move(Move m) {
|
||||
}
|
||||
|
||||
put_piece(st->capturedPiece, capsq); // Restore the captured piece
|
||||
|
||||
if (Eval::useNNUE)
|
||||
{
|
||||
PieceId dp1 = st->dirtyPiece.pieceId[1];
|
||||
assert(evalList.piece_with_id(dp1).from[WHITE] == PS_NONE);
|
||||
assert(evalList.piece_with_id(dp1).from[BLACK] == PS_NONE);
|
||||
evalList.put_piece(dp1, capsq, st->capturedPiece);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -967,16 +1001,32 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ
|
||||
rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
|
||||
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
|
||||
|
||||
if (Do && Eval::useNNUE)
|
||||
if (Eval::useNNUE)
|
||||
{
|
||||
PieceId dp0, dp1;
|
||||
auto& dp = st->dirtyPiece;
|
||||
dp.piece[0] = make_piece(us, KING);
|
||||
dp.from[0] = from;
|
||||
dp.to[0] = to;
|
||||
dp.piece[1] = make_piece(us, ROOK);
|
||||
dp.from[1] = rfrom;
|
||||
dp.to[1] = rto;
|
||||
dp.dirty_num = 2;
|
||||
dp.dirty_num = 2; // 2 pieces moved
|
||||
|
||||
if (Do)
|
||||
{
|
||||
dp0 = piece_id_on(from);
|
||||
dp1 = piece_id_on(rfrom);
|
||||
dp.pieceId[0] = dp0;
|
||||
dp.old_piece[0] = evalList.piece_with_id(dp0);
|
||||
evalList.put_piece(dp0, to, make_piece(us, KING));
|
||||
dp.new_piece[0] = evalList.piece_with_id(dp0);
|
||||
dp.pieceId[1] = dp1;
|
||||
dp.old_piece[1] = evalList.piece_with_id(dp1);
|
||||
evalList.put_piece(dp1, rto, make_piece(us, ROOK));
|
||||
dp.new_piece[1] = evalList.piece_with_id(dp1);
|
||||
}
|
||||
else
|
||||
{
|
||||
dp0 = piece_id_on(to);
|
||||
dp1 = piece_id_on(rto);
|
||||
evalList.put_piece(dp0, from, make_piece(us, KING));
|
||||
evalList.put_piece(dp1, rfrom, make_piece(us, ROOK));
|
||||
}
|
||||
}
|
||||
|
||||
// Remove both pieces first since squares could overlap in Chess960
|
||||
@@ -988,7 +1038,7 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ
|
||||
}
|
||||
|
||||
|
||||
/// Position::do_null_move() is used to do a "null move": it flips
|
||||
/// Position::do(undo)_null_move() is used to do(undo) a "null move": it flips
|
||||
/// the side to move without executing any move on the board.
|
||||
|
||||
void Position::do_null_move(StateInfo& newSt) {
|
||||
@@ -996,16 +1046,17 @@ void Position::do_null_move(StateInfo& newSt) {
|
||||
assert(!checkers());
|
||||
assert(&newSt != st);
|
||||
|
||||
std::memcpy(&newSt, st, offsetof(StateInfo, accumulator));
|
||||
if (Eval::useNNUE)
|
||||
{
|
||||
std::memcpy(&newSt, st, sizeof(StateInfo));
|
||||
st->accumulator.computed_score = false;
|
||||
}
|
||||
else
|
||||
std::memcpy(&newSt, st, offsetof(StateInfo, accumulator));
|
||||
|
||||
newSt.previous = st;
|
||||
st = &newSt;
|
||||
|
||||
st->dirtyPiece.dirty_num = 0;
|
||||
st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator()
|
||||
st->accumulator.computed[WHITE] = false;
|
||||
st->accumulator.computed[BLACK] = false;
|
||||
|
||||
if (st->epSquare != SQ_NONE)
|
||||
{
|
||||
st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
|
||||
@@ -1013,9 +1064,9 @@ void Position::do_null_move(StateInfo& newSt) {
|
||||
}
|
||||
|
||||
st->key ^= Zobrist::side;
|
||||
++st->rule50;
|
||||
prefetch(TT.first_entry(key()));
|
||||
prefetch(TT.first_entry(st->key));
|
||||
|
||||
++st->rule50;
|
||||
st->pliesFromNull = 0;
|
||||
|
||||
sideToMove = ~sideToMove;
|
||||
@@ -1027,9 +1078,6 @@ void Position::do_null_move(StateInfo& newSt) {
|
||||
assert(pos_is_ok());
|
||||
}
|
||||
|
||||
|
||||
/// Position::undo_null_move() must be used to undo a "null move"
|
||||
|
||||
void Position::undo_null_move() {
|
||||
|
||||
assert(!checkers());
|
||||
@@ -1041,7 +1089,7 @@ void Position::undo_null_move() {
|
||||
|
||||
/// Position::key_after() computes the new hash key after the given move. Needed
|
||||
/// for speculative prefetch. It doesn't recognize special moves like castling,
|
||||
/// en passant and promotions.
|
||||
/// en-passant and promotions.
|
||||
|
||||
Key Position::key_after(Move m) const {
|
||||
|
||||
@@ -1066,7 +1114,7 @@ bool Position::see_ge(Move m, Value threshold) const {
|
||||
|
||||
assert(is_ok(m));
|
||||
|
||||
// Only deal with normal moves, assume others pass a simple SEE
|
||||
// Only deal with normal moves, assume others pass a simple see
|
||||
if (type_of(m) != NORMAL)
|
||||
return VALUE_ZERO >= threshold;
|
||||
|
||||
@@ -1080,9 +1128,8 @@ bool Position::see_ge(Move m, Value threshold) const {
|
||||
if (swap <= 0)
|
||||
return true;
|
||||
|
||||
assert(color_of(piece_on(from)) == sideToMove);
|
||||
Bitboard occupied = pieces() ^ from ^ to;
|
||||
Color stm = sideToMove;
|
||||
Color stm = color_of(piece_on(from));
|
||||
Bitboard attackers = attackers_to(to, occupied);
|
||||
Bitboard stmAttackers, bb;
|
||||
int res = 1;
|
||||
@@ -1096,10 +1143,10 @@ bool Position::see_ge(Move m, Value threshold) const {
|
||||
if (!(stmAttackers = attackers & pieces(stm)))
|
||||
break;
|
||||
|
||||
// Don't allow pinned pieces to attack as long as there are
|
||||
// pinners on their original square.
|
||||
if (pinners(~stm) & occupied)
|
||||
stmAttackers &= ~blockers_for_king(stm);
|
||||
// Don't allow pinned pieces to attack (except the king) as long as
|
||||
// there are pinners on their original square.
|
||||
if (st->pinners[~stm] & occupied)
|
||||
stmAttackers &= ~st->blockersForKing[stm];
|
||||
|
||||
if (!stmAttackers)
|
||||
break;
|
||||
@@ -1113,7 +1160,7 @@ bool Position::see_ge(Move m, Value threshold) const {
|
||||
if ((swap = PawnValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
occupied ^= lsb(bb);
|
||||
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
|
||||
}
|
||||
|
||||
@@ -1122,7 +1169,7 @@ bool Position::see_ge(Move m, Value threshold) const {
|
||||
if ((swap = KnightValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
occupied ^= lsb(bb);
|
||||
}
|
||||
|
||||
else if ((bb = stmAttackers & pieces(BISHOP)))
|
||||
@@ -1130,7 +1177,7 @@ bool Position::see_ge(Move m, Value threshold) const {
|
||||
if ((swap = BishopValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
occupied ^= lsb(bb);
|
||||
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
|
||||
}
|
||||
|
||||
@@ -1139,7 +1186,7 @@ bool Position::see_ge(Move m, Value threshold) const {
|
||||
if ((swap = RookValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
occupied ^= lsb(bb);
|
||||
attackers |= attacks_bb<ROOK>(to, occupied) & pieces(ROOK, QUEEN);
|
||||
}
|
||||
|
||||
@@ -1148,7 +1195,7 @@ bool Position::see_ge(Move m, Value threshold) const {
|
||||
if ((swap = QueenValueMg - swap) < res)
|
||||
break;
|
||||
|
||||
occupied ^= least_significant_square_bb(bb);
|
||||
occupied ^= lsb(bb);
|
||||
attackers |= (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN))
|
||||
| (attacks_bb<ROOK >(to, occupied) & pieces(ROOK , QUEEN));
|
||||
}
|
||||
@@ -1222,7 +1269,7 @@ bool Position::has_game_cycle(int ply) const {
|
||||
Square s1 = from_sq(move);
|
||||
Square s2 = to_sq(move);
|
||||
|
||||
if (!((between_bb(s1, s2) ^ s2) & pieces()))
|
||||
if (!(between_bb(s1, s2) & pieces()))
|
||||
{
|
||||
if (ply > i)
|
||||
return true;
|
||||
@@ -1319,17 +1366,21 @@ bool Position::pos_is_ok() const {
|
||||
assert(0 && "pos_is_ok: Bitboards");
|
||||
|
||||
StateInfo si = *st;
|
||||
ASSERT_ALIGNED(&si, Eval::NNUE::CacheLineSize);
|
||||
|
||||
set_state(&si);
|
||||
if (std::memcmp(&si, st, sizeof(StateInfo)))
|
||||
assert(0 && "pos_is_ok: State");
|
||||
|
||||
for (Piece pc : Pieces)
|
||||
{
|
||||
if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))
|
||||
|| pieceCount[pc] != std::count(board, board + SQUARE_NB, pc))
|
||||
assert(0 && "pos_is_ok: Pieces");
|
||||
|
||||
for (int i = 0; i < pieceCount[pc]; ++i)
|
||||
if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i)
|
||||
assert(0 && "pos_is_ok: Index");
|
||||
}
|
||||
|
||||
for (Color c : { WHITE, BLACK })
|
||||
for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE})
|
||||
{
|
||||
@@ -1344,5 +1395,3 @@ bool Position::pos_is_ok() const {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+76
-41
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -26,12 +26,10 @@
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "evaluate.h"
|
||||
#include "psqt.h"
|
||||
#include "types.h"
|
||||
|
||||
#include "nnue/nnue_accumulator.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
/// StateInfo struct stores information needed to restore a Position object to
|
||||
/// its previous state when we retract a move. Whenever a move is made on the
|
||||
@@ -51,11 +49,11 @@ struct StateInfo {
|
||||
// Not copied when making a move (will be recomputed anyhow)
|
||||
Key key;
|
||||
Bitboard checkersBB;
|
||||
Piece capturedPiece;
|
||||
StateInfo* previous;
|
||||
Bitboard blockersForKing[COLOR_NB];
|
||||
Bitboard pinners[COLOR_NB];
|
||||
Bitboard checkSquares[PIECE_TYPE_NB];
|
||||
Piece capturedPiece;
|
||||
int repetition;
|
||||
|
||||
// Used by NNUE
|
||||
@@ -88,7 +86,7 @@ public:
|
||||
// FEN string input/output
|
||||
Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th);
|
||||
Position& set(const std::string& code, Color c, StateInfo* si);
|
||||
std::string fen() const;
|
||||
const std::string fen() const;
|
||||
|
||||
// Position representation
|
||||
Bitboard pieces(PieceType pt) const;
|
||||
@@ -101,6 +99,7 @@ public:
|
||||
bool empty(Square s) const;
|
||||
template<PieceType Pt> int count(Color c) const;
|
||||
template<PieceType Pt> int count() const;
|
||||
template<PieceType Pt> const Square* squares(Color c) const;
|
||||
template<PieceType Pt> Square square(Color c) const;
|
||||
bool is_on_semiopen_file(Color c, Square s) const;
|
||||
|
||||
@@ -114,19 +113,20 @@ public:
|
||||
Bitboard checkers() const;
|
||||
Bitboard blockers_for_king(Color c) const;
|
||||
Bitboard check_squares(PieceType pt) const;
|
||||
Bitboard pinners(Color c) const;
|
||||
bool is_discovery_check_on_king(Color c, Move m) const;
|
||||
|
||||
// Attacks to/from a given square
|
||||
Bitboard attackers_to(Square s) const;
|
||||
Bitboard attackers_to(Square s, Bitboard occupied) const;
|
||||
Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const;
|
||||
template<PieceType Pt> Bitboard attacks_by(Color c) const;
|
||||
|
||||
// Properties of moves
|
||||
bool legal(Move m) const;
|
||||
bool pseudo_legal(const Move m) const;
|
||||
bool capture(Move m) const;
|
||||
bool capture_or_promotion(Move m) const;
|
||||
bool gives_check(Move m) const;
|
||||
bool advanced_pawn_push(Move m) const;
|
||||
Piece moved_piece(Move m) const;
|
||||
Piece captured_piece() const;
|
||||
|
||||
@@ -170,9 +170,7 @@ public:
|
||||
|
||||
// Used by NNUE
|
||||
StateInfo* state() const;
|
||||
|
||||
void put_piece(Piece pc, Square s);
|
||||
void remove_piece(Square s);
|
||||
const EvalList* eval_list() const;
|
||||
|
||||
private:
|
||||
// Initialization helpers (used while setting up a position)
|
||||
@@ -181,26 +179,40 @@ private:
|
||||
void set_check_info(StateInfo* si) const;
|
||||
|
||||
// Other helpers
|
||||
void put_piece(Piece pc, Square s);
|
||||
void remove_piece(Square s);
|
||||
void move_piece(Square from, Square to);
|
||||
template<bool Do>
|
||||
void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto);
|
||||
|
||||
// ID of a piece on a given square
|
||||
PieceId piece_id_on(Square sq) const;
|
||||
|
||||
// Data members
|
||||
Piece board[SQUARE_NB];
|
||||
Bitboard byTypeBB[PIECE_TYPE_NB];
|
||||
Bitboard byColorBB[COLOR_NB];
|
||||
int pieceCount[PIECE_NB];
|
||||
Square pieceList[PIECE_NB][16];
|
||||
int index[SQUARE_NB];
|
||||
int castlingRightsMask[SQUARE_NB];
|
||||
Square castlingRookSquare[CASTLING_RIGHT_NB];
|
||||
Bitboard castlingPath[CASTLING_RIGHT_NB];
|
||||
Thread* thisThread;
|
||||
StateInfo* st;
|
||||
int gamePly;
|
||||
Color sideToMove;
|
||||
Score psq;
|
||||
Thread* thisThread;
|
||||
StateInfo* st;
|
||||
bool chess960;
|
||||
|
||||
// List of pieces used in NNUE evaluation function
|
||||
EvalList evalList;
|
||||
};
|
||||
|
||||
namespace PSQT {
|
||||
extern Score psq[PIECE_NB][SQUARE_NB];
|
||||
}
|
||||
|
||||
extern std::ostream& operator<<(std::ostream& os, const Position& pos);
|
||||
|
||||
inline Color Position::side_to_move() const {
|
||||
@@ -248,9 +260,13 @@ template<PieceType Pt> inline int Position::count() const {
|
||||
return count<Pt>(WHITE) + count<Pt>(BLACK);
|
||||
}
|
||||
|
||||
template<PieceType Pt> inline const Square* Position::squares(Color c) const {
|
||||
return pieceList[make_piece(c, Pt)];
|
||||
}
|
||||
|
||||
template<PieceType Pt> inline Square Position::square(Color c) const {
|
||||
assert(count<Pt>(c) == 1);
|
||||
return lsb(pieces(c, Pt));
|
||||
assert(pieceCount[make_piece(c, Pt)] == 1);
|
||||
return squares<Pt>(c)[0];
|
||||
}
|
||||
|
||||
inline Square Position::ep_square() const {
|
||||
@@ -285,22 +301,6 @@ inline Bitboard Position::attackers_to(Square s) const {
|
||||
return attackers_to(s, pieces());
|
||||
}
|
||||
|
||||
template<PieceType Pt>
|
||||
inline Bitboard Position::attacks_by(Color c) const {
|
||||
|
||||
if constexpr (Pt == PAWN)
|
||||
return c == WHITE ? pawn_attacks_bb<WHITE>(pieces(WHITE, PAWN))
|
||||
: pawn_attacks_bb<BLACK>(pieces(BLACK, PAWN));
|
||||
else
|
||||
{
|
||||
Bitboard threats = 0;
|
||||
Bitboard attackers = pieces(c, Pt);
|
||||
while (attackers)
|
||||
threats |= attacks_bb<Pt>(pop_lsb(attackers), pieces());
|
||||
return threats;
|
||||
}
|
||||
}
|
||||
|
||||
inline Bitboard Position::checkers() const {
|
||||
return st->checkersBB;
|
||||
}
|
||||
@@ -309,25 +309,29 @@ inline Bitboard Position::blockers_for_king(Color c) const {
|
||||
return st->blockersForKing[c];
|
||||
}
|
||||
|
||||
inline Bitboard Position::pinners(Color c) const {
|
||||
return st->pinners[c];
|
||||
}
|
||||
|
||||
inline Bitboard Position::check_squares(PieceType pt) const {
|
||||
return st->checkSquares[pt];
|
||||
}
|
||||
|
||||
inline bool Position::is_discovery_check_on_king(Color c, Move m) const {
|
||||
return st->blockersForKing[c] & from_sq(m);
|
||||
}
|
||||
|
||||
inline bool Position::pawn_passed(Color c, Square s) const {
|
||||
return !(pieces(~c, PAWN) & passed_pawn_span(c, s));
|
||||
}
|
||||
|
||||
inline bool Position::advanced_pawn_push(Move m) const {
|
||||
return type_of(moved_piece(m)) == PAWN
|
||||
&& relative_rank(sideToMove, to_sq(m)) > RANK_5;
|
||||
}
|
||||
|
||||
inline int Position::pawns_on_same_color_squares(Color c, Square s) const {
|
||||
return popcount(pieces(c, PAWN) & ((DarkSquares & s) ? DarkSquares : ~DarkSquares));
|
||||
}
|
||||
|
||||
inline Key Position::key() const {
|
||||
return st->rule50 < 14 ? st->key
|
||||
: st->key ^ make_key((st->rule50 - 14) / 8);
|
||||
return st->key;
|
||||
}
|
||||
|
||||
inline Key Position::pawn_key() const {
|
||||
@@ -368,10 +372,15 @@ inline bool Position::is_chess960() const {
|
||||
return chess960;
|
||||
}
|
||||
|
||||
inline bool Position::capture_or_promotion(Move m) const {
|
||||
assert(is_ok(m));
|
||||
return type_of(m) != NORMAL ? type_of(m) != CASTLING : !empty(to_sq(m));
|
||||
}
|
||||
|
||||
inline bool Position::capture(Move m) const {
|
||||
assert(is_ok(m));
|
||||
// Castling is encoded as "king captures rook"
|
||||
return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == EN_PASSANT;
|
||||
return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT;
|
||||
}
|
||||
|
||||
inline Piece Position::captured_piece() const {
|
||||
@@ -387,25 +396,35 @@ inline void Position::put_piece(Piece pc, Square s) {
|
||||
board[s] = pc;
|
||||
byTypeBB[ALL_PIECES] |= byTypeBB[type_of(pc)] |= s;
|
||||
byColorBB[color_of(pc)] |= s;
|
||||
pieceCount[pc]++;
|
||||
index[s] = pieceCount[pc]++;
|
||||
pieceList[pc][index[s]] = s;
|
||||
pieceCount[make_piece(color_of(pc), ALL_PIECES)]++;
|
||||
psq += PSQT::psq[pc][s];
|
||||
}
|
||||
|
||||
inline void Position::remove_piece(Square s) {
|
||||
|
||||
// WARNING: This is not a reversible operation. If we remove a piece in
|
||||
// do_move() and then replace it in undo_move() we will put it at the end of
|
||||
// the list and not in its original place, it means index[] and pieceList[]
|
||||
// are not invariant to a do_move() + undo_move() sequence.
|
||||
Piece pc = board[s];
|
||||
byTypeBB[ALL_PIECES] ^= s;
|
||||
byTypeBB[type_of(pc)] ^= s;
|
||||
byColorBB[color_of(pc)] ^= s;
|
||||
board[s] = NO_PIECE;
|
||||
pieceCount[pc]--;
|
||||
/* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */
|
||||
Square lastSquare = pieceList[pc][--pieceCount[pc]];
|
||||
index[lastSquare] = index[s];
|
||||
pieceList[pc][index[lastSquare]] = lastSquare;
|
||||
pieceList[pc][pieceCount[pc]] = SQ_NONE;
|
||||
pieceCount[make_piece(color_of(pc), ALL_PIECES)]--;
|
||||
psq -= PSQT::psq[pc][s];
|
||||
}
|
||||
|
||||
inline void Position::move_piece(Square from, Square to) {
|
||||
|
||||
// index[from] is not updated and becomes stale. This works as long as index[]
|
||||
// is accessed just by known occupied squares.
|
||||
Piece pc = board[from];
|
||||
Bitboard fromTo = from | to;
|
||||
byTypeBB[ALL_PIECES] ^= fromTo;
|
||||
@@ -413,6 +432,8 @@ inline void Position::move_piece(Square from, Square to) {
|
||||
byColorBB[color_of(pc)] ^= fromTo;
|
||||
board[from] = NO_PIECE;
|
||||
board[to] = pc;
|
||||
index[to] = index[from];
|
||||
pieceList[pc][index[to]] = to;
|
||||
psq += PSQT::psq[pc][to] - PSQT::psq[pc][from];
|
||||
}
|
||||
|
||||
@@ -425,6 +446,20 @@ inline StateInfo* Position::state() const {
|
||||
return st;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
inline const EvalList* Position::eval_list() const {
|
||||
|
||||
return &evalList;
|
||||
}
|
||||
|
||||
inline PieceId Position::piece_id_on(Square sq) const
|
||||
{
|
||||
|
||||
assert(piece_on(sq) != NO_PIECE);
|
||||
|
||||
PieceId pid = evalList.piece_id_list[sq];
|
||||
assert(is_ok(pid));
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
#endif // #ifndef POSITION_H_INCLUDED
|
||||
|
||||
+35
-44
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -16,23 +16,19 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "psqt.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "bitboard.h"
|
||||
#include "types.h"
|
||||
#include "bitboard.h"
|
||||
|
||||
namespace Stockfish {
|
||||
namespace PSQT {
|
||||
|
||||
namespace
|
||||
{
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
|
||||
auto constexpr S = make_score;
|
||||
|
||||
// 'Bonus' contains Piece-Square parameters.
|
||||
// Scores are explicit for files A to D, implicitly mirrored for E to H.
|
||||
// Bonus[PieceType][Square / 2] contains Piece-Square scores. For each piece
|
||||
// type on a given square a (middlegame, endgame) score pair is assigned. Table
|
||||
// is defined for files A..D and white side: it is symmetric for black side and
|
||||
// second half of the files.
|
||||
constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
|
||||
{ },
|
||||
{ },
|
||||
@@ -47,14 +43,14 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
|
||||
{ S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) }
|
||||
},
|
||||
{ // Bishop
|
||||
{ S(-37,-40), S(-4 ,-21), S( -6,-26), S(-16, -8) },
|
||||
{ S(-11,-26), S( 6, -9), S( 13,-12), S( 3, 1) },
|
||||
{ S(-5 ,-11), S( 15, -1), S( -4, -1), S( 12, 7) },
|
||||
{ S(-4 ,-14), S( 8, -4), S( 18, 0), S( 27, 12) },
|
||||
{ S(-8 ,-12), S( 20, -1), S( 15,-10), S( 22, 11) },
|
||||
{ S(-11,-21), S( 4, 4), S( 1, 3), S( 8, 4) },
|
||||
{ S(-12,-22), S(-10,-14), S( 4, -1), S( 0, 1) },
|
||||
{ S(-34,-32), S( 1,-29), S(-10,-26), S(-16,-17) }
|
||||
{ S(-53,-57), S( -5,-30), S( -8,-37), S(-23,-12) },
|
||||
{ S(-15,-37), S( 8,-13), S( 19,-17), S( 4, 1) },
|
||||
{ S( -7,-16), S( 21, -1), S( -5, -2), S( 17, 10) },
|
||||
{ S( -5,-20), S( 11, -6), S( 25, 0), S( 39, 17) },
|
||||
{ S(-12,-17), S( 29, -1), S( 22,-14), S( 31, 15) },
|
||||
{ S(-16,-30), S( 6, 6), S( 1, 4), S( 11, 6) },
|
||||
{ S(-17,-31), S(-14,-20), S( 5, -1), S( 0, 1) },
|
||||
{ S(-48,-46), S( 1,-42), S(-14,-37), S(-23,-24) }
|
||||
},
|
||||
{ // Rook
|
||||
{ S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) },
|
||||
@@ -68,13 +64,13 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
|
||||
},
|
||||
{ // Queen
|
||||
{ S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) },
|
||||
{ S(-3,-54), S( 5,-31), S( 8,-22), S(12, -4) },
|
||||
{ S(-3,-55), S( 5,-31), S( 8,-22), S(12, -4) },
|
||||
{ S(-3,-39), S( 6,-18), S(13, -9), S( 7, 3) },
|
||||
{ S( 4,-23), S( 5, -3), S( 9, 13), S( 8, 24) },
|
||||
{ S( 0,-29), S(14, -6), S(12, 9), S( 5, 21) },
|
||||
{ S(-4,-38), S(10,-18), S( 6,-11), S( 8, 1) },
|
||||
{ S(-4,-38), S(10,-18), S( 6,-12), S( 8, 1) },
|
||||
{ S(-5,-50), S( 6,-27), S(10,-24), S( 8, -8) },
|
||||
{ S(-2,-74), S(-2,-52), S( 1,-43), S(-2,-34) }
|
||||
{ S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) }
|
||||
},
|
||||
{ // King
|
||||
{ S(271, 1), S(327, 45), S(271, 85), S(198, 76) },
|
||||
@@ -91,22 +87,19 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
|
||||
constexpr Score PBonus[RANK_NB][FILE_NB] =
|
||||
{ // Pawn (asymmetric distribution)
|
||||
{ },
|
||||
{ S( 2, -8), S( 4, -6), S( 11, 9), S( 18, 5), S( 16, 16), S( 21, 6), S( 9, -6), S( -3,-18) },
|
||||
{ S( -9, -9), S(-15, -7), S( 11,-10), S( 15, 5), S( 31, 2), S( 23, 3), S( 6, -8), S(-20, -5) },
|
||||
{ S( -3, 7), S(-20, 1), S( 8, -8), S( 19, -2), S( 39,-14), S( 17,-13), S( 2,-11), S( -5, -6) },
|
||||
{ S( 11, 12), S( -4, 6), S(-11, 2), S( 2, -6), S( 11, -5), S( 0, -4), S(-12, 14), S( 5, 9) },
|
||||
{ S( 3, 27), S(-11, 18), S( -6, 19), S( 22, 29), S( -8, 30), S( -5, 9), S(-14, 8), S(-11, 14) },
|
||||
{ S( -7, -1), S( 6,-14), S( -2, 13), S(-11, 22), S( 4, 24), S(-14, 17), S( 10, 7), S( -9, 7) }
|
||||
{ S( 3,-10), S( 3, -6), S( 10, 10), S( 19, 0), S( 16, 14), S( 19, 7), S( 7, -5), S( -5,-19) },
|
||||
{ S( -9,-10), S(-15,-10), S( 11,-10), S( 15, 4), S( 32, 4), S( 22, 3), S( 5, -6), S(-22, -4) },
|
||||
{ S( -4, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S( -8, -9) },
|
||||
{ S( 13, 10), S( 0, 5), S(-13, 4), S( 1, -5), S( 11, -5), S( -2, -5), S(-13, 14), S( 5, 9) },
|
||||
{ S( 5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S( -8, 13) },
|
||||
{ S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
namespace PSQT
|
||||
{
|
||||
#undef S
|
||||
|
||||
Score psq[PIECE_NB][SQUARE_NB];
|
||||
|
||||
|
||||
// PSQT::init() initializes piece-square tables: the white halves of the tables are
|
||||
// copied from Bonus[] and PBonus[], adding the piece value, then the black halves of
|
||||
// the tables are initialized by flipping and changing the sign of the white scores.
|
||||
@@ -114,18 +107,16 @@ void init() {
|
||||
|
||||
for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING})
|
||||
{
|
||||
Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
|
||||
Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
|
||||
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
{
|
||||
File f = File(edge_distance(file_of(s)));
|
||||
psq[ pc][s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)]
|
||||
: Bonus[pc][rank_of(s)][f]);
|
||||
psq[~pc][flip_rank(s)] = -psq[pc][s];
|
||||
}
|
||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||
{
|
||||
File f = File(edge_distance(file_of(s)));
|
||||
psq[ pc][s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)]
|
||||
: Bonus[pc][rank_of(s)][f]);
|
||||
psq[~pc][flip_rank(s)] = -psq[pc][s];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace PSQT
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+503
-514
File diff suppressed because it is too large
Load Diff
+2
-10
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -25,8 +25,6 @@
|
||||
#include "movepick.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
class Position;
|
||||
|
||||
namespace Search {
|
||||
@@ -47,13 +45,9 @@ struct Stack {
|
||||
Move excludedMove;
|
||||
Move killers[2];
|
||||
Value staticEval;
|
||||
Depth depth;
|
||||
int statScore;
|
||||
int moveCount;
|
||||
bool inCheck;
|
||||
bool ttPv;
|
||||
bool ttHit;
|
||||
int doubleExtensions;
|
||||
};
|
||||
|
||||
|
||||
@@ -73,9 +67,9 @@ struct RootMove {
|
||||
|
||||
Value score = -VALUE_INFINITE;
|
||||
Value previousScore = -VALUE_INFINITE;
|
||||
Value averageScore = -VALUE_INFINITE;
|
||||
int selDepth = 0;
|
||||
int tbRank = 0;
|
||||
int bestMoveCount = 0;
|
||||
Value tbScore;
|
||||
std::vector<Move> pv;
|
||||
};
|
||||
@@ -111,6 +105,4 @@ void clear();
|
||||
|
||||
} // namespace Search
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef SEARCH_H_INCLUDED
|
||||
|
||||
-387
@@ -1,387 +0,0 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Stockfish is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef STOCKFISH_SIMD_H_INCLUDED
|
||||
#define STOCKFISH_SIMD_H_INCLUDED
|
||||
|
||||
#if defined(USE_AVX2)
|
||||
# include <immintrin.h>
|
||||
|
||||
#elif defined(USE_SSE41)
|
||||
# include <smmintrin.h>
|
||||
|
||||
#elif defined(USE_SSSE3)
|
||||
# include <tmmintrin.h>
|
||||
|
||||
#elif defined(USE_SSE2)
|
||||
# include <emmintrin.h>
|
||||
|
||||
#elif defined(USE_MMX)
|
||||
# include <mmintrin.h>
|
||||
|
||||
#elif defined(USE_NEON)
|
||||
# include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
// The inline asm is only safe for GCC, where it is necessary to get good codegen.
|
||||
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101693
|
||||
// Clang does fine without it.
|
||||
// Play around here: https://godbolt.org/z/7EWqrYq51
|
||||
#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER))
|
||||
#define USE_INLINE_ASM
|
||||
#endif
|
||||
|
||||
// Use either the AVX512 or AVX-VNNI version of the VNNI instructions.
|
||||
#if defined(USE_AVXVNNI)
|
||||
#define VNNI_PREFIX "%{vex%} "
|
||||
#else
|
||||
#define VNNI_PREFIX ""
|
||||
#endif
|
||||
|
||||
namespace Stockfish::Simd {
|
||||
|
||||
#if defined (USE_AVX512)
|
||||
|
||||
[[maybe_unused]] static int m512_hadd(__m512i sum, int bias) {
|
||||
return _mm512_reduce_add_epi32(sum) + bias;
|
||||
}
|
||||
|
||||
/*
|
||||
Parameters:
|
||||
sum0 = [zmm0.i128[0], zmm0.i128[1], zmm0.i128[2], zmm0.i128[3]]
|
||||
sum1 = [zmm1.i128[0], zmm1.i128[1], zmm1.i128[2], zmm1.i128[3]]
|
||||
sum2 = [zmm2.i128[0], zmm2.i128[1], zmm2.i128[2], zmm2.i128[3]]
|
||||
sum3 = [zmm3.i128[0], zmm3.i128[1], zmm3.i128[2], zmm3.i128[3]]
|
||||
|
||||
Returns:
|
||||
ret = [
|
||||
reduce_add_epi32(zmm0.i128[0]), reduce_add_epi32(zmm1.i128[0]), reduce_add_epi32(zmm2.i128[0]), reduce_add_epi32(zmm3.i128[0]),
|
||||
reduce_add_epi32(zmm0.i128[1]), reduce_add_epi32(zmm1.i128[1]), reduce_add_epi32(zmm2.i128[1]), reduce_add_epi32(zmm3.i128[1]),
|
||||
reduce_add_epi32(zmm0.i128[2]), reduce_add_epi32(zmm1.i128[2]), reduce_add_epi32(zmm2.i128[2]), reduce_add_epi32(zmm3.i128[2]),
|
||||
reduce_add_epi32(zmm0.i128[3]), reduce_add_epi32(zmm1.i128[3]), reduce_add_epi32(zmm2.i128[3]), reduce_add_epi32(zmm3.i128[3])
|
||||
]
|
||||
*/
|
||||
[[maybe_unused]] static __m512i m512_hadd128x16_interleave(
|
||||
__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) {
|
||||
|
||||
__m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1);
|
||||
__m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1);
|
||||
|
||||
__m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3);
|
||||
__m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3);
|
||||
|
||||
__m512i sum01 = _mm512_add_epi32(sum01a, sum01b);
|
||||
__m512i sum23 = _mm512_add_epi32(sum23a, sum23b);
|
||||
|
||||
__m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23);
|
||||
__m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23);
|
||||
|
||||
return _mm512_add_epi32(sum0123a, sum0123b);
|
||||
}
|
||||
|
||||
[[maybe_unused]] static __m128i m512_haddx4(
|
||||
__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3,
|
||||
__m128i bias) {
|
||||
|
||||
__m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3);
|
||||
|
||||
__m256i sum256lo = _mm512_castsi512_si256(sum);
|
||||
__m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1);
|
||||
|
||||
sum256lo = _mm256_add_epi32(sum256lo, sum256hi);
|
||||
|
||||
__m128i sum128lo = _mm256_castsi256_si128(sum256lo);
|
||||
__m128i sum128hi = _mm256_extracti128_si256(sum256lo, 1);
|
||||
|
||||
return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias);
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void m512_add_dpbusd_epi32(
|
||||
__m512i& acc,
|
||||
__m512i a,
|
||||
__m512i b) {
|
||||
|
||||
# if defined (USE_VNNI)
|
||||
# if defined (USE_INLINE_ASM)
|
||||
asm(
|
||||
"vpdpbusd %[b], %[a], %[acc]\n\t"
|
||||
: [acc]"+v"(acc)
|
||||
: [a]"v"(a), [b]"vm"(b)
|
||||
);
|
||||
# else
|
||||
acc = _mm512_dpbusd_epi32(acc, a, b);
|
||||
# endif
|
||||
# else
|
||||
# if defined (USE_INLINE_ASM)
|
||||
__m512i tmp = _mm512_maddubs_epi16(a, b);
|
||||
asm(
|
||||
"vpmaddwd %[tmp], %[ones], %[tmp]\n\t"
|
||||
"vpaddd %[acc], %[tmp], %[acc]\n\t"
|
||||
: [acc]"+v"(acc), [tmp]"+&v"(tmp)
|
||||
: [ones]"v"(_mm512_set1_epi16(1))
|
||||
);
|
||||
# else
|
||||
__m512i product0 = _mm512_maddubs_epi16(a, b);
|
||||
product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1));
|
||||
acc = _mm512_add_epi32(acc, product0);
|
||||
# endif
|
||||
# endif
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void m512_add_dpbusd_epi32x2(
|
||||
__m512i& acc,
|
||||
__m512i a0, __m512i b0,
|
||||
__m512i a1, __m512i b1) {
|
||||
|
||||
# if defined (USE_VNNI)
|
||||
# if defined (USE_INLINE_ASM)
|
||||
asm(
|
||||
"vpdpbusd %[b0], %[a0], %[acc]\n\t"
|
||||
"vpdpbusd %[b1], %[a1], %[acc]\n\t"
|
||||
: [acc]"+v"(acc)
|
||||
: [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1)
|
||||
);
|
||||
# else
|
||||
acc = _mm512_dpbusd_epi32(acc, a0, b0);
|
||||
acc = _mm512_dpbusd_epi32(acc, a1, b1);
|
||||
# endif
|
||||
# else
|
||||
# if defined (USE_INLINE_ASM)
|
||||
__m512i tmp0 = _mm512_maddubs_epi16(a0, b0);
|
||||
__m512i tmp1 = _mm512_maddubs_epi16(a1, b1);
|
||||
asm(
|
||||
"vpaddsw %[tmp0], %[tmp1], %[tmp0]\n\t"
|
||||
"vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t"
|
||||
"vpaddd %[acc], %[tmp0], %[acc]\n\t"
|
||||
: [acc]"+v"(acc), [tmp0]"+&v"(tmp0)
|
||||
: [tmp1]"v"(tmp1), [ones]"v"(_mm512_set1_epi16(1))
|
||||
);
|
||||
# else
|
||||
__m512i product0 = _mm512_maddubs_epi16(a0, b0);
|
||||
__m512i product1 = _mm512_maddubs_epi16(a1, b1);
|
||||
product0 = _mm512_adds_epi16(product0, product1);
|
||||
product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1));
|
||||
acc = _mm512_add_epi32(acc, product0);
|
||||
# endif
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined (USE_AVX2)
|
||||
|
||||
[[maybe_unused]] static int m256_hadd(__m256i sum, int bias) {
|
||||
__m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1));
|
||||
sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC));
|
||||
sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB));
|
||||
return _mm_cvtsi128_si32(sum128) + bias;
|
||||
}
|
||||
|
||||
[[maybe_unused]] static __m128i m256_haddx4(
|
||||
__m256i sum0, __m256i sum1, __m256i sum2, __m256i sum3,
|
||||
__m128i bias) {
|
||||
|
||||
sum0 = _mm256_hadd_epi32(sum0, sum1);
|
||||
sum2 = _mm256_hadd_epi32(sum2, sum3);
|
||||
|
||||
sum0 = _mm256_hadd_epi32(sum0, sum2);
|
||||
|
||||
__m128i sum128lo = _mm256_castsi256_si128(sum0);
|
||||
__m128i sum128hi = _mm256_extracti128_si256(sum0, 1);
|
||||
|
||||
return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias);
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void m256_add_dpbusd_epi32(
|
||||
__m256i& acc,
|
||||
__m256i a,
|
||||
__m256i b) {
|
||||
|
||||
# if defined (USE_VNNI)
|
||||
# if defined (USE_INLINE_ASM)
|
||||
asm(
|
||||
VNNI_PREFIX "vpdpbusd %[b], %[a], %[acc]\n\t"
|
||||
: [acc]"+v"(acc)
|
||||
: [a]"v"(a), [b]"vm"(b)
|
||||
);
|
||||
# else
|
||||
acc = _mm256_dpbusd_epi32(acc, a, b);
|
||||
# endif
|
||||
# else
|
||||
# if defined (USE_INLINE_ASM)
|
||||
__m256i tmp = _mm256_maddubs_epi16(a, b);
|
||||
asm(
|
||||
"vpmaddwd %[tmp], %[ones], %[tmp]\n\t"
|
||||
"vpaddd %[acc], %[tmp], %[acc]\n\t"
|
||||
: [acc]"+v"(acc), [tmp]"+&v"(tmp)
|
||||
: [ones]"v"(_mm256_set1_epi16(1))
|
||||
);
|
||||
# else
|
||||
__m256i product0 = _mm256_maddubs_epi16(a, b);
|
||||
product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1));
|
||||
acc = _mm256_add_epi32(acc, product0);
|
||||
# endif
|
||||
# endif
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void m256_add_dpbusd_epi32x2(
|
||||
__m256i& acc,
|
||||
__m256i a0, __m256i b0,
|
||||
__m256i a1, __m256i b1) {
|
||||
|
||||
# if defined (USE_VNNI)
|
||||
# if defined (USE_INLINE_ASM)
|
||||
asm(
|
||||
VNNI_PREFIX "vpdpbusd %[b0], %[a0], %[acc]\n\t"
|
||||
VNNI_PREFIX "vpdpbusd %[b1], %[a1], %[acc]\n\t"
|
||||
: [acc]"+v"(acc)
|
||||
: [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1)
|
||||
);
|
||||
# else
|
||||
acc = _mm256_dpbusd_epi32(acc, a0, b0);
|
||||
acc = _mm256_dpbusd_epi32(acc, a1, b1);
|
||||
# endif
|
||||
# else
|
||||
# if defined (USE_INLINE_ASM)
|
||||
__m256i tmp0 = _mm256_maddubs_epi16(a0, b0);
|
||||
__m256i tmp1 = _mm256_maddubs_epi16(a1, b1);
|
||||
asm(
|
||||
"vpaddsw %[tmp0], %[tmp1], %[tmp0]\n\t"
|
||||
"vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t"
|
||||
"vpaddd %[acc], %[tmp0], %[acc]\n\t"
|
||||
: [acc]"+v"(acc), [tmp0]"+&v"(tmp0)
|
||||
: [tmp1]"v"(tmp1), [ones]"v"(_mm256_set1_epi16(1))
|
||||
);
|
||||
# else
|
||||
__m256i product0 = _mm256_maddubs_epi16(a0, b0);
|
||||
__m256i product1 = _mm256_maddubs_epi16(a1, b1);
|
||||
product0 = _mm256_adds_epi16(product0, product1);
|
||||
product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1));
|
||||
acc = _mm256_add_epi32(acc, product0);
|
||||
# endif
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined (USE_SSSE3)
|
||||
|
||||
[[maybe_unused]] static int m128_hadd(__m128i sum, int bias) {
|
||||
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC
|
||||
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB
|
||||
return _mm_cvtsi128_si32(sum) + bias;
|
||||
}
|
||||
|
||||
[[maybe_unused]] static __m128i m128_haddx4(
|
||||
__m128i sum0, __m128i sum1, __m128i sum2, __m128i sum3,
|
||||
__m128i bias) {
|
||||
|
||||
sum0 = _mm_hadd_epi32(sum0, sum1);
|
||||
sum2 = _mm_hadd_epi32(sum2, sum3);
|
||||
sum0 = _mm_hadd_epi32(sum0, sum2);
|
||||
return _mm_add_epi32(sum0, bias);
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void m128_add_dpbusd_epi32(
|
||||
__m128i& acc,
|
||||
__m128i a,
|
||||
__m128i b) {
|
||||
|
||||
# if defined (USE_INLINE_ASM)
|
||||
__m128i tmp = _mm_maddubs_epi16(a, b);
|
||||
asm(
|
||||
"pmaddwd %[ones], %[tmp]\n\t"
|
||||
"paddd %[tmp], %[acc]\n\t"
|
||||
: [acc]"+v"(acc), [tmp]"+&v"(tmp)
|
||||
: [ones]"v"(_mm_set1_epi16(1))
|
||||
);
|
||||
# else
|
||||
__m128i product0 = _mm_maddubs_epi16(a, b);
|
||||
product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1));
|
||||
acc = _mm_add_epi32(acc, product0);
|
||||
# endif
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void m128_add_dpbusd_epi32x2(
|
||||
__m128i& acc,
|
||||
__m128i a0, __m128i b0,
|
||||
__m128i a1, __m128i b1) {
|
||||
|
||||
# if defined (USE_INLINE_ASM)
|
||||
__m128i tmp0 = _mm_maddubs_epi16(a0, b0);
|
||||
__m128i tmp1 = _mm_maddubs_epi16(a1, b1);
|
||||
asm(
|
||||
"paddsw %[tmp1], %[tmp0]\n\t"
|
||||
"pmaddwd %[ones], %[tmp0]\n\t"
|
||||
"paddd %[tmp0], %[acc]\n\t"
|
||||
: [acc]"+v"(acc), [tmp0]"+&v"(tmp0)
|
||||
: [tmp1]"v"(tmp1), [ones]"v"(_mm_set1_epi16(1))
|
||||
);
|
||||
# else
|
||||
__m128i product0 = _mm_maddubs_epi16(a0, b0);
|
||||
__m128i product1 = _mm_maddubs_epi16(a1, b1);
|
||||
product0 = _mm_adds_epi16(product0, product1);
|
||||
product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1));
|
||||
acc = _mm_add_epi32(acc, product0);
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined (USE_NEON)
|
||||
|
||||
[[maybe_unused]] static int neon_m128_reduce_add_epi32(int32x4_t s) {
|
||||
# if USE_NEON >= 8
|
||||
return vaddvq_s32(s);
|
||||
# else
|
||||
return s[0] + s[1] + s[2] + s[3];
|
||||
# endif
|
||||
}
|
||||
|
||||
[[maybe_unused]] static int neon_m128_hadd(int32x4_t sum, int bias) {
|
||||
return neon_m128_reduce_add_epi32(sum) + bias;
|
||||
}
|
||||
|
||||
[[maybe_unused]] static int32x4_t neon_m128_haddx4(
|
||||
int32x4_t sum0, int32x4_t sum1, int32x4_t sum2, int32x4_t sum3,
|
||||
int32x4_t bias) {
|
||||
|
||||
int32x4_t hsums {
|
||||
neon_m128_reduce_add_epi32(sum0),
|
||||
neon_m128_reduce_add_epi32(sum1),
|
||||
neon_m128_reduce_add_epi32(sum2),
|
||||
neon_m128_reduce_add_epi32(sum3)
|
||||
};
|
||||
return vaddq_s32(hsums, bias);
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void neon_m128_add_dpbusd_epi32x2(
|
||||
int32x4_t& acc,
|
||||
int8x8_t a0, int8x8_t b0,
|
||||
int8x8_t a1, int8x8_t b1) {
|
||||
|
||||
int16x8_t product = vmull_s8(a0, b0);
|
||||
product = vmlal_s8(product, a1, b1);
|
||||
acc = vpadalq_s16(acc, product);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif // STOCKFISH_SIMD_H_INCLUDED
|
||||
+28
-45
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -50,11 +50,9 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
using namespace Stockfish::Tablebases;
|
||||
using namespace Tablebases;
|
||||
|
||||
int Stockfish::Tablebases::MaxCardinality;
|
||||
|
||||
namespace Stockfish {
|
||||
int Tablebases::MaxCardinality;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -105,6 +103,9 @@ template<> inline void swap_endian<uint8_t>(uint8_t&) {}
|
||||
|
||||
template<typename T, int LE> T number(void* addr)
|
||||
{
|
||||
static const union { uint32_t i; char c[4]; } Le = { 0x01020304 };
|
||||
static const bool IsLittleEndian = (Le.c[0] == 4);
|
||||
|
||||
T v;
|
||||
|
||||
if ((uintptr_t)addr & (alignof(T) - 1)) // Unaligned pointer (very rare)
|
||||
@@ -189,8 +190,7 @@ public:
|
||||
std::stringstream ss(Paths);
|
||||
std::string path;
|
||||
|
||||
while (std::getline(ss, path, SepChar))
|
||||
{
|
||||
while (std::getline(ss, path, SepChar)) {
|
||||
fname = path + "/" + f;
|
||||
std::ifstream::open(fname);
|
||||
if (is_open())
|
||||
@@ -223,9 +223,7 @@ public:
|
||||
|
||||
*mapping = statbuf.st_size;
|
||||
*baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
#if defined(MADV_RANDOM)
|
||||
madvise(*baseAddress, statbuf.st_size, MADV_RANDOM);
|
||||
#endif
|
||||
::close(fd);
|
||||
|
||||
if (*baseAddress == MAP_FAILED)
|
||||
@@ -565,8 +563,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) {
|
||||
int buf64Size = 64;
|
||||
Sym sym;
|
||||
|
||||
while (true)
|
||||
{
|
||||
while (true) {
|
||||
int len = 0; // This is the symbol length - d->min_sym_len
|
||||
|
||||
// Now get the symbol length. For any symbol s64 of length l right-padded
|
||||
@@ -604,8 +601,8 @@ int decompress_pairs(PairsData* d, uint64_t idx) {
|
||||
// We binary-search for our value recursively expanding into the left and
|
||||
// right child symbols until we reach a leaf node where symlen[sym] + 1 == 1
|
||||
// that will store the value we need.
|
||||
while (d->symlen[sym])
|
||||
{
|
||||
while (d->symlen[sym]) {
|
||||
|
||||
Sym left = d->btree[sym].get<LR::Left>();
|
||||
|
||||
// If a symbol contains 36 sub-symbols (d->symlen[sym] + 1 = 36) and
|
||||
@@ -710,7 +707,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
|
||||
|
||||
leadPawns = b = pos.pieces(color_of(pc), PAWN);
|
||||
do
|
||||
squares[size++] = pop_lsb(b) ^ flipSquares;
|
||||
squares[size++] = pop_lsb(&b) ^ flipSquares;
|
||||
while (b);
|
||||
|
||||
leadPawnsCnt = size;
|
||||
@@ -730,7 +727,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
|
||||
// directly map them to the correct color and square.
|
||||
b = pos.pieces() ^ leadPawns;
|
||||
do {
|
||||
Square s = pop_lsb(b);
|
||||
Square s = pop_lsb(&b);
|
||||
squares[size] = s ^ flipSquares;
|
||||
pieces[size++] = Piece(pos.piece_on(s) ^ flipColor);
|
||||
} while (b);
|
||||
@@ -761,7 +758,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
|
||||
if (entry->hasPawns) {
|
||||
idx = LeadPawnIdx[leadPawnsCnt][squares[0]];
|
||||
|
||||
std::stable_sort(squares + 1, squares + leadPawnsCnt, pawns_comp);
|
||||
std::sort(squares + 1, squares + leadPawnsCnt, pawns_comp);
|
||||
|
||||
for (int i = 1; i < leadPawnsCnt; ++i)
|
||||
idx += Binomial[i][MapPawns[squares[i]]];
|
||||
@@ -769,7 +766,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
|
||||
goto encode_remaining; // With pawns we have finished special treatments
|
||||
}
|
||||
|
||||
// In positions without pawns, we further flip the squares to ensure leading
|
||||
// In positions withouth pawns, we further flip the squares to ensure leading
|
||||
// piece is below RANK_5.
|
||||
if (rank_of(squares[0]) > RANK_4)
|
||||
for (int i = 0; i < size; ++i)
|
||||
@@ -812,7 +809,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
|
||||
// Rs "together" in 62 * 61 / 2 ways (we divide by 2 because rooks can be
|
||||
// swapped and still get the same position.)
|
||||
//
|
||||
// In case we have at least 3 unique pieces (included kings) we encode them
|
||||
// In case we have at least 3 unique pieces (inlcuded kings) we encode them
|
||||
// together.
|
||||
if (entry->hasUniquePieces) {
|
||||
|
||||
@@ -827,7 +824,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu
|
||||
+ (squares[1] - adjust1)) * 62
|
||||
+ squares[2] - adjust2;
|
||||
|
||||
// First piece is on a1-h8 diagonal, second below: map this occurrence to
|
||||
// First piece is on a1-h8 diagonal, second below: map this occurence to
|
||||
// 6 to differentiate from the above case, rank_of() maps a1-d4 diagonal
|
||||
// to 0...3 and finally MapB1H1H7[] maps the b1-h1-h7 triangle to 0..27.
|
||||
else if (off_A1H8(squares[1]))
|
||||
@@ -857,12 +854,12 @@ encode_remaining:
|
||||
idx *= d->groupIdx[0];
|
||||
Square* groupSq = squares + d->groupLen[0];
|
||||
|
||||
// Encode remaining pawns then pieces according to square, in ascending order
|
||||
// Encode remainig pawns then pieces according to square, in ascending order
|
||||
bool remainingPawns = entry->hasPawns && entry->pawnCount[1];
|
||||
|
||||
while (d->groupLen[++next])
|
||||
{
|
||||
std::stable_sort(groupSq, groupSq + d->groupLen[next]);
|
||||
std::sort(groupSq, groupSq + d->groupLen[next]);
|
||||
uint64_t n = 0;
|
||||
|
||||
// Map down a square if "comes later" than a square in the previous
|
||||
@@ -885,7 +882,7 @@ encode_remaining:
|
||||
|
||||
// Group together pieces that will be encoded together. The general rule is that
|
||||
// a group contains pieces of same type and color. The exception is the leading
|
||||
// group that, in case of positions without pawns, can be formed by 3 different
|
||||
// group that, in case of positions withouth pawns, can be formed by 3 different
|
||||
// pieces (default) or by the king pair when there is not a unique piece apart
|
||||
// from the kings. When there are pawns, pawns are always first in pieces[].
|
||||
//
|
||||
@@ -917,7 +914,7 @@ void set_groups(T& e, PairsData* d, int order[], File f) {
|
||||
//
|
||||
// This ensures unique encoding for the whole position. The order of the
|
||||
// groups is a per-table parameter and could not follow the canonical leading
|
||||
// pawns/pieces -> remaining pawns -> remaining pieces. In particular the
|
||||
// pawns/pieces -> remainig pawns -> remaining pieces. In particular the
|
||||
// first group is at order[0] position and the remaining pawns, when present,
|
||||
// are at order[1] position.
|
||||
bool pp = e.hasPawns && e.pawnCount[1]; // Pawns on both sides
|
||||
@@ -937,7 +934,7 @@ void set_groups(T& e, PairsData* d, int order[], File f) {
|
||||
d->groupIdx[1] = idx;
|
||||
idx *= Binomial[d->groupLen[1]][48 - d->groupLen[0]];
|
||||
}
|
||||
else // Remaining pieces
|
||||
else // Remainig pieces
|
||||
{
|
||||
d->groupIdx[next] = idx;
|
||||
idx *= Binomial[d->groupLen[next]][freeSquares];
|
||||
@@ -947,7 +944,7 @@ void set_groups(T& e, PairsData* d, int order[], File f) {
|
||||
d->groupIdx[n] = idx;
|
||||
}
|
||||
|
||||
// In Recursive Pairing each symbol represents a pair of children symbols. So
|
||||
// In Recursive Pairing each symbol represents a pair of childern symbols. So
|
||||
// read d->btree[] symbols data and expand each one in his left and right child
|
||||
// symbol until reaching the leafs that represent the symbol value.
|
||||
uint8_t set_symlen(PairsData* d, Sym s, std::vector<bool>& visited) {
|
||||
@@ -1001,7 +998,7 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) {
|
||||
// so that d->lowestSym[i] >= d->lowestSym[i+1] (when read as LittleEndian).
|
||||
// Starting from this we compute a base64[] table indexed by symbol length
|
||||
// and containing 64 bit values so that d->base64[i] >= d->base64[i+1].
|
||||
// See https://en.wikipedia.org/wiki/Huffman_coding
|
||||
// See http://www.eecs.harvard.edu/~michaelm/E210/huffman.pdf
|
||||
for (int i = d->base64.size() - 2; i >= 0; --i) {
|
||||
d->base64[i] = (d->base64[i + 1] + number<Sym, LittleEndian>(&d->lowestSym[i])
|
||||
- number<Sym, LittleEndian>(&d->lowestSym[i + 1])) / 2;
|
||||
@@ -1142,7 +1139,7 @@ void* mapped(TBTable<Type>& e, const Position& pos) {
|
||||
if (e.ready.load(std::memory_order_acquire))
|
||||
return e.baseAddress; // Could be nullptr if file does not exist
|
||||
|
||||
std::scoped_lock<std::mutex> lk(mutex);
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
|
||||
if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock
|
||||
return e.baseAddress;
|
||||
@@ -1317,7 +1314,7 @@ void Tablebases::init(const std::string& paths) {
|
||||
for (auto p : bothOnDiagonal)
|
||||
MapKK[p.first][p.second] = code++;
|
||||
|
||||
// Binomial[] stores the Binomial Coefficients using Pascal rule. There
|
||||
// Binomial[] stores the Binomial Coefficents using Pascal rule. There
|
||||
// are Binomial[k][n] ways to choose k elements from a set of n elements.
|
||||
Binomial[0][0] = 1;
|
||||
|
||||
@@ -1337,7 +1334,7 @@ void Tablebases::init(const std::string& paths) {
|
||||
for (int leadPawnsCnt = 1; leadPawnsCnt <= 5; ++leadPawnsCnt)
|
||||
for (File f = FILE_A; f <= FILE_D; ++f)
|
||||
{
|
||||
// Restart the index at every file because TB table is split
|
||||
// Restart the index at every file because TB table is splitted
|
||||
// by file, so we can reuse the same index for different files.
|
||||
int idx = 0;
|
||||
|
||||
@@ -1441,7 +1438,7 @@ WDLScore Tablebases::probe_wdl(Position& pos, ProbeState* result) {
|
||||
// If n = 100 immediately after a capture or pawn move, then the position
|
||||
// is also certainly a win, and during the whole phase until the next
|
||||
// capture or pawn move, the inequality to be preserved is
|
||||
// dtz + 50-move-counter <= 100.
|
||||
// dtz + 50-movecounter <= 100.
|
||||
//
|
||||
// In short, if a move is available resulting in dtz + 50-move-counter <= 99,
|
||||
// then do not accept moves leading to dtz + 50-move-counter == 100.
|
||||
@@ -1536,14 +1533,6 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) {
|
||||
WDLScore wdl = -probe_wdl(pos, &result);
|
||||
dtz = dtz_before_zeroing(wdl);
|
||||
}
|
||||
else if (pos.is_draw(1))
|
||||
{
|
||||
// In case a root move leads to a draw by repetition or
|
||||
// 50-move rule, we set dtz to zero. Note: since we are
|
||||
// only 1 ply from the root, this must be a true 3-fold
|
||||
// repetition inside the game history.
|
||||
dtz = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, take dtz for the new position and correct by 1 ply
|
||||
@@ -1594,7 +1583,6 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) {
|
||||
|
||||
ProbeState result;
|
||||
StateInfo st;
|
||||
WDLScore wdl;
|
||||
|
||||
bool rule50 = Options["Syzygy50MoveRule"];
|
||||
|
||||
@@ -1603,10 +1591,7 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) {
|
||||
{
|
||||
pos.do_move(m.pv[0], st);
|
||||
|
||||
if (pos.is_draw(1))
|
||||
wdl = WDLDraw;
|
||||
else
|
||||
wdl = -probe_wdl(pos, &result);
|
||||
WDLScore wdl = -probe_wdl(pos, &result);
|
||||
|
||||
pos.undo_move(m.pv[0]);
|
||||
|
||||
@@ -1623,5 +1608,3 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "../search.h"
|
||||
|
||||
namespace Stockfish::Tablebases {
|
||||
namespace Tablebases {
|
||||
|
||||
enum WDLScore {
|
||||
WDLLoss = -2, // Loss
|
||||
@@ -38,7 +38,7 @@ enum WDLScore {
|
||||
// Possible states after a probing operation
|
||||
enum ProbeState {
|
||||
FAIL = 0, // Probe failed (missing file table)
|
||||
OK = 1, // Probe successful
|
||||
OK = 1, // Probe succesful
|
||||
CHANGE_STM = -1, // DTZ should check the other side
|
||||
ZEROING_BEST_MOVE = 2 // Best move zeroes DTZ (capture or pawn move)
|
||||
};
|
||||
@@ -73,6 +73,6 @@ inline std::ostream& operator<<(std::ostream& os, const ProbeState v) {
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace Stockfish::Tablebases
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+34
-26
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -26,8 +26,6 @@
|
||||
#include "syzygy/tbprobe.h"
|
||||
#include "tt.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
ThreadPool Threads; // Global object
|
||||
|
||||
|
||||
@@ -53,12 +51,24 @@ Thread::~Thread() {
|
||||
}
|
||||
|
||||
|
||||
/// Thread::bestMoveCount(Move move) return best move counter for the given root move
|
||||
|
||||
int Thread::best_move_count(Move move) const {
|
||||
|
||||
auto rm = std::find(rootMoves.begin() + pvIdx,
|
||||
rootMoves.begin() + pvLast, move);
|
||||
|
||||
return rm != rootMoves.begin() + pvLast ? rm->bestMoveCount : 0;
|
||||
}
|
||||
|
||||
|
||||
/// Thread::clear() reset histories, usually before a new game
|
||||
|
||||
void Thread::clear() {
|
||||
|
||||
counterMoves.fill(MOVE_NONE);
|
||||
mainHistory.fill(0);
|
||||
lowPlyHistory.fill(0);
|
||||
captureHistory.fill(0);
|
||||
|
||||
for (bool inCheck : { false, true })
|
||||
@@ -66,7 +76,7 @@ void Thread::clear() {
|
||||
{
|
||||
for (auto& to : continuationHistory[inCheck][c])
|
||||
for (auto& h : to)
|
||||
h->fill(-71);
|
||||
h->fill(0);
|
||||
continuationHistory[inCheck][c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1);
|
||||
}
|
||||
}
|
||||
@@ -127,16 +137,14 @@ void Thread::idle_loop() {
|
||||
|
||||
void ThreadPool::set(size_t requested) {
|
||||
|
||||
if (size() > 0) // destroy any existing thread(s)
|
||||
{
|
||||
if (size() > 0) { // destroy any existing thread(s)
|
||||
main()->wait_for_search_finished();
|
||||
|
||||
while (size() > 0)
|
||||
delete back(), pop_back();
|
||||
}
|
||||
|
||||
if (requested > 0) // create new thread(s)
|
||||
{
|
||||
if (requested > 0) { // create new thread(s)
|
||||
push_back(new MainThread(0));
|
||||
|
||||
while (size() < requested)
|
||||
@@ -161,7 +169,6 @@ void ThreadPool::clear() {
|
||||
|
||||
main()->callsCnt = 0;
|
||||
main()->bestPreviousScore = VALUE_INFINITE;
|
||||
main()->bestPreviousAverageScore = VALUE_INFINITE;
|
||||
main()->previousTimeReduction = 1.0;
|
||||
}
|
||||
|
||||
@@ -197,18 +204,21 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
|
||||
|
||||
// We use Position::set() to set root position across threads. But there are
|
||||
// some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot
|
||||
// be deduced from a fen string, so set() clears them and they are set from
|
||||
// setupStates->back() later. The rootState is per thread, earlier states are shared
|
||||
// since they are read-only.
|
||||
// be deduced from a fen string, so set() clears them and to not lose the info
|
||||
// we need to backup and later restore setupStates->back(). Note that setupStates
|
||||
// is shared by threads but is accessed in read-only mode.
|
||||
StateInfo tmp = setupStates->back();
|
||||
|
||||
for (Thread* th : *this)
|
||||
{
|
||||
th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0;
|
||||
th->rootDepth = th->completedDepth = 0;
|
||||
th->rootMoves = rootMoves;
|
||||
th->rootPos.set(pos.fen(), pos.is_chess960(), &th->rootState, th);
|
||||
th->rootState = setupStates->back();
|
||||
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
|
||||
}
|
||||
|
||||
setupStates->back() = tmp;
|
||||
|
||||
main()->start_searching();
|
||||
}
|
||||
|
||||
@@ -228,16 +238,16 @@ Thread* ThreadPool::get_best_thread() const {
|
||||
votes[th->rootMoves[0].pv[0]] +=
|
||||
(th->rootMoves[0].score - minScore + 14) * int(th->completedDepth);
|
||||
|
||||
if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY)
|
||||
{
|
||||
// Make sure we pick the shortest mate / TB conversion or stave off mate the longest
|
||||
if (th->rootMoves[0].score > bestThread->rootMoves[0].score)
|
||||
bestThread = th;
|
||||
}
|
||||
else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY
|
||||
|| ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY
|
||||
&& votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]))
|
||||
bestThread = th;
|
||||
if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY)
|
||||
{
|
||||
// Make sure we pick the shortest mate / TB conversion or stave off mate the longest
|
||||
if (th->rootMoves[0].score > bestThread->rootMoves[0].score)
|
||||
bestThread = th;
|
||||
}
|
||||
else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY
|
||||
|| ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY
|
||||
&& votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]))
|
||||
bestThread = th;
|
||||
}
|
||||
|
||||
return bestThread;
|
||||
@@ -262,5 +272,3 @@ void ThreadPool::wait_for_search_finished() const {
|
||||
if (th != front())
|
||||
th->wait_for_search_finished();
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+7
-13
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -32,7 +32,6 @@
|
||||
#include "search.h"
|
||||
#include "thread_win32_osx.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
/// Thread class keeps together all the thread-related stuff. We use
|
||||
/// per-thread pawn and material hash tables so that once we get a
|
||||
@@ -55,27 +54,25 @@ public:
|
||||
void idle_loop();
|
||||
void start_searching();
|
||||
void wait_for_search_finished();
|
||||
size_t id() const { return idx; }
|
||||
int best_move_count(Move move) const;
|
||||
|
||||
Pawns::Table pawnsTable;
|
||||
Material::Table materialTable;
|
||||
size_t pvIdx, pvLast;
|
||||
RunningAverage complexityAverage;
|
||||
std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
|
||||
uint64_t ttHitAverage;
|
||||
int selDepth, nmpMinPly;
|
||||
Color nmpColor;
|
||||
Value bestValue, optimism[COLOR_NB];
|
||||
std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
|
||||
|
||||
Position rootPos;
|
||||
StateInfo rootState;
|
||||
Search::RootMoves rootMoves;
|
||||
Depth rootDepth, completedDepth, depth;
|
||||
Value rootDelta;
|
||||
Depth rootDepth, completedDepth;
|
||||
CounterMoveHistory counterMoves;
|
||||
ButterflyHistory mainHistory;
|
||||
LowPlyHistory lowPlyHistory;
|
||||
CapturePieceToHistory captureHistory;
|
||||
ContinuationHistory continuationHistory[2][2];
|
||||
Score trend;
|
||||
Score contempt;
|
||||
};
|
||||
|
||||
|
||||
@@ -90,7 +87,6 @@ struct MainThread : public Thread {
|
||||
|
||||
double previousTimeReduction;
|
||||
Value bestPreviousScore;
|
||||
Value bestPreviousAverageScore;
|
||||
Value iterValue[4];
|
||||
int callsCnt;
|
||||
bool stopOnPonderhit;
|
||||
@@ -131,6 +127,4 @@ private:
|
||||
|
||||
extern ThreadPool Threads;
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef THREAD_H_INCLUDED
|
||||
|
||||
+2
-10
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -27,12 +27,10 @@
|
||||
/// The implementation calls pthread_create() with the stack size parameter
|
||||
/// equal to the linux 8MB default, on platforms that support it.
|
||||
|
||||
#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(USE_PTHREADS)
|
||||
#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__)
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
static const size_t TH_STACK_SIZE = 8 * 1024 * 1024;
|
||||
|
||||
template <class T, class P = std::pair<T*, void(T::*)()>>
|
||||
@@ -59,16 +57,10 @@ public:
|
||||
void join() { pthread_join(thread, NULL); }
|
||||
};
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#else // Default case: use STL classes
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
typedef std::thread NativeThread;
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif
|
||||
|
||||
#endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED
|
||||
|
||||
+12
-20
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -24,8 +24,6 @@
|
||||
#include "timeman.h"
|
||||
#include "uci.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
TimeManagement Time; // Our global time management object
|
||||
|
||||
|
||||
@@ -40,9 +38,9 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
|
||||
TimePoint slowMover = TimePoint(Options["Slow Mover"]);
|
||||
TimePoint npmsec = TimePoint(Options["nodestime"]);
|
||||
|
||||
// optScale is a percentage of available time to use for the current move.
|
||||
// maxScale is a multiplier applied to optimumTime.
|
||||
double optScale, maxScale;
|
||||
// opt_scale is a percentage of available time to use for the current move.
|
||||
// max_scale is a multiplier applied to optimumTime.
|
||||
double opt_scale, max_scale;
|
||||
|
||||
// If we have to play in 'nodes as time' mode, then convert from time
|
||||
// to nodes, and use resulting values in time management formulas.
|
||||
@@ -68,9 +66,6 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
|
||||
TimePoint timeLeft = std::max(TimePoint(1),
|
||||
limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg));
|
||||
|
||||
// Use extra time with larger increments
|
||||
double optExtra = std::clamp(1.0 + 12.0 * limits.inc[us] / limits.time[us], 1.0, 1.12);
|
||||
|
||||
// A user may scale time usage by setting UCI option "Slow Mover"
|
||||
// Default is 100 and changing this value will probably lose elo.
|
||||
timeLeft = slowMover * timeLeft / 100;
|
||||
@@ -80,26 +75,23 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
|
||||
// game time for the current move, so also cap to 20% of available game time.
|
||||
if (limits.movestogo == 0)
|
||||
{
|
||||
optScale = std::min(0.0084 + std::pow(ply + 3.0, 0.5) * 0.0042,
|
||||
0.2 * limits.time[us] / double(timeLeft))
|
||||
* optExtra;
|
||||
maxScale = std::min(7.0, 4.0 + ply / 12.0);
|
||||
opt_scale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0,
|
||||
0.2 * limits.time[us] / double(timeLeft));
|
||||
max_scale = std::min(7.0, 4.0 + ply / 12.0);
|
||||
}
|
||||
|
||||
// x moves in y seconds (+ z increment)
|
||||
else
|
||||
{
|
||||
optScale = std::min((0.88 + ply / 116.4) / mtg,
|
||||
0.88 * limits.time[us] / double(timeLeft));
|
||||
maxScale = std::min(6.3, 1.5 + 0.11 * mtg);
|
||||
opt_scale = std::min((0.8 + ply / 128.0) / mtg,
|
||||
0.8 * limits.time[us] / double(timeLeft));
|
||||
max_scale = std::min(6.3, 1.5 + 0.11 * mtg);
|
||||
}
|
||||
|
||||
// Never use more than 80% of the available time for this move
|
||||
optimumTime = TimePoint(optScale * timeLeft);
|
||||
maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime));
|
||||
optimumTime = TimePoint(opt_scale * timeLeft);
|
||||
maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, max_scale * optimumTime));
|
||||
|
||||
if (Options["Ponder"])
|
||||
optimumTime += optimumTime / 4;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+1
-5
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -23,8 +23,6 @@
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
/// The TimeManagement class computes the optimal time to think depending on
|
||||
/// the maximum available time, the game move number and other parameters.
|
||||
|
||||
@@ -46,6 +44,4 @@ private:
|
||||
|
||||
extern TimeManagement Time;
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef TIMEMAN_H_INCLUDED
|
||||
|
||||
+20
-27
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -26,8 +26,6 @@
|
||||
#include "tt.h"
|
||||
#include "uci.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
TranspositionTable TT; // Our global transposition table
|
||||
|
||||
/// TTEntry::save() populates the TTEntry with a new node's data, possibly
|
||||
@@ -39,19 +37,18 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev)
|
||||
if (m || (uint16_t)k != key16)
|
||||
move16 = (uint16_t)m;
|
||||
|
||||
// Overwrite less valuable entries (cheapest checks first)
|
||||
if ( b == BOUND_EXACT
|
||||
|| (uint16_t)k != key16
|
||||
|| d - DEPTH_OFFSET + 2 * pv > depth8 - 4)
|
||||
// Overwrite less valuable entries
|
||||
if ((uint16_t)k != key16
|
||||
|| d - DEPTH_OFFSET > depth8 - 4
|
||||
|| b == BOUND_EXACT)
|
||||
{
|
||||
assert(d > DEPTH_OFFSET);
|
||||
assert(d < 256 + DEPTH_OFFSET);
|
||||
assert(d >= DEPTH_OFFSET);
|
||||
|
||||
key16 = (uint16_t)k;
|
||||
depth8 = (uint8_t)(d - DEPTH_OFFSET);
|
||||
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
|
||||
value16 = (int16_t)v;
|
||||
eval16 = (int16_t)ev;
|
||||
genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b);
|
||||
depth8 = (uint8_t)(d - DEPTH_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,12 +61,11 @@ void TranspositionTable::resize(size_t mbSize) {
|
||||
|
||||
Threads.main()->wait_for_search_finished();
|
||||
|
||||
aligned_large_pages_free(table);
|
||||
aligned_ttmem_free(mem);
|
||||
|
||||
clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster);
|
||||
|
||||
table = static_cast<Cluster*>(aligned_large_pages_alloc(clusterCount * sizeof(Cluster)));
|
||||
if (!table)
|
||||
table = static_cast<Cluster*>(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), mem));
|
||||
if (!mem)
|
||||
{
|
||||
std::cerr << "Failed to allocate " << mbSize
|
||||
<< "MB for transposition table." << std::endl;
|
||||
@@ -123,23 +119,22 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
|
||||
const uint16_t key16 = (uint16_t)key; // Use the low 16 bits as key inside the cluster
|
||||
|
||||
for (int i = 0; i < ClusterSize; ++i)
|
||||
if (tte[i].key16 == key16 || !tte[i].depth8)
|
||||
if (!tte[i].key16 || tte[i].key16 == key16)
|
||||
{
|
||||
tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & (GENERATION_DELTA - 1))); // Refresh
|
||||
tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & 0x7)); // Refresh
|
||||
|
||||
return found = (bool)tte[i].depth8, &tte[i];
|
||||
return found = (bool)tte[i].key16, &tte[i];
|
||||
}
|
||||
|
||||
// Find an entry to be replaced according to the replacement strategy
|
||||
TTEntry* replace = tte;
|
||||
for (int i = 1; i < ClusterSize; ++i)
|
||||
// Due to our packed storage format for generation and its cyclic
|
||||
// nature we add GENERATION_CYCLE (256 is the modulus, plus what
|
||||
// is needed to keep the unrelated lowest n bits from affecting
|
||||
// the result) to calculate the entry age correctly even after
|
||||
// generation8 overflows into the next cycle.
|
||||
if ( replace->depth8 - ((GENERATION_CYCLE + generation8 - replace->genBound8) & GENERATION_MASK)
|
||||
> tte[i].depth8 - ((GENERATION_CYCLE + generation8 - tte[i].genBound8) & GENERATION_MASK))
|
||||
// nature we add 263 (256 is the modulus plus 7 to keep the unrelated
|
||||
// lowest three bits from affecting the result) to calculate the entry
|
||||
// age correctly even after generation8 overflows into the next cycle.
|
||||
if ( replace->depth8 - ((263 + generation8 - replace->genBound8) & 0xF8)
|
||||
> tte[i].depth8 - ((263 + generation8 - tte[i].genBound8) & 0xF8))
|
||||
replace = &tte[i];
|
||||
|
||||
return found = false, replace;
|
||||
@@ -154,9 +149,7 @@ int TranspositionTable::hashfull() const {
|
||||
int cnt = 0;
|
||||
for (int i = 0; i < 1000; ++i)
|
||||
for (int j = 0; j < ClusterSize; ++j)
|
||||
cnt += table[i].entry[j].depth8 && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8;
|
||||
cnt += (table[i].entry[j].genBound8 & 0xF8) == generation8;
|
||||
|
||||
return cnt / ClusterSize;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -22,18 +22,16 @@
|
||||
#include "misc.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
/// TTEntry struct is the 10 bytes transposition table entry, defined as below:
|
||||
///
|
||||
/// key 16 bit
|
||||
/// depth 8 bit
|
||||
/// generation 5 bit
|
||||
/// pv node 1 bit
|
||||
/// bound type 2 bit
|
||||
/// move 16 bit
|
||||
/// value 16 bit
|
||||
/// eval value 16 bit
|
||||
/// generation 5 bit
|
||||
/// pv node 1 bit
|
||||
/// bound type 2 bit
|
||||
/// depth 8 bit
|
||||
|
||||
struct TTEntry {
|
||||
|
||||
@@ -49,11 +47,11 @@ private:
|
||||
friend class TranspositionTable;
|
||||
|
||||
uint16_t key16;
|
||||
uint8_t depth8;
|
||||
uint8_t genBound8;
|
||||
uint16_t move16;
|
||||
int16_t value16;
|
||||
int16_t eval16;
|
||||
uint8_t genBound8;
|
||||
uint8_t depth8;
|
||||
};
|
||||
|
||||
|
||||
@@ -74,15 +72,9 @@ class TranspositionTable {
|
||||
|
||||
static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size");
|
||||
|
||||
// Constants used to refresh the hash table periodically
|
||||
static constexpr unsigned GENERATION_BITS = 3; // nb of bits reserved for other things
|
||||
static constexpr int GENERATION_DELTA = (1 << GENERATION_BITS); // increment for generation field
|
||||
static constexpr int GENERATION_CYCLE = 255 + (1 << GENERATION_BITS); // cycle length
|
||||
static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF; // mask to pull out generation number
|
||||
|
||||
public:
|
||||
~TranspositionTable() { aligned_large_pages_free(table); }
|
||||
void new_search() { generation8 += GENERATION_DELTA; } // Lower bits are used for other things
|
||||
~TranspositionTable() { aligned_ttmem_free(mem); }
|
||||
void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound
|
||||
TTEntry* probe(const Key key, bool& found) const;
|
||||
int hashfull() const;
|
||||
void resize(size_t mbSize);
|
||||
@@ -97,11 +89,10 @@ private:
|
||||
|
||||
size_t clusterCount;
|
||||
Cluster* table;
|
||||
void* mem;
|
||||
uint8_t generation8; // Size must be not bigger than TTEntry::genBound8
|
||||
};
|
||||
|
||||
extern TranspositionTable TT;
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef TT_H_INCLUDED
|
||||
|
||||
+19
-8
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -26,10 +26,9 @@
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
bool Tune::update_on_last;
|
||||
const UCI::Option* LastOption = nullptr;
|
||||
BoolConditions Conditions;
|
||||
static std::map<std::string, int> TuneResults;
|
||||
|
||||
string Tune::next(string& names, bool pop) {
|
||||
@@ -109,7 +108,23 @@ template<> void Tune::Entry<Score>::read_option() {
|
||||
template<> void Tune::Entry<Tune::PostUpdate>::init_option() {}
|
||||
template<> void Tune::Entry<Tune::PostUpdate>::read_option() { value(); }
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
// Set binary conditions according to a probability that depends
|
||||
// on the corresponding parameter value.
|
||||
|
||||
void BoolConditions::set() {
|
||||
|
||||
static PRNG rng(now());
|
||||
static bool startup = true; // To workaround fishtest bench
|
||||
|
||||
for (size_t i = 0; i < binary.size(); i++)
|
||||
binary[i] = !startup && (values[i] + int(rng.rand<unsigned>() % variance) > threshold);
|
||||
|
||||
startup = false;
|
||||
|
||||
for (size_t i = 0; i < binary.size(); i++)
|
||||
sync_cout << binary[i] << sync_endl;
|
||||
}
|
||||
|
||||
|
||||
// Init options with tuning session results instead of default values. Useful to
|
||||
@@ -123,11 +138,7 @@ template<> void Tune::Entry<Tune::PostUpdate>::read_option() { value(); }
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
void Tune::read_results() {
|
||||
|
||||
/* ...insert your values here... */
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+38
-8
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -24,8 +24,6 @@
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
typedef std::pair<int, int> Range; // Option's min-max values
|
||||
typedef Range (RangeFun) (int);
|
||||
|
||||
@@ -46,6 +44,27 @@ struct SetRange {
|
||||
#define SetDefaultRange SetRange(default_range)
|
||||
|
||||
|
||||
/// BoolConditions struct is used to tune boolean conditions in the
|
||||
/// code by toggling them on/off according to a probability that
|
||||
/// depends on the value of a tuned integer parameter: for high
|
||||
/// values of the parameter condition is always disabled, for low
|
||||
/// values is always enabled, otherwise it is enabled with a given
|
||||
/// probability that depnends on the parameter under tuning.
|
||||
|
||||
struct BoolConditions {
|
||||
void init(size_t size) { values.resize(size, defaultValue), binary.resize(size, 0); }
|
||||
void set();
|
||||
|
||||
std::vector<int> binary, values;
|
||||
int defaultValue = 465, variance = 40, threshold = 500;
|
||||
SetRange range = SetRange(0, 1000);
|
||||
};
|
||||
|
||||
extern BoolConditions Conditions;
|
||||
|
||||
inline void set_conditions() { Conditions.set(); }
|
||||
|
||||
|
||||
/// Tune class implements the 'magic' code that makes the setup of a fishtest
|
||||
/// tuning session as easy as it can be. Mainly you have just to remove const
|
||||
/// qualifiers from the variables you want to tune and flag them for tuning, so
|
||||
@@ -84,7 +103,7 @@ class Tune {
|
||||
|
||||
static Tune& instance() { static Tune t; return t; } // Singleton
|
||||
|
||||
// Use polymorphism to accommodate Entry of different types in the same vector
|
||||
// Use polymorphism to accomodate Entry of different types in the same vector
|
||||
struct EntryBase {
|
||||
virtual ~EntryBase() = default;
|
||||
virtual void init_option() = 0;
|
||||
@@ -111,9 +130,9 @@ class Tune {
|
||||
SetRange range;
|
||||
};
|
||||
|
||||
// Our facility to fill the container, each Entry corresponds to a parameter
|
||||
// to tune. We use variadic templates to deal with an unspecified number of
|
||||
// entries, each one of a possible different type.
|
||||
// Our facilty to fill the container, each Entry corresponds to a parameter to tune.
|
||||
// We use variadic templates to deal with an unspecified number of entries, each one
|
||||
// of a possible different type.
|
||||
static std::string next(std::string& names, bool pop = true);
|
||||
|
||||
int add(const SetRange&, std::string&&) { return 0; }
|
||||
@@ -138,6 +157,14 @@ class Tune {
|
||||
return add(value, (next(names), std::move(names)), args...);
|
||||
}
|
||||
|
||||
// Template specialization for BoolConditions
|
||||
template<typename... Args>
|
||||
int add(const SetRange& range, std::string&& names, BoolConditions& cond, Args&&... args) {
|
||||
for (size_t size = cond.values.size(), i = 0; i < size; i++)
|
||||
add(cond.range, next(names, i == size - 1) + "_" + std::to_string(i), cond.values[i]);
|
||||
return add(range, std::move(names), args...);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<EntryBase>> list;
|
||||
|
||||
public:
|
||||
@@ -158,6 +185,9 @@ public:
|
||||
|
||||
#define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true
|
||||
|
||||
} // namespace Stockfish
|
||||
// Some macro to tune toggling of boolean conditions
|
||||
#define CONDITION(x) (Conditions.binary[__COUNTER__] || (x))
|
||||
#define TUNE_CONDITIONS() int UNIQUE(c, __LINE__) = (Conditions.init(__COUNTER__), 0); \
|
||||
TUNE(Conditions, set_conditions)
|
||||
|
||||
#endif // #ifndef TUNE_H_INCLUDED
|
||||
|
||||
+120
-26
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -57,12 +57,6 @@
|
||||
/// _WIN32 Building on Windows (any)
|
||||
/// _WIN64 Building on Windows 64 bit
|
||||
|
||||
#if defined(__GNUC__ ) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <= 2)) && defined(_WIN32) && !defined(__clang__)
|
||||
#define ALIGNAS_ON_STACK_VARIABLES_BROKEN
|
||||
#endif
|
||||
|
||||
#define ASSERT_ALIGNED(ptr, alignment) assert(reinterpret_cast<uintptr_t>(ptr) % alignment == 0)
|
||||
|
||||
#if defined(_WIN64) && defined(_MSC_VER) // No Makefile used
|
||||
# include <intrin.h> // Microsoft header for _BitScanForward64()
|
||||
# define IS_64BIT
|
||||
@@ -83,8 +77,6 @@
|
||||
# define pext(b, m) 0
|
||||
#endif
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
#ifdef USE_POPCNT
|
||||
constexpr bool HasPopCnt = true;
|
||||
#else
|
||||
@@ -115,7 +107,7 @@ constexpr int MAX_PLY = 246;
|
||||
/// bit 6-11: origin square (from 0 to 63)
|
||||
/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
|
||||
/// bit 14-15: special move flag: promotion (1), en passant (2), castling (3)
|
||||
/// NOTE: en passant bit is set only when a pawn can be captured
|
||||
/// NOTE: EN-PASSANT bit is set only when a pawn can be captured
|
||||
///
|
||||
/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in
|
||||
/// any normal move destination square is always different from origin square
|
||||
@@ -129,7 +121,7 @@ enum Move : int {
|
||||
enum MoveType {
|
||||
NORMAL,
|
||||
PROMOTION = 1 << 14,
|
||||
EN_PASSANT = 2 << 14,
|
||||
ENPASSANT = 2 << 14,
|
||||
CASTLING = 3 << 14
|
||||
};
|
||||
|
||||
@@ -186,11 +178,12 @@ enum Value : int {
|
||||
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
|
||||
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY,
|
||||
|
||||
PawnValueMg = 126, PawnValueEg = 208,
|
||||
PawnValueMg = 124, PawnValueEg = 206,
|
||||
KnightValueMg = 781, KnightValueEg = 854,
|
||||
BishopValueMg = 825, BishopValueEg = 915,
|
||||
RookValueMg = 1276, RookValueEg = 1380,
|
||||
QueenValueMg = 2538, QueenValueEg = 2682,
|
||||
Tempo = 28,
|
||||
|
||||
MidgameLimit = 15258, EndgameLimit = 3915
|
||||
};
|
||||
@@ -203,11 +196,27 @@ enum PieceType {
|
||||
|
||||
enum Piece {
|
||||
NO_PIECE,
|
||||
W_PAWN = PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
|
||||
B_PAWN = PAWN + 8, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
|
||||
W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
|
||||
B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
|
||||
PIECE_NB = 16
|
||||
};
|
||||
|
||||
// An ID used to track the pieces. Max. 32 pieces on board.
|
||||
enum PieceId {
|
||||
PIECE_ID_ZERO = 0,
|
||||
PIECE_ID_KING = 30,
|
||||
PIECE_ID_WKING = 30,
|
||||
PIECE_ID_BKING = 31,
|
||||
PIECE_ID_NONE = 32
|
||||
};
|
||||
|
||||
inline PieceId operator++(PieceId& d, int) {
|
||||
|
||||
PieceId x = d;
|
||||
d = PieceId(int(d) + 1);
|
||||
return x;
|
||||
}
|
||||
|
||||
constexpr Value PieceValue[PHASE_NB][PIECE_NB] = {
|
||||
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO,
|
||||
VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO },
|
||||
@@ -223,8 +232,7 @@ enum : int {
|
||||
DEPTH_QS_RECAPTURES = -5,
|
||||
|
||||
DEPTH_NONE = -6,
|
||||
|
||||
DEPTH_OFFSET = -7 // value used only for TT entry occupancy check
|
||||
DEPTH_OFFSET = DEPTH_NONE
|
||||
};
|
||||
|
||||
enum Square : int {
|
||||
@@ -262,20 +270,93 @@ enum Rank : int {
|
||||
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB
|
||||
};
|
||||
|
||||
// Keep track of what a move changes on the board (used by NNUE)
|
||||
// unique number for each piece type on each square
|
||||
enum PieceSquare : uint32_t {
|
||||
PS_NONE = 0,
|
||||
PS_W_PAWN = 1,
|
||||
PS_B_PAWN = 1 * SQUARE_NB + 1,
|
||||
PS_W_KNIGHT = 2 * SQUARE_NB + 1,
|
||||
PS_B_KNIGHT = 3 * SQUARE_NB + 1,
|
||||
PS_W_BISHOP = 4 * SQUARE_NB + 1,
|
||||
PS_B_BISHOP = 5 * SQUARE_NB + 1,
|
||||
PS_W_ROOK = 6 * SQUARE_NB + 1,
|
||||
PS_B_ROOK = 7 * SQUARE_NB + 1,
|
||||
PS_W_QUEEN = 8 * SQUARE_NB + 1,
|
||||
PS_B_QUEEN = 9 * SQUARE_NB + 1,
|
||||
PS_W_KING = 10 * SQUARE_NB + 1,
|
||||
PS_END = PS_W_KING, // pieces without kings (pawns included)
|
||||
PS_B_KING = 11 * SQUARE_NB + 1,
|
||||
PS_END2 = 12 * SQUARE_NB + 1
|
||||
};
|
||||
|
||||
struct ExtPieceSquare {
|
||||
PieceSquare from[COLOR_NB];
|
||||
};
|
||||
|
||||
// Array for finding the PieceSquare corresponding to the piece on the board
|
||||
extern ExtPieceSquare kpp_board_index[PIECE_NB];
|
||||
|
||||
constexpr bool is_ok(PieceId pid);
|
||||
constexpr Square rotate180(Square sq);
|
||||
|
||||
// Structure holding which tracked piece (PieceId) is where (PieceSquare)
|
||||
class EvalList {
|
||||
|
||||
public:
|
||||
// Max. number of pieces without kings is 30 but must be a multiple of 4 in AVX2
|
||||
static const int MAX_LENGTH = 32;
|
||||
|
||||
// Array that holds the piece id for the pieces on the board
|
||||
PieceId piece_id_list[SQUARE_NB];
|
||||
|
||||
// List of pieces, separate from White and Black POV
|
||||
PieceSquare* piece_list_fw() const { return const_cast<PieceSquare*>(pieceListFw); }
|
||||
PieceSquare* piece_list_fb() const { return const_cast<PieceSquare*>(pieceListFb); }
|
||||
|
||||
// Place the piece pc with piece_id on the square sq on the board
|
||||
void put_piece(PieceId piece_id, Square sq, Piece pc)
|
||||
{
|
||||
assert(is_ok(piece_id));
|
||||
if (pc != NO_PIECE)
|
||||
{
|
||||
pieceListFw[piece_id] = PieceSquare(kpp_board_index[pc].from[WHITE] + sq);
|
||||
pieceListFb[piece_id] = PieceSquare(kpp_board_index[pc].from[BLACK] + rotate180(sq));
|
||||
piece_id_list[sq] = piece_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
pieceListFw[piece_id] = PS_NONE;
|
||||
pieceListFb[piece_id] = PS_NONE;
|
||||
piece_id_list[sq] = piece_id;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the specified piece_id piece to ExtPieceSquare type and return it
|
||||
ExtPieceSquare piece_with_id(PieceId piece_id) const
|
||||
{
|
||||
ExtPieceSquare eps;
|
||||
eps.from[WHITE] = pieceListFw[piece_id];
|
||||
eps.from[BLACK] = pieceListFb[piece_id];
|
||||
return eps;
|
||||
}
|
||||
|
||||
private:
|
||||
PieceSquare pieceListFw[MAX_LENGTH];
|
||||
PieceSquare pieceListFb[MAX_LENGTH];
|
||||
};
|
||||
|
||||
// For differential evaluation of pieces that changed since last turn
|
||||
struct DirtyPiece {
|
||||
|
||||
// Number of changed pieces
|
||||
int dirty_num;
|
||||
|
||||
// Max 3 pieces can change in one move. A promotion with capture moves
|
||||
// both the pawn and the captured piece to SQ_NONE and the piece promoted
|
||||
// to from SQ_NONE to the capture square.
|
||||
Piece piece[3];
|
||||
// The ids of changed pieces, max. 2 pieces can change in one move
|
||||
PieceId pieceId[2];
|
||||
|
||||
// From and to squares, which may be SQ_NONE
|
||||
Square from[3];
|
||||
Square to[3];
|
||||
// What changed from the piece with that piece number
|
||||
ExtPieceSquare old_piece[2];
|
||||
ExtPieceSquare new_piece[2];
|
||||
};
|
||||
|
||||
/// Score enum stores a middlegame and an endgame value in a single integer (enum).
|
||||
@@ -325,6 +406,8 @@ ENABLE_FULL_OPERATORS_ON(Value)
|
||||
ENABLE_FULL_OPERATORS_ON(Direction)
|
||||
|
||||
ENABLE_INCR_OPERATORS_ON(Piece)
|
||||
ENABLE_INCR_OPERATORS_ON(PieceSquare)
|
||||
ENABLE_INCR_OPERATORS_ON(PieceId)
|
||||
ENABLE_INCR_OPERATORS_ON(PieceType)
|
||||
ENABLE_INCR_OPERATORS_ON(Square)
|
||||
ENABLE_INCR_OPERATORS_ON(File)
|
||||
@@ -413,6 +496,10 @@ inline Color color_of(Piece pc) {
|
||||
return Color(pc >> 3);
|
||||
}
|
||||
|
||||
constexpr bool is_ok(PieceId pid) {
|
||||
return pid < PIECE_ID_NONE;
|
||||
}
|
||||
|
||||
constexpr bool is_ok(Square s) {
|
||||
return s >= SQ_A1 && s <= SQ_H8;
|
||||
}
|
||||
@@ -449,6 +536,11 @@ constexpr Square to_sq(Move m) {
|
||||
return Square(m & 0x3F);
|
||||
}
|
||||
|
||||
// Return relative square when turning the board 180 degrees
|
||||
constexpr Square rotate180(Square sq) {
|
||||
return (Square)(sq ^ 0x3F);
|
||||
}
|
||||
|
||||
constexpr int from_to(Move m) {
|
||||
return m & 0xFFF;
|
||||
}
|
||||
@@ -465,6 +557,10 @@ constexpr Move make_move(Square from, Square to) {
|
||||
return Move((from << 6) + to);
|
||||
}
|
||||
|
||||
constexpr Move reverse_move(Move m) {
|
||||
return make_move(to_sq(m), from_sq(m));
|
||||
}
|
||||
|
||||
template<MoveType T>
|
||||
constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) {
|
||||
return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);
|
||||
@@ -479,8 +575,6 @@ constexpr Key make_key(uint64_t seed) {
|
||||
return seed * 6364136223846793005ULL + 1442695040888963407ULL;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef TYPES_H_INCLUDED
|
||||
|
||||
#include "tune.h" // Global visibility to tuning setup
|
||||
|
||||
+7
-19
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -34,8 +34,6 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
extern vector<string> setup_bench(const Position&, istream&);
|
||||
|
||||
namespace {
|
||||
@@ -87,7 +85,7 @@ namespace {
|
||||
Position p;
|
||||
p.set(pos.fen(), Options["UCI_Chess960"], &states->back(), Threads.main());
|
||||
|
||||
Eval::NNUE::verify();
|
||||
Eval::verify_NNUE();
|
||||
|
||||
sync_cout << "\n" << Eval::trace(p) << sync_endl;
|
||||
}
|
||||
@@ -172,7 +170,7 @@ namespace {
|
||||
|
||||
if (token == "go" || token == "eval")
|
||||
{
|
||||
cerr << "\nPosition: " << cnt++ << '/' << num << " (" << pos.fen() << ")" << endl;
|
||||
cerr << "\nPosition: " << cnt++ << '/' << num << endl;
|
||||
if (token == "go")
|
||||
{
|
||||
go(pos, is, states);
|
||||
@@ -207,13 +205,13 @@ namespace {
|
||||
// Coefficients of a 3rd order polynomial fit based on fishtest data
|
||||
// for two parameters needed to transform eval to the argument of a
|
||||
// logistic function.
|
||||
double as[] = {-1.17202460e-01, 5.94729104e-01, 1.12065546e+01, 1.22606222e+02};
|
||||
double bs[] = {-1.79066759, 11.30759193, -17.43677612, 36.47147479};
|
||||
double as[] = {-8.24404295, 64.23892342, -95.73056462, 153.86478679};
|
||||
double bs[] = {-3.37154371, 28.44489198, -56.67657741, 72.05858751};
|
||||
double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3];
|
||||
double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3];
|
||||
|
||||
// Transform eval to centipawns with limited range
|
||||
double x = std::clamp(double(100 * v) / PawnValueEg, -2000.0, 2000.0);
|
||||
double x = Utility::clamp(double(100 * v) / PawnValueEg, -1000.0, 1000.0);
|
||||
|
||||
// Return win rate in per mille (rounded to nearest)
|
||||
return int(0.5 + 1000 / (1 + std::exp((a - x) / b)));
|
||||
@@ -277,15 +275,7 @@ void UCI::loop(int argc, char* argv[]) {
|
||||
else if (token == "d") sync_cout << pos << sync_endl;
|
||||
else if (token == "eval") trace_eval(pos);
|
||||
else if (token == "compiler") sync_cout << compiler_info() << sync_endl;
|
||||
else if (token == "export_net")
|
||||
{
|
||||
std::optional<std::string> filename;
|
||||
std::string f;
|
||||
if (is >> skipws >> f)
|
||||
filename = f;
|
||||
Eval::NNUE::save_eval(filename);
|
||||
}
|
||||
else if (!token.empty() && token[0] != '#')
|
||||
else
|
||||
sync_cout << "Unknown command: " << cmd << sync_endl;
|
||||
|
||||
} while (token != "quit" && argc == 1); // Command line args are one-shot
|
||||
@@ -379,5 +369,3 @@ Move UCI::to_move(const Position& pos, string& str) {
|
||||
|
||||
return MOVE_NONE;
|
||||
}
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -24,8 +24,6 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
class Position;
|
||||
|
||||
namespace UCI {
|
||||
@@ -80,6 +78,4 @@ Move to_move(const Position& pos, std::string& str);
|
||||
|
||||
extern UCI::OptionsMap Options;
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
#endif // #ifndef UCI_H_INCLUDED
|
||||
|
||||
+8
-11
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
||||
Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file)
|
||||
Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file)
|
||||
|
||||
Stockfish is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "evaluate.h"
|
||||
#include "misc.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
@@ -31,8 +30,6 @@
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace Stockfish {
|
||||
|
||||
UCI::OptionsMap Options; // Global object
|
||||
|
||||
namespace UCI {
|
||||
@@ -43,8 +40,8 @@ void on_hash_size(const Option& o) { TT.resize(size_t(o)); }
|
||||
void on_logger(const Option& o) { start_logger(o); }
|
||||
void on_threads(const Option& o) { Threads.set(size_t(o)); }
|
||||
void on_tb_path(const Option& o) { Tablebases::init(o); }
|
||||
void on_use_NNUE(const Option& ) { Eval::NNUE::init(); }
|
||||
void on_eval_file(const Option& ) { Eval::NNUE::init(); }
|
||||
void on_use_NNUE(const Option& ) { Eval::init_NNUE(); }
|
||||
void on_eval_file(const Option& ) { Eval::init_NNUE(); }
|
||||
|
||||
/// Our case insensitive less() function as required by UCI protocol
|
||||
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
|
||||
@@ -61,6 +58,8 @@ void init(OptionsMap& o) {
|
||||
constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048;
|
||||
|
||||
o["Debug Log File"] << Option("", on_logger);
|
||||
o["Contempt"] << Option(24, -100, 100);
|
||||
o["Analysis Contempt"] << Option("Both var Off var White var Black var Both", "Both");
|
||||
o["Threads"] << Option(1, 1, 512, on_threads);
|
||||
o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size);
|
||||
o["Clear Hash"] << Option(on_clear_hash);
|
||||
@@ -79,8 +78,8 @@ void init(OptionsMap& o) {
|
||||
o["SyzygyProbeDepth"] << Option(1, 1, 100);
|
||||
o["Syzygy50MoveRule"] << Option(true);
|
||||
o["SyzygyProbeLimit"] << Option(7, 0, 7);
|
||||
o["Use NNUE"] << Option(true, on_use_NNUE);
|
||||
o["EvalFile"] << Option(EvalFileDefaultName, on_eval_file);
|
||||
o["Use NNUE"] << Option(false, on_use_NNUE);
|
||||
o["EvalFile"] << Option("nn-97f742aaefcd.nnue", on_eval_file);
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +163,7 @@ Option& Option::operator=(const string& v) {
|
||||
|
||||
assert(!type.empty());
|
||||
|
||||
if ( (type != "button" && type != "string" && v.empty())
|
||||
if ( (type != "button" && v.empty())
|
||||
|| (type == "check" && v != "true" && v != "false")
|
||||
|| (type == "spin" && (stof(v) < min || stof(v) > max)))
|
||||
return *this;
|
||||
@@ -190,5 +189,3 @@ Option& Option::operator=(const string& v) {
|
||||
}
|
||||
|
||||
} // namespace UCI
|
||||
|
||||
} // namespace Stockfish
|
||||
|
||||
+15
-15
@@ -13,14 +13,14 @@ case $1 in
|
||||
--valgrind)
|
||||
echo "valgrind testing started"
|
||||
prefix=''
|
||||
exeprefix='valgrind --error-exitcode=42 --errors-for-leak-kinds=all --leak-check=full'
|
||||
exeprefix='valgrind --error-exitcode=42'
|
||||
postfix='1>/dev/null'
|
||||
threads="1"
|
||||
;;
|
||||
--valgrind-thread)
|
||||
echo "valgrind-thread testing started"
|
||||
prefix=''
|
||||
exeprefix='valgrind --fair-sched=try --error-exitcode=42'
|
||||
exeprefix='valgrind --error-exitcode=42'
|
||||
postfix='1>/dev/null'
|
||||
threads="2"
|
||||
;;
|
||||
@@ -39,16 +39,16 @@ case $1 in
|
||||
threads="2"
|
||||
|
||||
cat << EOF > tsan.supp
|
||||
race:Stockfish::TTEntry::move
|
||||
race:Stockfish::TTEntry::depth
|
||||
race:Stockfish::TTEntry::bound
|
||||
race:Stockfish::TTEntry::save
|
||||
race:Stockfish::TTEntry::value
|
||||
race:Stockfish::TTEntry::eval
|
||||
race:Stockfish::TTEntry::is_pv
|
||||
race:TTEntry::move
|
||||
race:TTEntry::depth
|
||||
race:TTEntry::bound
|
||||
race:TTEntry::save
|
||||
race:TTEntry::value
|
||||
race:TTEntry::eval
|
||||
race:TTEntry::is_pv
|
||||
|
||||
race:Stockfish::TranspositionTable::probe
|
||||
race:Stockfish::TranspositionTable::hashfull
|
||||
race:TranspositionTable::probe
|
||||
race:TranspositionTable::hashfull
|
||||
|
||||
EOF
|
||||
|
||||
@@ -70,7 +70,7 @@ for args in "eval" \
|
||||
"go depth 10" \
|
||||
"go movetime 1000" \
|
||||
"go wtime 8000 btime 8000 winc 500 binc 500" \
|
||||
"bench 128 $threads 8 default depth"
|
||||
"bench 128 $threads 10 default depth"
|
||||
do
|
||||
|
||||
echo "$prefix $exeprefix ./stockfish $args $postfix"
|
||||
@@ -80,7 +80,7 @@ done
|
||||
|
||||
# more general testing, following an uci protocol exchange
|
||||
cat << EOF > game.exp
|
||||
set timeout 240
|
||||
set timeout 10
|
||||
spawn $exeprefix ./stockfish
|
||||
|
||||
send "uci\n"
|
||||
@@ -98,7 +98,7 @@ cat << EOF > game.exp
|
||||
expect "bestmove"
|
||||
|
||||
send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n"
|
||||
send "go depth 10\n"
|
||||
send "go depth 30\n"
|
||||
expect "bestmove"
|
||||
|
||||
send "quit\n"
|
||||
@@ -121,7 +121,7 @@ cat << EOF > syzygy.exp
|
||||
send "uci\n"
|
||||
send "setoption name SyzygyPath value ../tests/syzygy/\n"
|
||||
expect "info string Found 35 tablebases" {} timeout {exit 1}
|
||||
send "bench 128 1 8 default depth\n"
|
||||
send "bench 128 1 10 default depth\n"
|
||||
send "quit\n"
|
||||
expect eof
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ trap 'error ${LINENO}' ERR
|
||||
|
||||
echo "reprosearch testing started"
|
||||
|
||||
# repeat two short games, separated by ucinewgame.
|
||||
# repeat two short games, separated by ucinewgame.
|
||||
# with go nodes $nodes they should result in exactly
|
||||
# the same node count for each iteration.
|
||||
cat << EOF > repeat.exp
|
||||
@@ -43,7 +43,7 @@ cat << EOF > repeat.exp
|
||||
expect eof
|
||||
EOF
|
||||
|
||||
# to increase the likelihood of finding a non-reproducible case,
|
||||
# to increase the likelyhood of finding a non-reproducible case,
|
||||
# the allowed number of nodes are varied systematically
|
||||
for i in `seq 1 20`
|
||||
do
|
||||
|
||||
Reference in New Issue
Block a user