├── .github ├── ISSUE_TEMPLATE │ ├── BUG-REPORT.yml │ └── config.yml └── workflows │ ├── build-windows.yml │ ├── make.yml │ ├── stockfish.yml │ ├── stockfish_arm_binaries.yml │ ├── stockfish_binaries.yml │ ├── stockfish_compile_test.yml │ ├── stockfish_sanitizers.yml │ └── stockfish_test.yml ├── .gitignore ├── .vscode └── settings.json ├── AUTHORS ├── CITATION.cff ├── Copying.txt ├── README.md ├── Top CPU Contributors.txt ├── appveyor.yml ├── deploy ├── appveyor.yml ├── linux.sh ├── msys.sh ├── old-gcc.patch └── stable-slim.dockerfile ├── src ├── Makefile ├── benchmark.cpp ├── benchmark.h ├── bitbase.cpp ├── bitboard.cpp ├── bitboard.h ├── endgame.cpp ├── endgame.h ├── evaluate.cpp ├── evaluate.h ├── incbin │ ├── UNLICENCE │ └── incbin.h ├── main.cpp ├── material.cpp ├── material.h ├── misc.cpp ├── misc.h ├── movegen.cpp ├── movegen.h ├── movepick.cpp ├── movepick.h ├── nnue │ ├── evaluate_nnue.cpp │ ├── evaluate_nnue.h │ ├── features │ │ ├── half_ka_v2_hm.cpp │ │ └── half_ka_v2_hm.h │ ├── layers │ │ ├── affine_transform.h │ │ ├── affine_transform_sparse_input.h │ │ ├── clipped_relu.h │ │ ├── simd.h │ │ └── sqr_clipped_relu.h │ ├── nnue_accumulator.h │ ├── nnue_architecture.h │ ├── nnue_common.h │ └── nnue_feature_transformer.h ├── pawns.cpp ├── pawns.h ├── position.cpp ├── position.h ├── psqt.cpp ├── psqt.h ├── search.cpp ├── search.h ├── syzygy │ ├── tbprobe.cpp │ └── tbprobe.h ├── thread.cpp ├── thread.h ├── thread_win32_osx.h ├── timeman.cpp ├── timeman.h ├── tt.cpp ├── tt.h ├── tune.cpp ├── tune.h ├── types.h ├── uci.cpp ├── uci.h └── ucioption.cpp └── tests ├── instrumented.sh ├── perft.sh ├── puzzle.sh ├── reprosearch.sh └── signature.sh /.github/ISSUE_TEMPLATE/BUG-REPORT.yml: -------------------------------------------------------------------------------- 1 | name: Report issue 2 | description: Create a report to help us fix issues with the engine 3 | body: 4 | - type: textarea 5 | attributes: 6 | label: Describe the issue 7 | description: A clear and concise description of what you're experiencing. 8 | validations: 9 | required: true 10 | 11 | - type: textarea 12 | attributes: 13 | label: Expected behavior 14 | description: A clear and concise description of what you expected to happen. 15 | validations: 16 | required: true 17 | 18 | - type: textarea 19 | attributes: 20 | label: Steps to reproduce 21 | description: | 22 | Steps to reproduce the behavior. 23 | You can also use this section to paste the command line output. 24 | placeholder: | 25 | ``` 26 | position startpos moves g2g4 e7e5 f2f3 27 | go mate 1 28 | info string NNUE evaluation using nn-6877cd24400e.nnue enabled 29 | info depth 1 seldepth 1 multipv 1 score mate 1 nodes 33 nps 11000 tbhits 0 time 3 pv d8h4 30 | bestmove d8h4 31 | ``` 32 | validations: 33 | required: true 34 | 35 | - type: textarea 36 | attributes: 37 | label: Anything else? 38 | description: | 39 | Anything that will give us more context about the issue you are encountering. 40 | You can also use this section to propose ideas on how to solve the issue. 41 | validations: 42 | required: false 43 | 44 | - type: dropdown 45 | attributes: 46 | label: Operating system 47 | options: 48 | - All 49 | - Windows 50 | - Linux 51 | - MacOS 52 | - Android 53 | - Other or N/A 54 | validations: 55 | required: true 56 | 57 | - type: input 58 | attributes: 59 | label: Stockfish version 60 | description: | 61 | This can be found by running the engine. 62 | You can also use the commit ID. 63 | placeholder: Stockfish 15 / e6e324e 64 | validations: 65 | required: true 66 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Discord server 4 | url: https://discord.gg/GWDRS3kU6R 5 | about: Feel free to ask for support or have a chat with us in our Discord server! 6 | - name: Discussions, Q&A, ideas, show us something... 7 | url: https://github.com/official-stockfish/Stockfish/discussions/new 8 | about: Do you have an idea for Stockfish? Do you want to show something that you made? Please open a discussion about it! 9 | -------------------------------------------------------------------------------- /.github/workflows/build-windows.yml: -------------------------------------------------------------------------------- 1 | name: build-windows 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: install 17 | run: sudo apt install mingw-w64 18 | 19 | - name: make default 20 | run: cd src && make clean && make -j build COMP=mingw ARCH=x86-64 EXE=stockfish_x64.exe && strip stockfish_x64.exe 21 | 22 | - name: make modern 23 | run: cd src && make clean && make -j build COMP=mingw ARCH=x86-64-modern EXE=stockfish_x64_modern.exe && strip stockfish_x64_modern.exe 24 | 25 | - name: make bmi2 26 | run: cd src && make clean && make -j build COMP=mingw ARCH=x86-64-bmi2 EXE=stockfish_x64_bmi2.exe && strip stockfish_x64_bmi2.exe 27 | 28 | - uses: actions/upload-artifact@v2 29 | with: 30 | name: stockfish 31 | path: src/stockfish*.exe 32 | -------------------------------------------------------------------------------- /.github/workflows/make.yml: -------------------------------------------------------------------------------- 1 | name: Make CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ master, no-nnue ] 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-python@v2 16 | with: 17 | python-version: '3.x' 18 | - name: configure 19 | run: sudo apt install expect && pip3 install chess && git clone https://github.com/ddugovic/chess-artist.git ./tests/chess-artist --depth 1 20 | - name: make 21 | run: cd src && make -j3 build ARCH=x86-64-bmi2 COMP=clang debug=yes 22 | - name: make net 23 | run: cd src && make net 24 | - name: make check 25 | run: cd src && ../tests/perft.sh && ../tests/reprosearch.sh && ../tests/puzzle.sh 26 | -------------------------------------------------------------------------------- /.github/workflows/stockfish.yml: -------------------------------------------------------------------------------- 1 | name: Stockfish 2 | on: 3 | push: 4 | tags: 5 | - '*' 6 | branches: 7 | - master 8 | - tools 9 | - github_ci 10 | pull_request: 11 | branches: 12 | - master 13 | - tools 14 | jobs: 15 | Prerelease: 16 | if: github.ref == 'refs/heads/master' 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v3 20 | with: 21 | fetch-depth: 0 22 | 23 | # returns null if no pre-release exists 24 | - name: Get Commit SHA of Latest Pre-release 25 | run: | 26 | # Install required packages 27 | sudo apt-get update 28 | sudo apt-get install -y curl jq 29 | 30 | echo "COMMIT_SHA=$(jq -r 'map(select(.prerelease)) | first | .tag_name' <<< $(curl -s https://api.github.com/repos/${{ github.repository_owner }}/Stockfish/releases))" >> $GITHUB_ENV 31 | 32 | # delete old previous pre-release and tag 33 | - uses: dev-drprasad/delete-tag-and-release@v0.2.1 34 | if: env.COMMIT_SHA != 'null' 35 | with: 36 | tag_name: ${{ env.COMMIT_SHA }} 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | 40 | Sanitizers: 41 | uses: ./.github/workflows/stockfish_sanitizers.yml 42 | Tests: 43 | uses: ./.github/workflows/stockfish_test.yml 44 | Compiles: 45 | uses: ./.github/workflows/stockfish_compile_test.yml 46 | Binaries: 47 | if: github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag') 48 | uses: ./.github/workflows/stockfish_binaries.yml 49 | ARM_Binaries: 50 | if: github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag') 51 | uses: ./.github/workflows/stockfish_arm_binaries.yml 52 | -------------------------------------------------------------------------------- /.github/workflows/stockfish_arm_binaries.yml: -------------------------------------------------------------------------------- 1 | name: Stockfish 2 | on: 3 | workflow_call: 4 | jobs: 5 | Stockfish: 6 | name: ${{ matrix.config.name }} ${{ matrix.binaries }} 7 | runs-on: ${{ matrix.config.os }} 8 | env: 9 | COMPILER: ${{ matrix.config.compiler }} 10 | COMP: ${{ matrix.config.comp }} 11 | EMU: ${{ matrix.config.emu }} 12 | EXT: ${{ matrix.config.ext }} 13 | OS: ${{ matrix.config.os }} 14 | BINARY: ${{ matrix.binaries }} 15 | strategy: 16 | matrix: 17 | config: 18 | - name: Android NDK aarch64 19 | os: ubuntu-22.04 20 | compiler: aarch64-linux-android21-clang++ 21 | emu: qemu-aarch64 22 | comp: ndk 23 | shell: bash 24 | - name: Android NDK arm 25 | os: ubuntu-22.04 26 | compiler: armv7a-linux-androideabi21-clang++ 27 | emu: qemu-arm 28 | comp: ndk 29 | shell: bash 30 | binaries: 31 | - armv8-dotprod 32 | - armv8 33 | - armv7 34 | - armv7-neon 35 | exclude: 36 | - binaries: armv8-dotprod 37 | config: {compiler: armv7a-linux-androideabi21-clang++} 38 | - binaries: armv8 39 | config: {compiler: armv7a-linux-androideabi21-clang++} 40 | - binaries: armv7 41 | config: {compiler: aarch64-linux-android21-clang++} 42 | - binaries: armv7-neon 43 | config: {compiler: aarch64-linux-android21-clang++} 44 | defaults: 45 | run: 46 | working-directory: src 47 | shell: ${{ matrix.config.shell }} 48 | steps: 49 | - uses: actions/checkout@v3 50 | with: 51 | fetch-depth: 0 52 | 53 | - name: Download required linux packages 54 | if: runner.os == 'Linux' 55 | run: | 56 | sudo apt update 57 | sudo apt install qemu-user 58 | 59 | - name: Install NDK 60 | if: runner.os == 'Linux' 61 | run: | 62 | if [ $COMP == ndk ]; then 63 | NDKV="21.4.7075529" 64 | ANDROID_ROOT=/usr/local/lib/android 65 | ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk 66 | SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager 67 | echo "y" | $SDKMANAGER "ndk;$NDKV" 68 | ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$NDKV 69 | ANDROID_NDK_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin 70 | echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV 71 | fi 72 | 73 | - name: Download the used network from the fishtest framework 74 | run: make net 75 | 76 | - name: Check compiler 77 | run: | 78 | if [ $COMP == ndk ]; then 79 | export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH 80 | fi 81 | $COMPILER -v 82 | 83 | - name: Test help target 84 | run: make help 85 | 86 | - name: Check git 87 | run: git --version 88 | 89 | # Compile profile guided builds 90 | 91 | - name: Compile ${{ matrix.binaries }} build 92 | run: | 93 | if [ $COMP == ndk ]; then 94 | export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH 95 | export LDFLAGS="-static -Wno-unused-command-line-argument" 96 | fi 97 | make clean 98 | make -j2 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH=$EMU 99 | make strip ARCH=$BINARY COMP=$COMP 100 | mv ./stockfish$EXT ../stockfish-android-$BINARY$EXT 101 | 102 | - name: Remove non src files 103 | run: rm -f *.o .depend *.nnue 104 | 105 | - name: Download wiki 106 | run: | 107 | git clone https://github.com/official-stockfish/Stockfish.wiki.git ../wiki 108 | cd ../wiki 109 | rm -rf .git 110 | 111 | - name: Create tar archive. 112 | run: | 113 | cd .. 114 | mkdir stockfish 115 | cp -r wiki stockfish/ 116 | cp -r src stockfish/ 117 | cp stockfish-android-$BINARY$EXT stockfish/ 118 | cp "Top CPU Contributors.txt" stockfish/ 119 | cp Copying.txt stockfish/ 120 | cp AUTHORS stockfish/ 121 | cp CITATION.cff stockfish/ 122 | cp README.md stockfish/ 123 | tar -cvf stockfish-android-$BINARY.tar stockfish 124 | 125 | - name: Upload binaries 126 | uses: actions/upload-artifact@v3 127 | with: 128 | name: stockfish-android-${{ matrix.binaries }} 129 | path: stockfish-android-${{ matrix.binaries }}.tar 130 | 131 | - name: Release 132 | if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag' 133 | uses: softprops/action-gh-release@v1 134 | with: 135 | files: stockfish-android-${{ matrix.binaries }}.tar 136 | 137 | - name: Get last commit sha 138 | id: last_commit 139 | run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV 140 | 141 | - name: Get commit date 142 | id: commit_date 143 | run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV 144 | 145 | # Make sure that an old ci which still runs on master doesn't recreate a prerelease 146 | - name: Check Pullable Commits 147 | id: check_commits 148 | run: | 149 | git fetch 150 | CHANGES=$(git rev-list HEAD..origin/master --count) 151 | echo "CHANGES=$CHANGES" >> $GITHUB_ENV 152 | 153 | - name: Prerelease 154 | if: github.ref_name == 'master' && env.CHANGES == '0' 155 | continue-on-error: true 156 | uses: softprops/action-gh-release@v1 157 | with: 158 | name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} 159 | tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} 160 | prerelease: true 161 | files: stockfish-android-${{ matrix.binaries }}.tar 162 | -------------------------------------------------------------------------------- /.github/workflows/stockfish_binaries.yml: -------------------------------------------------------------------------------- 1 | name: Stockfish 2 | on: 3 | workflow_call: 4 | jobs: 5 | Stockfish: 6 | name: ${{ matrix.config.name }} ${{ matrix.binaries }} 7 | runs-on: ${{ matrix.config.os }} 8 | env: 9 | COMPILER: ${{ matrix.config.compiler }} 10 | COMP: ${{ matrix.config.comp }} 11 | EXT: ${{ matrix.config.ext }} 12 | NAME: ${{ matrix.config.simple_name }} 13 | BINARY: ${{ matrix.binaries }} 14 | strategy: 15 | matrix: 16 | config: 17 | - name: Ubuntu 20.04 GCC 18 | os: ubuntu-20.04 19 | simple_name: ubuntu 20 | compiler: g++ 21 | comp: gcc 22 | shell: bash 23 | archive_ext: tar 24 | - name: MacOS 12 Apple Clang 25 | os: macos-12 26 | simple_name: macos 27 | compiler: clang++ 28 | comp: clang 29 | shell: bash 30 | archive_ext: tar 31 | - name: Windows 2022 Mingw-w64 GCC x86_64 32 | os: windows-2022 33 | simple_name: windows 34 | compiler: g++ 35 | comp: mingw 36 | msys_sys: mingw64 37 | msys_env: x86_64-gcc 38 | shell: msys2 {0} 39 | ext: .exe 40 | archive_ext: zip 41 | binaries: 42 | - x86-64 43 | - x86-64-modern 44 | - x86-64-avx2 45 | - x86-64-bmi2 46 | exclude: 47 | - binaries: x86-64-avx2 48 | config: { os: macos-12 } 49 | - binaries: x86-64-bmi2 50 | config: { os: macos-12 } 51 | defaults: 52 | run: 53 | working-directory: src 54 | shell: ${{ matrix.config.shell }} 55 | steps: 56 | - uses: actions/checkout@v3 57 | with: 58 | fetch-depth: 0 59 | 60 | - name: Download required linux packages 61 | if: runner.os == 'Linux' 62 | run: sudo apt update 63 | 64 | - name: Setup msys and install required packages 65 | if: runner.os == 'Windows' 66 | uses: msys2/setup-msys2@v2 67 | with: 68 | msystem: ${{ matrix.config.msys_sys }} 69 | install: mingw-w64-${{ matrix.config.msys_env }} make git zip 70 | 71 | - name: Download the used network from the fishtest framework 72 | run: make net 73 | 74 | - name: Check compiler 75 | run: $COMPILER -v 76 | 77 | - name: Test help target 78 | run: make help 79 | 80 | - name: Check git 81 | run: git --version 82 | 83 | # Compile profile guided builds 84 | 85 | - name: Compile ${{ matrix.binaries }} build 86 | run: | 87 | make -j2 profile-build ARCH=$BINARY COMP=$COMP 88 | make strip ARCH=$BINARY COMP=$COMP 89 | mv ./stockfish$EXT ../stockfish-$NAME-$BINARY$EXT 90 | 91 | - name: Remove non src files 92 | run: git clean -fx 93 | 94 | - name: Download wiki 95 | run: | 96 | git clone https://github.com/official-stockfish/Stockfish.wiki.git ../wiki 97 | rm -rf ../wiki/.git 98 | 99 | - name: Create directory. 100 | run: | 101 | cd .. 102 | mkdir stockfish 103 | cp -r wiki stockfish/ 104 | cp -r src stockfish/ 105 | cp stockfish-$NAME-$BINARY$EXT stockfish/ 106 | cp "Top CPU Contributors.txt" stockfish/ 107 | cp Copying.txt stockfish/ 108 | cp AUTHORS stockfish/ 109 | cp CITATION.cff stockfish/ 110 | cp README.md stockfish/ 111 | 112 | - name: Create tar 113 | if: runner.os != 'Windows' 114 | run: | 115 | cd .. 116 | tar -cvf stockfish-$NAME-$BINARY.tar stockfish 117 | 118 | - name: Create zip 119 | if: runner.os == 'Windows' 120 | run: | 121 | cd .. 122 | zip -r stockfish-$NAME-$BINARY.zip stockfish 123 | 124 | - name: Upload binaries 125 | if: runner.os != 'Windows' 126 | uses: actions/upload-artifact@v3 127 | with: 128 | name: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }} 129 | path: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.tar 130 | 131 | # Artifacts automatically get zipped 132 | # to avoid double zipping, we use the unzipped directory 133 | - name: Upload binaries 134 | if: runner.os == 'Windows' 135 | uses: actions/upload-artifact@v3 136 | with: 137 | name: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }} 138 | path: stockfish 139 | 140 | - name: Release 141 | if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag' 142 | uses: softprops/action-gh-release@v1 143 | with: 144 | files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} 145 | 146 | - name: Get last commit sha 147 | id: last_commit 148 | run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV 149 | 150 | - name: Get commit date 151 | id: commit_date 152 | run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV 153 | 154 | # Make sure that an old ci which still runs on master doesn't recreate a prerelease 155 | - name: Check Pullable Commits 156 | id: check_commits 157 | run: | 158 | git fetch 159 | CHANGES=$(git rev-list HEAD..origin/master --count) 160 | echo "CHANGES=$CHANGES" >> $GITHUB_ENV 161 | 162 | - name: Prerelease 163 | if: github.ref_name == 'master' && env.CHANGES == '0' 164 | continue-on-error: true 165 | uses: softprops/action-gh-release@v1 166 | with: 167 | name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} 168 | tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} 169 | prerelease: true 170 | files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} 171 | -------------------------------------------------------------------------------- /.github/workflows/stockfish_compile_test.yml: -------------------------------------------------------------------------------- 1 | name: Stockfish 2 | on: 3 | workflow_call: 4 | jobs: 5 | Stockfish: 6 | name: ${{ matrix.config.name }} 7 | runs-on: ${{ matrix.config.os }} 8 | env: 9 | COMPILER: ${{ matrix.config.compiler }} 10 | COMP: ${{ matrix.config.comp }} 11 | strategy: 12 | matrix: 13 | config: 14 | - name: Ubuntu 20.04 GCC 15 | os: ubuntu-20.04 16 | compiler: g++ 17 | comp: gcc 18 | shell: bash 19 | - name: Ubuntu 20.04 Clang 20 | os: ubuntu-20.04 21 | compiler: clang++ 22 | comp: clang 23 | shell: bash 24 | - name: MacOS 12 Apple Clang 25 | os: macos-12 26 | compiler: clang++ 27 | comp: clang 28 | shell: bash 29 | - name: MacOS 12 GCC 11 30 | os: macos-12 31 | compiler: g++-11 32 | comp: gcc 33 | shell: bash 34 | - name: Windows 2022 Mingw-w64 GCC x86_64 35 | os: windows-2022 36 | compiler: g++ 37 | comp: mingw 38 | msys_sys: mingw64 39 | msys_env: x86_64-gcc 40 | shell: msys2 {0} 41 | - name: Windows 2022 Mingw-w64 Clang x86_64 42 | os: windows-2022 43 | compiler: clang++ 44 | comp: clang 45 | msys_sys: clang64 46 | msys_env: clang-x86_64-clang 47 | shell: msys2 {0} 48 | 49 | defaults: 50 | run: 51 | working-directory: src 52 | shell: ${{ matrix.config.shell }} 53 | steps: 54 | - uses: actions/checkout@v3 55 | with: 56 | fetch-depth: 0 57 | 58 | - name: Setup msys and install required packages 59 | if: runner.os == 'Windows' 60 | uses: msys2/setup-msys2@v2 61 | with: 62 | msystem: ${{matrix.config.msys_sys}} 63 | install: mingw-w64-${{matrix.config.msys_env}} make git 64 | 65 | - name: Download the used network from the fishtest framework 66 | run: make net 67 | 68 | - name: Check compiler 69 | run: $COMPILER -v 70 | 71 | - name: Test help target 72 | run: make help 73 | 74 | - name: Check git 75 | run: git --version 76 | 77 | # x86-64 with newer extensions tests 78 | 79 | - name: Compile x86-64-avx2 build 80 | run: | 81 | make clean 82 | make -j2 ARCH=x86-64-avx2 build 83 | 84 | - name: Compile x86-64-bmi2 build 85 | run: | 86 | make clean 87 | make -j2 ARCH=x86-64-bmi2 build 88 | 89 | - name: Compile x86-64-avx512 build 90 | run: | 91 | make clean 92 | make -j2 ARCH=x86-64-avx512 build 93 | 94 | - name: Compile x86-64-vnni512 build 95 | run: | 96 | make clean 97 | make -j2 ARCH=x86-64-vnni512 build 98 | 99 | - name: Compile x86-64-vnni256 build 100 | run: | 101 | make clean 102 | make -j2 ARCH=x86-64-vnni256 build 103 | -------------------------------------------------------------------------------- /.github/workflows/stockfish_sanitizers.yml: -------------------------------------------------------------------------------- 1 | name: Stockfish 2 | on: 3 | workflow_call: 4 | jobs: 5 | Stockfish: 6 | name: ${{ matrix.sanitizers.name }} 7 | runs-on: ${{ matrix.config.os }} 8 | env: 9 | COMPILER: ${{ matrix.config.compiler }} 10 | COMP: ${{ matrix.config.comp }} 11 | CXXFLAGS: "-Werror" 12 | strategy: 13 | matrix: 14 | config: 15 | - name: Ubuntu 20.04 GCC 16 | os: ubuntu-20.04 17 | compiler: g++ 18 | comp: gcc 19 | shell: bash 20 | sanitizers: 21 | - name: Run with thread sanitizer 22 | make_option: sanitize=thread 23 | instrumented_option: sanitizer-thread 24 | - name: Run with UB sanitizer 25 | make_option: sanitize=undefined 26 | instrumented_option: sanitizer-undefined 27 | - name: Run under valgrind 28 | make_option: "" 29 | instrumented_option: valgrind 30 | - name: Run under valgrind-thread 31 | make_option: "" 32 | instrumented_option: valgrind-thread 33 | defaults: 34 | run: 35 | working-directory: src 36 | shell: ${{ matrix.config.shell }} 37 | steps: 38 | - uses: actions/checkout@v3 39 | with: 40 | fetch-depth: 0 41 | 42 | - name: Download required linux packages 43 | run: | 44 | sudo apt update 45 | sudo apt install expect valgrind g++-multilib 46 | 47 | - name: Download the used network from the fishtest framework 48 | run: make net 49 | 50 | - name: Check compiler 51 | run: $COMPILER -v 52 | 53 | - name: Test help target 54 | run: make help 55 | 56 | - name: Check git 57 | run: git --version 58 | 59 | # Sanitizers 60 | 61 | - name: ${{ matrix.sanitizers.name }} 62 | run: | 63 | export CXXFLAGS="-O1 -fno-inline" 64 | make clean 65 | make -j2 ARCH=x86-64-modern ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null 66 | ../tests/instrumented.sh --${{ matrix.sanitizers.instrumented_option }} 67 | -------------------------------------------------------------------------------- /.github/workflows/stockfish_test.yml: -------------------------------------------------------------------------------- 1 | name: Stockfish 2 | on: 3 | workflow_call: 4 | jobs: 5 | Stockfish: 6 | name: ${{ matrix.config.name }} 7 | runs-on: ${{ matrix.config.os }} 8 | env: 9 | COMPILER: ${{ matrix.config.compiler }} 10 | COMP: ${{ matrix.config.comp }} 11 | CXXFLAGS: "-Werror" 12 | strategy: 13 | matrix: 14 | config: 15 | - name: Ubuntu 20.04 GCC 16 | os: ubuntu-20.04 17 | compiler: g++ 18 | comp: gcc 19 | run_32bit_tests: true 20 | run_64bit_tests: true 21 | shell: bash 22 | - name: Ubuntu 20.04 Clang 23 | os: ubuntu-20.04 24 | compiler: clang++ 25 | comp: clang 26 | run_32bit_tests: true 27 | run_64bit_tests: true 28 | shell: bash 29 | - name: Android NDK aarch64 30 | os: ubuntu-22.04 31 | compiler: aarch64-linux-android21-clang++ 32 | comp: ndk 33 | run_armv8_tests: true 34 | shell: bash 35 | - name: Android NDK arm 36 | os: ubuntu-22.04 37 | compiler: armv7a-linux-androideabi21-clang++ 38 | comp: ndk 39 | run_armv7_tests: true 40 | shell: bash 41 | - name: MacOS 12 Apple Clang 42 | os: macos-12 43 | compiler: clang++ 44 | comp: clang 45 | run_64bit_tests: true 46 | shell: bash 47 | - name: MacOS 12 GCC 11 48 | os: macos-12 49 | compiler: g++-11 50 | comp: gcc 51 | run_64bit_tests: true 52 | shell: bash 53 | - name: Windows 2022 Mingw-w64 GCC x86_64 54 | os: windows-2022 55 | compiler: g++ 56 | comp: mingw 57 | run_64bit_tests: true 58 | msys_sys: mingw64 59 | msys_env: x86_64-gcc 60 | shell: msys2 {0} 61 | - name: Windows 2022 Mingw-w64 GCC i686 62 | os: windows-2022 63 | compiler: g++ 64 | comp: mingw 65 | run_32bit_tests: true 66 | msys_sys: mingw32 67 | msys_env: i686-gcc 68 | shell: msys2 {0} 69 | - name: Windows 2022 Mingw-w64 Clang x86_64 70 | os: windows-2022 71 | compiler: clang++ 72 | comp: clang 73 | run_64bit_tests: true 74 | msys_sys: clang64 75 | msys_env: clang-x86_64-clang 76 | shell: msys2 {0} 77 | defaults: 78 | run: 79 | working-directory: src 80 | shell: ${{ matrix.config.shell }} 81 | steps: 82 | - uses: actions/checkout@v3 83 | with: 84 | fetch-depth: 0 85 | 86 | - name: Download required linux packages 87 | if: runner.os == 'Linux' 88 | run: | 89 | sudo apt update 90 | sudo apt install expect valgrind g++-multilib qemu-user 91 | 92 | - name: Install NDK 93 | if: runner.os == 'Linux' 94 | run: | 95 | if [ $COMP == ndk ]; then 96 | NDKV="21.4.7075529" 97 | ANDROID_ROOT=/usr/local/lib/android 98 | ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk 99 | SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager 100 | echo "y" | $SDKMANAGER "ndk;$NDKV" 101 | ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$NDKV 102 | ANDROID_NDK_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin 103 | echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV 104 | fi 105 | 106 | - name: Download required macOS packages 107 | if: runner.os == 'macOS' 108 | run: brew install coreutils 109 | 110 | - name: Setup msys and install required packages 111 | if: runner.os == 'Windows' 112 | uses: msys2/setup-msys2@v2 113 | with: 114 | msystem: ${{ matrix.config.msys_sys }} 115 | install: mingw-w64-${{ matrix.config.msys_env }} make git expect 116 | 117 | - name: Download the used network from the fishtest framework 118 | run: make net 119 | 120 | - name: Extract the bench number from the commit history 121 | run: | 122 | for hash in $(git rev-list -100 HEAD); do 123 | benchref=$(git show -s $hash | tac | grep -m 1 -o -x '[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*' | sed 's/[^0-9]//g') && break || true 124 | done 125 | [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $(git rev-parse HEAD~$n)" && echo "Reference bench: $benchref" || echo "No bench found" 126 | 127 | - name: Check compiler 128 | run: | 129 | if [ $COMP == ndk ]; then 130 | export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH 131 | fi 132 | $COMPILER -v 133 | 134 | - name: Test help target 135 | run: make help 136 | 137 | - name: Check git 138 | run: git --version 139 | 140 | # x86-32 tests 141 | 142 | - name: Test debug x86-32 build 143 | if: matrix.config.run_32bit_tests 144 | run: | 145 | export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" 146 | make clean 147 | make -j2 ARCH=x86-32 optimize=no debug=yes build 148 | ../tests/signature.sh $benchref 149 | 150 | - name: Test x86-32 build 151 | if: matrix.config.run_32bit_tests 152 | run: | 153 | make clean 154 | make -j2 ARCH=x86-32 build 155 | ../tests/signature.sh $benchref 156 | 157 | - name: Test x86-32-sse41-popcnt build 158 | if: matrix.config.run_32bit_tests 159 | run: | 160 | make clean 161 | make -j2 ARCH=x86-32-sse41-popcnt build 162 | ../tests/signature.sh $benchref 163 | 164 | - name: Test x86-32-sse2 build 165 | if: matrix.config.run_32bit_tests 166 | run: | 167 | make clean 168 | make -j2 ARCH=x86-32-sse2 build 169 | ../tests/signature.sh $benchref 170 | 171 | - name: Test general-32 build 172 | if: matrix.config.run_32bit_tests 173 | run: | 174 | make clean 175 | make -j2 ARCH=general-32 build 176 | ../tests/signature.sh $benchref 177 | 178 | # x86-64 tests 179 | 180 | - name: Test debug x86-64-modern build 181 | if: matrix.config.run_64bit_tests 182 | run: | 183 | export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" 184 | make clean 185 | make -j2 ARCH=x86-64-modern optimize=no debug=yes build 186 | ../tests/signature.sh $benchref 187 | 188 | - name: Test x86-64-bmi2 build 189 | if: matrix.config.run_64bit_tests && runner.os != 'macOS' 190 | run: | 191 | make clean 192 | make -j2 ARCH=x86-64-bmi2 build 193 | ../tests/signature.sh $benchref 194 | 195 | - name: Test x86-64-avx2 build 196 | if: matrix.config.run_64bit_tests && runner.os != 'macOS' 197 | run: | 198 | make clean 199 | make -j2 ARCH=x86-64-avx2 build 200 | ../tests/signature.sh $benchref 201 | 202 | - name: Test x86-64-modern build 203 | if: matrix.config.run_64bit_tests 204 | run: | 205 | make clean 206 | make -j2 ARCH=x86-64-modern build 207 | ../tests/signature.sh $benchref 208 | 209 | - name: Test x86-64-ssse3 build 210 | if: matrix.config.run_64bit_tests 211 | run: | 212 | make clean 213 | make -j2 ARCH=x86-64-ssse3 build 214 | ../tests/signature.sh $benchref 215 | 216 | - name: Test x86-64-sse3-popcnt build 217 | if: matrix.config.run_64bit_tests 218 | run: | 219 | make clean 220 | make -j2 ARCH=x86-64-sse3-popcnt build 221 | ../tests/signature.sh $benchref 222 | 223 | - name: Test x86-64 build 224 | if: matrix.config.run_64bit_tests 225 | run: | 226 | make clean 227 | make -j2 ARCH=x86-64 build 228 | ../tests/signature.sh $benchref 229 | 230 | - name: Test general-64 build 231 | if: matrix.config.run_64bit_tests 232 | run: | 233 | make clean 234 | make -j2 ARCH=general-64 build 235 | ../tests/signature.sh $benchref 236 | 237 | # armv8 tests 238 | 239 | - name: Test armv8 build 240 | if: matrix.config.run_armv8_tests 241 | run: | 242 | export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH 243 | export LDFLAGS="-static -Wno-unused-command-line-argument" 244 | make clean 245 | make -j2 ARCH=armv8 build 246 | ../tests/signature.sh $benchref 247 | 248 | # armv7 tests 249 | 250 | - name: Test armv7 build 251 | if: matrix.config.run_armv7_tests 252 | run: | 253 | export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH 254 | export LDFLAGS="-static -Wno-unused-command-line-argument" 255 | make clean 256 | make -j2 ARCH=armv7 build 257 | ../tests/signature.sh $benchref 258 | 259 | - name: Test armv7-neon build 260 | if: matrix.config.run_armv7_tests 261 | run: | 262 | export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH 263 | export LDFLAGS="-static -Wno-unused-command-line-argument" 264 | make clean 265 | make -j2 ARCH=armv7-neon build 266 | ../tests/signature.sh $benchref 267 | 268 | # Other tests 269 | 270 | - name: Check perft and search reproducibility 271 | if: matrix.config.run_64bit_tests 272 | run: | 273 | make clean 274 | make -j2 ARCH=x86-64-modern build 275 | ../tests/perft.sh 276 | ../tests/reprosearch.sh 277 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Files from build 2 | **/*.o 3 | **/*.s 4 | src/.depend 5 | 6 | # Built binary 7 | src/stockfish* 8 | src/-lstdc++.res 9 | 10 | # Neural network for the NNUE evaluation 11 | **/*.nnue 12 | 13 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "algorithm": "cpp", 4 | "bitset": "cpp", 5 | "iterator": "cpp", 6 | "xhash": "cpp", 7 | "xmemory": "cpp", 8 | "xtree": "cpp", 9 | "xutility": "cpp", 10 | "array": "cpp", 11 | "atomic": "cpp", 12 | "bit": "cpp", 13 | "cctype": "cpp", 14 | "charconv": "cpp", 15 | "chrono": "cpp", 16 | "clocale": "cpp", 17 | "cmath": "cpp", 18 | "compare": "cpp", 19 | "concepts": "cpp", 20 | "condition_variable": "cpp", 21 | "cstddef": "cpp", 22 | "cstdint": "cpp", 23 | "cstdio": "cpp", 24 | "cstdlib": "cpp", 25 | "cstring": "cpp", 26 | "ctime": "cpp", 27 | "cwchar": "cpp", 28 | "deque": "cpp", 29 | "exception": "cpp", 30 | "format": "cpp", 31 | "forward_list": "cpp", 32 | "fstream": "cpp", 33 | "initializer_list": "cpp", 34 | "iomanip": "cpp", 35 | "ios": "cpp", 36 | "iosfwd": "cpp", 37 | "iostream": "cpp", 38 | "istream": "cpp", 39 | "limits": "cpp", 40 | "list": "cpp", 41 | "locale": "cpp", 42 | "map": "cpp", 43 | "memory": "cpp", 44 | "mutex": "cpp", 45 | "new": "cpp", 46 | "optional": "cpp", 47 | "ostream": "cpp", 48 | "ratio": "cpp", 49 | "set": "cpp", 50 | "sstream": "cpp", 51 | "stdexcept": "cpp", 52 | "stop_token": "cpp", 53 | "streambuf": "cpp", 54 | "string": "cpp", 55 | "system_error": "cpp", 56 | "thread": "cpp", 57 | "tuple": "cpp", 58 | "type_traits": "cpp", 59 | "typeinfo": "cpp", 60 | "unordered_map": "cpp", 61 | "utility": "cpp", 62 | "vector": "cpp", 63 | "xfacet": "cpp", 64 | "xiosbase": "cpp", 65 | "xlocale": "cpp", 66 | "xlocbuf": "cpp", 67 | "xlocinfo": "cpp", 68 | "xlocmes": "cpp", 69 | "xlocmon": "cpp", 70 | "xlocnum": "cpp", 71 | "xloctime": "cpp", 72 | "xstring": "cpp", 73 | "xtr1common": "cpp" 74 | } 75 | } -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # List of authors for Multi-Variant Stockfish 2 | Daniel Dugovic (ddugovic) 3 | Fabian Fichter (ianfab) 4 | 5 | # Founders of the Stockfish project and fishtest infrastructure 6 | Tord Romstad (romstad) 7 | Marco Costalba (mcostalba) 8 | Joona Kiiski (zamar) 9 | Gary Linscott (glinscott) 10 | 11 | # Authors and inventors of NNUE, training, and NNUE port 12 | Yu Nasu (ynasu87) 13 | Motohiro Isozaki (yaneurao) 14 | Hisayori Noda (nodchip) 15 | 16 | # All other authors of Stockfish code (in alphabetical order) 17 | Aditya (absimaldata) 18 | Adrian Petrescu (apetresc) 19 | Ajith Chandy Jose (ajithcj) 20 | Alain Savard (Rocky640) 21 | Alayan Feh (Alayan-stk-2) 22 | Alexander Kure 23 | Alexander Pagel (Lolligerhans) 24 | Alfredo Menezes (lonfom169) 25 | Ali AlZhrani (Cooffe) 26 | Andreas Matthies (Matthies) 27 | Andrei Vetrov (proukornew) 28 | Andrew Grant (AndyGrant) 29 | Andrey Neporada (nepal) 30 | Andy Duplain 31 | Antoine Champion (antoinechampion) 32 | Aram Tumanian (atumanian) 33 | Arjun Temurnikar 34 | Artem Solopiy (EntityFX) 35 | Auguste Pop 36 | Balint Pfliegel 37 | Ben Chaney (Chaneybenjamini) 38 | Ben Koshy (BKSpurgeon) 39 | Bill Henry (VoyagerOne) 40 | Bojun Guo (noobpwnftw, Nooby) 41 | borg323 42 | Boštjan Mejak (PedanticHacker) 43 | braich 44 | Brian Sheppard (SapphireBrand, briansheppard-toast) 45 | Bruno de Melo Costa (BM123499) 46 | Bruno Pellanda (pellanda) 47 | Bryan Cross (crossbr) 48 | candirufish 49 | Chess13234 50 | Chris Cain (ceebo) 51 | clefrks 52 | Dale Weiler (graphitemaster) 53 | Daniel Axtens (daxtens) 54 | Daniel Monroe (Ergodice) 55 | Dan Schmidt (dfannius) 56 | Dariusz Orzechowski (dorzechowski) 57 | David (dav1312) 58 | David Zar 59 | Daylen Yang (daylen) 60 | Deshawn Mohan-Smith (GoldenRare) 61 | Dieter Dobbelaere (ddobbelaere) 62 | DiscanX 63 | Dominik Schlösser (domschl) 64 | double-beep 65 | Douglas Matos Gomes (dsmsgms) 66 | Dubslow 67 | Eduardo Cáceres (eduherminio) 68 | Eelco de Groot (KingDefender) 69 | Elvin Liu (solarlight2) 70 | erbsenzaehler 71 | Ernesto Gatti 72 | Fabian Beuke (madnight) 73 | Fanael Linithien (Fanael) 74 | fanon 75 | Fauzi Akram Dabat (FauziAkram) 76 | Felix Wittmann 77 | gamander 78 | Gary Heckman (gheckman) 79 | George Sobala (gsobala) 80 | gguliash 81 | Giacomo Lorenzetti (G-Lorenz) 82 | Gian-Carlo Pascutto (gcp) 83 | Goh CJ (cj5716) 84 | Gontran Lemaire (gonlem) 85 | Goodkov Vasiliy Aleksandrovich (goodkov) 86 | Gregor Cramer 87 | GuardianRM 88 | Guy Vreuls (gvreuls) 89 | Günther Demetz (pb00067, pb00068) 90 | Henri Wiechers 91 | Hiraoka Takuya (HiraokaTakuya) 92 | homoSapiensSapiens 93 | Hongzhi Cheng 94 | Ivan Ivec (IIvec) 95 | Jacques B. (Timshel) 96 | Jake Senne (w1wwwwww) 97 | Jan Ondruš (hxim) 98 | Jared Kish (Kurtbusch, kurt22i) 99 | Jarrod Torriero (DU-jdto) 100 | Jean-Francois Romang (jromang) 101 | Jean Gauthier (OuaisBla) 102 | Jekaa 103 | Jerry Donald Watson (jerrydonaldwatson) 104 | jjoshua2 105 | Jonathan Buladas Dumale (SFisGOD) 106 | Jonathan Calovski (Mysseno) 107 | Jonathan McDermid (jonathanmcdermid) 108 | Joost VandeVondele (vondele) 109 | Joseph Ellis (jhellis3) 110 | Joseph R. Prostko 111 | Jörg Oster (joergoster) 112 | Julian Willemer (NightlyKing) 113 | jundery 114 | Justin Blanchard (UncombedCoconut) 115 | Kelly Wilson 116 | Ken Takusagawa 117 | Kian E (KJE-98) 118 | kinderchocolate 119 | Kiran Panditrao (Krgp) 120 | Kojirion 121 | Krystian Kuzniarek (kuzkry) 122 | Leonardo Ljubičić (ICCF World Champion) 123 | Leonid Pechenik (lp--) 124 | Liam Keegan (lkeegan) 125 | Linmiao Xu (linrock) 126 | Linus Arver (listx) 127 | loco-loco 128 | Lub van den Berg (ElbertoOne) 129 | Luca Brivio (lucabrivio) 130 | Lucas Braesch (lucasart) 131 | Lyudmil Antonov (lantonov) 132 | Maciej Żenczykowski (zenczykowski) 133 | Malcolm Campbell (xoto10) 134 | Mark Tenzer (31m059) 135 | marotear 136 | Matt Ginsberg (mattginsberg) 137 | Matthew Lai (matthewlai) 138 | Matthew Sullivan (Matt14916) 139 | Max A. (Disservin) 140 | Maxim Masiutin (maximmasiutin) 141 | Maxim Molchanov (Maxim) 142 | Michael An (man) 143 | Michael Byrne (MichaelB7) 144 | Michael Chaly (Vizvezdenec) 145 | Michael Stembera (mstembera) 146 | Michael Whiteley (protonspring) 147 | Michel Van den Bergh (vdbergh) 148 | Miguel Lahoz (miguel-l) 149 | Mikael Bäckman (mbootsector) 150 | Mike Babigian (Farseer) 151 | Mira 152 | Miroslav Fontán (Hexik) 153 | Moez Jellouli (MJZ1977) 154 | Mohammed Li (tthsqe12) 155 | Muzhen J (XInTheDark) 156 | Nathan Rugg (nmrugg) 157 | Nguyen Pham (nguyenpham) 158 | Nicklas Persson (NicklasPersson) 159 | Nick Pelling (nickpelling) 160 | Niklas Fiekas (niklasf) 161 | Nikolay Kostov (NikolayIT) 162 | Norman Schmidt (FireFather) 163 | notruck 164 | Ofek Shochat (OfekShochat, ghostway) 165 | Ondrej Mosnáček (WOnder93) 166 | Ondřej Mišina (AndrovT) 167 | Oskar Werkelin Ahlin 168 | Pablo Vazquez 169 | Panthee 170 | Pascal Romaret 171 | Pasquale Pigazzini (ppigazzini) 172 | Patrick Jansen (mibere) 173 | Peter Schneider (pschneider1968) 174 | Peter Zsifkovits (CoffeeOne) 175 | PikaCat 176 | Praveen Kumar Tummala (praveentml) 177 | Prokop Randáček (ProkopRandacek) 178 | Rahul Dsilva (silversolver1) 179 | Ralph Stößer (Ralph Stoesser) 180 | Raminder Singh 181 | renouve 182 | Reuven Peleg (R-Peleg) 183 | Richard Lloyd (Richard-Lloyd) 184 | rn5f107s2 185 | Rodrigo Exterckötter Tjäder 186 | Rodrigo Roim (roim) 187 | Ronald de Man (syzygy1, syzygy) 188 | Ron Britvich (Britvich) 189 | rqs 190 | Rui Coelho (ruicoelhopedro) 191 | Ryan Schmitt 192 | Ryan Takker 193 | Sami Kiminki (skiminki) 194 | Sebastian Buchwald (UniQP) 195 | Sergei Antonov (saproj) 196 | Sergei Ivanov (svivanov72) 197 | Sergio Vieri (sergiovieri) 198 | sf-x 199 | Shahin M. Shahin (peregrine) 200 | Shane Booth (shane31) 201 | Shawn Varghese (xXH4CKST3RXx) 202 | Siad Daboul (Topologist) 203 | Stefan Geschwentner (locutus2) 204 | Stefano Cardanobile (Stefano80) 205 | Stefano Di Martino (StefanoD) 206 | Steinar Gunderson (sesse) 207 | Stéphane Nicolet (snicolet) 208 | Syine Mineta (MinetaS) 209 | Thanar2 210 | thaspel 211 | theo77186 212 | Tomasz Sobczyk (Sopel97) 213 | Tom Truscott 214 | Tom Vijlbrief (tomtor) 215 | Torsten Franz (torfranz, tfranzer) 216 | Torsten Hellwig (Torom) 217 | Tracey Emery (basepr1me) 218 | tttak 219 | Unai Corzo (unaiic) 220 | Uri Blass (uriblass) 221 | Vince Negri (cuddlestmonkey) 222 | Viren 223 | windfishballad 224 | xefoci7612 225 | zz4032 226 | 227 | # Additionally, we acknowledge the authors and maintainers of fishtest, 228 | # an amazing and essential framework for Stockfish development! 229 | # 230 | # https://github.com/glinscott/fishtest/blob/master/AUTHORS 231 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | # This CITATION.cff file was generated with cffinit. 2 | # Visit https://bit.ly/cffinit to generate yours today! 3 | 4 | cff-version: 1.2.0 5 | title: Stockfish 6 | message: >- 7 | Please cite this software using the metadata from this 8 | file. 9 | type: software 10 | authors: 11 | - name: The Stockfish developers (see AUTHORS file) 12 | repository-code: 'https://github.com/official-stockfish/Stockfish' 13 | url: 'https://stockfishchess.org/' 14 | repository-artifact: 'https://stockfishchess.org/download/' 15 | abstract: Stockfish is a free and strong UCI chess engine. 16 | keywords: 17 | - chess 18 | - artificial intelligence (AI) 19 | - tree search 20 | - alpha-beta search 21 | - neural networks (NN) 22 | - efficiently updatable neural networks (NNUE) 23 | license: GPL-3.0 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | [![Stockfish][stockfish128-logo]][website-link] 4 | 5 |

Stockfish

6 | 7 | A free and strong UCI chess engine. 8 |
9 | [Explore Stockfish docs »][wiki-link] 10 |
11 |
12 | [Report bug][issue-link] 13 | · 14 | [Open a discussion][discussions-link] 15 | · 16 | [Discord][discord-link] 17 | · 18 | [Blog][website-blog-link] 19 | 20 | [![Build (Multi-Variant)][build-badge]][build-link] 21 | [![License][license-badge]][license-link] 22 |
23 | [![Release][release-badge]][release-link] 24 | [![Commits (Multi-Variant)][commits-badge]][commits-link] 25 |
26 | [![Website][website-badge]][website-link] 27 | [![Fishtest][fishtest-badge]][fishtest-link] 28 | [![Discord (Multi-Variant)][variant-discord-badge]][variant-discord-link] 29 | 30 |
31 | 32 | ## Overview 33 | 34 | Multi-Variant Stockfish is a [Stockfish][github-link] fork specialized to play chess and [Lichess.org variants](https://lichess.org/variant): 35 | - [Crazyhouse](https://lichess.org/variant/crazyhouse) 36 | - [Atomic](https://lichess.org/variant/atomic) 37 | - [Horde](https://lichess.org/variant/horde) 38 | - [King of the Hill](https://lichess.org/variant/kingOfTheHill) 39 | - [Racing Kings](https://lichess.org/variant/racingKings) 40 | - [Antichess](https://lichess.org/variant/antichess) 41 | - [Three-Check](https://lichess.org/variant/threeCheck) 42 | - [Chess960](https://lichess.org/variant/chess960) (played natively by official Stockfish) 43 | - Losers 44 | - Giveaway 45 | - Suicide 46 | - Loop 47 | - Extinction 48 | - Grid 49 | - Two Kings 50 | 51 | Please consider sponsoring [Fairy-Stockfish][fairy-website-link] which has a more robust architecture and supports a wider array of variants. 52 | 53 | ### Stockfish 54 | 55 | [Stockfish][website-link] is a **free and strong UCI chess engine** derived from 56 | Glaurung 2.1 that analyzes chess positions and computes the optimal moves. 57 | 58 | Stockfish **does not include a graphical user interface** (GUI) that is required 59 | to display a chessboard and to make it easy to input moves. These GUIs are 60 | developed independently from Stockfish and are available online. **Read the 61 | documentation for your GUI** of choice for information about how to use 62 | Stockfish with it. 63 | 64 | See also the Stockfish [documentation][wiki-usage-link] for further usage help. 65 | 66 | ## Files (Multi-Variant) 67 | 68 | This distribution of Multi-Variant Stockfish consists of the following files: 69 | 70 | * [README.md][readme-link], the file you are currently reading. 71 | 72 | * [Copying.txt][license-link], a text file containing the GNU General Public 73 | License version 3. 74 | 75 | * [AUTHORS][authors-link], a text file with the list of authors for the project. 76 | 77 | * [src][src-link], a subdirectory containing the full source code, including a 78 | Makefile that can be used to compile Stockfish on Unix-like systems. 79 | 80 | * a file with the .nnue extension, storing the neural network for the NNUE 81 | evaluation. Binary distributions will have this file embedded. 82 | 83 | ## The UCI protocol 84 | 85 | The [Universal Chess Interface][uci-link] (UCI) is a standard text-based protocol 86 | used to communicate with a chess engine and is the recommended way to do so for 87 | typical graphical user interfaces (GUI) or chess tools. Stockfish implements the 88 | majority of its options. 89 | 90 | Developers can see the default values for the UCI options available in Stockfish 91 | by typing `./stockfish uci` in a terminal, but most users should typically use a 92 | chess GUI to interact with Stockfish. 93 | 94 | For more information on UCI or debug commands, see our [documentation][wiki-commands-link]. 95 | 96 | ## Compiling Stockfish 97 | 98 | Stockfish has support for 32 or 64-bit CPUs, certain hardware instructions, 99 | big-endian machines such as Power PC, and other platforms. 100 | 101 | On Unix-like systems, it should be easy to compile Stockfish directly from the 102 | source code with the included Makefile in the folder `src`. In general, it is 103 | recommended to run `make help` to see a list of make targets with corresponding 104 | descriptions. 105 | 106 | ``` 107 | cd src 108 | make -j build ARCH=x86-64-modern 109 | ``` 110 | 111 | Detailed compilation instructions for all platforms can be found in our 112 | [documentation][wiki-compile-link]. 113 | 114 | ## Contributing 115 | 116 | ### Donating hardware 117 | 118 | Improving Stockfish requires a massive amount of testing. You can donate your 119 | hardware resources by installing the [Fishtest Worker][worker-link] and viewing 120 | the current tests on [Fishtest][fishtest-link]. 121 | 122 | ### Improving the code 123 | 124 | In the [chessprogramming wiki][programming-link], many techniques used in 125 | Stockfish are explained with a lot of background information. 126 | The [section on Stockfish][programmingsf-link] describes many features 127 | and techniques used by Stockfish. However, it is generic rather than 128 | focused on Stockfish's precise implementation. 129 | 130 | The engine testing is done on [Fishtest][fishtest-link]. 131 | If you want to help improve Stockfish, please read this [guideline][guideline-link] 132 | first, where the basics of Stockfish development are explained. 133 | 134 | Discussions about Stockfish take place these days mainly in the Stockfish 135 | [Discord server][discord-link]. This is also the best place to ask questions 136 | about the codebase and how to improve it. 137 | 138 | ## Terms of use 139 | 140 | Stockfish is free and distributed under the 141 | [**GNU General Public License version 3**][license-link] (GPL v3). Essentially, 142 | this means you are free to do almost exactly what you want with the program, 143 | including distributing it among your friends, making it available for download 144 | from your website, selling it (either by itself or as part of some bigger 145 | software package), or using it as the starting point for a software project of 146 | your own. 147 | 148 | The only real limitation is that whenever you distribute Stockfish in some way, 149 | you MUST always include the license and the full source code (or a pointer to 150 | where the source code can be found) to generate the exact binary you are 151 | distributing. If you make any changes to the source code, these changes must 152 | also be made available under GPL v3. 153 | 154 | 155 | [authors-link]: https://github.com/ddugovic/Stockfish/blob/master/AUTHORS 156 | [build-link]: https://github.com/ddugovic/Stockfish/actions/workflows/stockfish.yml 157 | [commits-link]: https://github.com/ddugovic/Stockfish/commits/master 158 | [discord-link]: https://discord.gg/GWDRS3kU6R 159 | [variant-discord-link]:https://discord.com/invite/9EpCZJc 160 | [issue-link]: https://github.com/official-stockfish/Stockfish/issues/new?assignees=&labels=&template=BUG-REPORT.yml 161 | [discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new 162 | [fishtest-link]: https://tests.stockfishchess.org/tests 163 | [guideline-link]: https://github.com/glinscott/fishtest/wiki/Creating-my-first-test 164 | [license-link]: https://github.com/ddugovic/Stockfish/blob/master/Copying.txt 165 | [programming-link]: https://www.chessprogramming.org/Main_Page 166 | [programmingsf-link]: https://www.chessprogramming.org/Stockfish 167 | [readme-link]: https://github.com/ddugovic/Stockfish/blob/master/README.md 168 | [release-link]: https://github.com/official-stockfish/Stockfish/releases/latest 169 | [src-link]: https://github.com/ddugovic/Stockfish/tree/master/src 170 | [stockfish128-logo]: https://stockfishchess.org/images/logo/icon_128x128.png 171 | [uci-link]: https://backscattering.de/chess/uci/ 172 | [website-link]: https://stockfishchess.org 173 | [fairy-website-link]: https://fairy-stockfish.github.io 174 | [website-blog-link]: https://stockfishchess.org/blog/ 175 | [wiki-link]: https://github.com/official-stockfish/Stockfish/wiki 176 | [wiki-usage-link]: https://github.com/official-stockfish/Stockfish/wiki/Download-and-usage 177 | [wiki-compile-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source 178 | [wiki-commands-link]: https://github.com/official-stockfish/Stockfish/wiki/Commands 179 | [worker-link]: https://github.com/glinscott/fishtest/wiki/Running-the-worker 180 | 181 | [build-badge]: https://img.shields.io/github/actions/workflow/status/ddugovic/Stockfish/stockfish.yml?branch=master&style=for-the-badge&label=stockfish&logo=github 182 | [commits-badge]: https://img.shields.io/github/commits-since/ddugovic/Stockfish/latest?style=for-the-badge 183 | [discord-badge]: https://img.shields.io/discord/435943710472011776?style=for-the-badge&label=discord&logo=Discord 184 | [variant-discord-badge]:https://img.shields.io/discord/161062341427986433?style=for-the-badge&label=discord&logo=Discord 185 | [fishtest-badge]: https://img.shields.io/website?style=for-the-badge&down_color=red&down_message=Offline&label=Fishtest&up_color=success&up_message=Online&url=https%3A%2F%2Ftests.stockfishchess.org%2Ftests%2Ffinished 186 | [license-badge]: https://img.shields.io/github/license/ddugovic/Stockfish?style=for-the-badge&label=license&color=success 187 | [release-badge]: https://img.shields.io/github/v/release/official-stockfish/Stockfish?style=for-the-badge&label=official%20release 188 | [website-badge]: https://img.shields.io/website?style=for-the-badge&down_color=red&down_message=Offline&label=website&up_color=success&up_message=Online&url=https%3A%2F%2Fstockfishchess.org 189 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | clone_depth: 50 3 | 4 | branches: 5 | only: 6 | - master 7 | 8 | # Operating system (build VM template) 9 | os: Visual Studio 2019 10 | 11 | # Build platform, i.e. x86, x64, AnyCPU. This setting is optional. 12 | platform: 13 | - x86 14 | - x64 15 | 16 | # build Configuration, i.e. Debug, Release, etc. 17 | configuration: 18 | - Debug 19 | - Release 20 | 21 | matrix: 22 | # The build fail immediately once one of the job fails 23 | fast_finish: true 24 | 25 | artifacts: 26 | - path: 'src\$(configuration)\*.exe' 27 | name: Stockfish 28 | 29 | # Scripts that are called at very beginning, before repo cloning 30 | init: 31 | - cmake --version 32 | - msbuild /version 33 | 34 | before_build: 35 | - ps: | 36 | # Get sources 37 | $src = get-childitem -Path *.cpp -Recurse | select -ExpandProperty FullName 38 | $src = $src -join ' ' 39 | $src = $src.Replace("\", "/") 40 | 41 | # Build CMakeLists.txt 42 | $t = 'cmake_minimum_required(VERSION 3.17)', 43 | 'project(Stockfish)', 44 | 'set(CMAKE_CXX_STANDARD 17)', 45 | 'set(CMAKE_CXX_STANDARD_REQUIRED ON)', 46 | 'set(CMAKE_CXX_EXTENSIONS OFF)', 47 | 'set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src)', 48 | 'set(source_files', $src, ')', 49 | 'add_executable(stockfish ${source_files})', 50 | 'target_compile_definitions(stockfish PRIVATE USE_NNUE ANTI ATOMIC CRAZYHOUSE HORDE KOTH RACE THREECHECK)' 51 | 52 | # Write CMakeLists.txt without BOM 53 | $MyPath = (Get-Item -Path "." -Verbose).FullName + '\CMakeLists.txt' 54 | $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False 55 | [System.IO.File]::WriteAllLines($MyPath, $t, $Utf8NoBomEncoding) 56 | 57 | # Obtain bench reference from git log 58 | $b = git log HEAD | sls "\b[Bb]ench[ :]+[0-9]{7}" | select -first 1 59 | $bench = $b -match '\D+(\d+)' | % { $matches[1] } 60 | Write-Host "Reference bench:" $bench 61 | $g = "Visual Studio 16 2019" 62 | If (${env:PLATFORM} -eq 'x64') { $a = "x64" } 63 | If (${env:PLATFORM} -eq 'x86') { $a = "Win32" } 64 | cmake -G "${g}" -A ${a} . 65 | Write-Host "Generated files for: " $g $a 66 | 67 | build_script: 68 | - cmake --build . --config %CONFIGURATION% -- /verbosity:minimal 69 | - ps: | 70 | # Download default NNUE net from fishtest 71 | $nnuenet = Get-Content -Path src\evaluate.h | Select-String -CaseSensitive -Pattern "EvalFileDefaultName" | Select-String -CaseSensitive -Pattern "nn-[a-z0-9]{12}.nnue" 72 | $dummy = $nnuenet -match "(?nn-[a-z0-9]{12}.nnue)" 73 | $nnuenet = $Matches.nnuenet 74 | Write-Host "Default net:" $nnuenet 75 | $nnuedownloadurl = "https://tests.stockfishchess.org/api/nn/$nnuenet" 76 | $nnuefilepath = "src\${env:CONFIGURATION}\$nnuenet" 77 | if (Test-Path -Path $nnuefilepath) { 78 | Write-Host "Already available." 79 | } else { 80 | Write-Host "Downloading $nnuedownloadurl to $nnuefilepath" 81 | Invoke-WebRequest -Uri $nnuedownloadurl -OutFile $nnuefilepath 82 | } 83 | 84 | before_test: 85 | - cd src/%CONFIGURATION% 86 | - stockfish bench 2> out.txt >NUL 87 | - ps: | 88 | # Verify bench number 89 | $s = (gc "./out.txt" | out-string) 90 | $r = ($s -match 'Nodes searched \D+(\d+)' | % { $matches[1] }) 91 | Write-Host "Engine bench:" $r 92 | Write-Host "Reference bench:" $bench 93 | If ($r -ne $bench) { exit 1 } 94 | -------------------------------------------------------------------------------- /deploy/appveyor.yml: -------------------------------------------------------------------------------- 1 | os: Visual Studio 2015 2 | 3 | clone_folder: C:\projects\Stockfish 4 | shallow_clone: true 5 | clone_depth: 1 6 | 7 | platform: x64 8 | 9 | cache: 10 | - x86_64-4.9.2-release-posix-seh-rt_v4-rev2.7z 11 | 12 | install: 13 | - if not exist x86_64-4.9.2-release-posix-seh-rt_v4-rev2.7z appveyor DownloadFile "https://sourceforge.net/projects/mingw-w64/files/Toolchains targetting Win64/Personal Builds/mingw-builds/4.9.2/threads-posix/seh/x86_64-4.9.2-release-posix-seh-rt_v4-rev2.7z" -FileName x86_64-4.9.2-release-posix-seh-rt_v4-rev2.7z 14 | - 7z x -oC:\MinGW\msys\1.0\ x86_64-4.9.2-release-posix-seh-rt_v4-rev2.7z > nul 15 | 16 | build_script: 17 | - C:\MinGW\msys\1.0\bin\sh.exe -c "cd /c/projects/Stockfish/; /c/projects/Stockfish/deploy/msys.sh" 18 | 19 | artifacts: 20 | - path: '*\stockfish-windows-*' 21 | 22 | deploy: 23 | - provider: S3 24 | access_key_id: 25 | secure: 1BWB8sb0ecF9242HMlKH54uFCn9GtjhxeaysqbVVDG4= 26 | secret_access_key: 27 | secure: l+Z1nugPzeV5fviFeckmfjjpvz7vfoALSMDt40Se77enlblgsr0/+/MrHQ+PMx/a 28 | bucket: variant-stockfish 29 | region: us-west-2 30 | set_public: true 31 | folder: ddugovic 32 | artifact: /stockfish-windows-.*/ 33 | - provider: GitHub 34 | release: $(APPVEYOR_REPO_TAG_NAME) 35 | description: 'Multi-variant Stockfish binaries' 36 | auth_token: 37 | secure: HM1uS+Vhume4hO4gY5Ev8XU8rurfeviKBNbfu3xOrl3zWnKcsdAzkNA2JExElSAM 38 | artifact: /stockfish-windows-.*/ 39 | draft: false 40 | prerelease: false 41 | on: 42 | appveyor_repo_tag: true 43 | -------------------------------------------------------------------------------- /deploy/linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | cd src 4 | 5 | echo 6 | echo "### Linker info" 7 | echo 8 | ldd --version || echo "no ldd" 9 | 10 | echo 11 | echo "### CPU capabilities" 12 | echo 13 | grep "^flags" /proc/cpuinfo || echo 14 | 15 | echo 16 | echo "### Running profile-build for x86-64 ..." 17 | echo 18 | make clean 19 | make profile-build ARCH=x86-64 EXE=stockfish-x86_64 20 | 21 | if [ -f /proc/cpuinfo ]; then 22 | make clean 23 | if grep "^flags" /proc/cpuinfo | grep -q popcnt ; then 24 | echo 25 | echo "### Running profile-build for x86-64-modern ..." 26 | echo 27 | make profile-build ARCH=x86-64-modern EXE=stockfish-x86_64-modern 28 | else 29 | echo 30 | echo "### Running build for x86-64-modern ..." 31 | echo 32 | make build ARCH=x86-64-modern EXE=stockfish-x86_64-modern 33 | fi 34 | 35 | make clean 36 | if grep "^flags" /proc/cpuinfo | grep popcnt | grep -q bmi2 ; then 37 | echo 38 | echo "### Running profile-build for x86-64-bmi2 ..." 39 | echo 40 | make profile-build ARCH=x86-64-bmi2 EXE=stockfish-x86_64-bmi2 41 | else 42 | echo 43 | echo "### Running build for x86-64-bmi2 ..." 44 | echo 45 | make build ARCH=x86-64-bmi2 EXE=stockfish-x86_64-bmi2 46 | fi 47 | fi 48 | -------------------------------------------------------------------------------- /deploy/msys.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export PATH=.:/mingw64/bin:/usr/local/bin:/mingw/bin:/bin 4 | export 5 | mkdir "$APPVEYOR_REPO_BRANCH" 6 | 7 | cd src 8 | 9 | make clean 10 | make profile-build COMP=mingw ARCH=x86-64 11 | strip stockfish.exe 12 | mv stockfish.exe "../$APPVEYOR_REPO_BRANCH/stockfish-windows-x86_64.exe" 13 | 14 | make clean 15 | make profile-build COMP=mingw ARCH=x86-64-modern 16 | strip stockfish.exe 17 | mv stockfish.exe "../$APPVEYOR_REPO_BRANCH/stockfish-windows-x86_64-modern.exe" 18 | 19 | make clean 20 | make profile-build COMP=mingw ARCH=x86-64-bmi2 21 | strip stockfish.exe 22 | mv stockfish.exe "../$APPVEYOR_REPO_BRANCH/stockfish-windows-x86_64-bmi2.exe" 23 | 24 | cd .. 25 | -------------------------------------------------------------------------------- /deploy/old-gcc.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/thread.h b/src/thread.h 2 | index 3aa5bb51..ac72d15d 100644 3 | --- a/src/thread.h 4 | +++ b/src/thread.h 5 | @@ -84,7 +84,7 @@ public: 6 | 7 | struct MainThread : public Thread { 8 | 9 | - using Thread::Thread; 10 | + explicit MainThread(size_t n) : Thread(n) { } 11 | 12 | void search() override; 13 | void check_time(); 14 | -------------------------------------------------------------------------------- /deploy/stable-slim.dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:stable-slim 2 | RUN apt-get update && apt-get install -y make g++ 3 | VOLUME /home/builder/Stockfish 4 | WORKDIR /home/builder/Stockfish 5 | RUN groupadd -r builder && useradd -r -g builder builder 6 | USER builder 7 | CMD ./deploy/linux.sh 8 | -------------------------------------------------------------------------------- /src/benchmark.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef BENCHMARK_H_INCLUDED 20 | #define BENCHMARK_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | namespace Stockfish { 27 | 28 | class Position; 29 | 30 | std::vector setup_bench(const Position&, std::istream&); 31 | 32 | } // namespace Stockfish 33 | 34 | #endif // #ifndef BENCHMARK_H_INCLUDED 35 | -------------------------------------------------------------------------------- /src/bitbase.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "bitboard.h" 24 | #include "types.h" 25 | 26 | namespace Stockfish { 27 | 28 | namespace { 29 | 30 | // There are 24 possible pawn squares: files A to D and ranks from 2 to 7. 31 | // Positions with the pawn on files E to H will be mirrored before probing. 32 | constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608 33 | 34 | std::bitset KPKBitbase; 35 | 36 | // A KPK bitbase index is an integer in [0, IndexMax] range 37 | // 38 | // Information is mapped in a way that minimizes the number of iterations: 39 | // 40 | // bit 0- 5: white king square (from SQ_A1 to SQ_H8) 41 | // bit 6-11: black king square (from SQ_A1 to SQ_H8) 42 | // bit 12: side to move (WHITE or BLACK) 43 | // bit 13-14: white pawn file (from FILE_A to FILE_D) 44 | // bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2) 45 | unsigned index(Color stm, Square bksq, Square wksq, Square psq) { 46 | return int(wksq) | (bksq << 6) | (stm << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15); 47 | } 48 | 49 | enum Result { 50 | INVALID = 0, 51 | UNKNOWN = 1, 52 | DRAW = 2, 53 | WIN = 4 54 | }; 55 | 56 | Result& operator|=(Result& r, Result v) { return r = Result(r | v); } 57 | 58 | struct KPKPosition { 59 | KPKPosition() = default; 60 | explicit KPKPosition(unsigned idx); 61 | operator Result() const { return result; } 62 | Result classify(const std::vector& db); 63 | 64 | Color stm; 65 | Square ksq[COLOR_NB], psq; 66 | Result result; 67 | }; 68 | 69 | } // namespace 70 | 71 | bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) { 72 | 73 | assert(file_of(wpsq) <= FILE_D); 74 | 75 | return KPKBitbase[index(stm, bksq, wksq, wpsq)]; 76 | } 77 | 78 | 79 | void Bitbases::init() { 80 | 81 | std::vector db(MAX_INDEX); 82 | unsigned idx, repeat = 1; 83 | 84 | // Initialize db with known win / draw positions 85 | for (idx = 0; idx < MAX_INDEX; ++idx) 86 | db[idx] = KPKPosition(idx); 87 | 88 | // Iterate through the positions until none of the unknown positions can be 89 | // changed to either wins or draws (15 cycles needed). 90 | while (repeat) 91 | for (repeat = idx = 0; idx < MAX_INDEX; ++idx) 92 | repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN); 93 | 94 | // Fill the bitbase with the decisive results 95 | for (idx = 0; idx < MAX_INDEX; ++idx) 96 | if (db[idx] == WIN) 97 | KPKBitbase.set(idx); 98 | } 99 | 100 | namespace { 101 | 102 | KPKPosition::KPKPosition(unsigned idx) { 103 | 104 | ksq[WHITE] = Square((idx >> 0) & 0x3F); 105 | ksq[BLACK] = Square((idx >> 6) & 0x3F); 106 | stm = Color ((idx >> 12) & 0x01); 107 | psq = make_square(File((idx >> 13) & 0x3), Rank(RANK_7 - ((idx >> 15) & 0x7))); 108 | 109 | // Invalid if two pieces are on the same square or if a king can be captured 110 | if ( distance(ksq[WHITE], ksq[BLACK]) <= 1 111 | || ksq[WHITE] == psq 112 | || ksq[BLACK] == psq 113 | || (stm == WHITE && (pawn_attacks_bb(WHITE, psq) & ksq[BLACK]))) 114 | result = INVALID; 115 | 116 | // Win if the pawn can be promoted without getting captured 117 | else if ( stm == WHITE 118 | && rank_of(psq) == RANK_7 119 | && ksq[WHITE] != psq + NORTH 120 | && ( distance(ksq[BLACK], psq + NORTH) > 1 121 | || (distance(ksq[WHITE], psq + NORTH) == 1))) 122 | result = WIN; 123 | 124 | // Draw if it is stalemate or the black king can capture the pawn 125 | else if ( stm == BLACK 126 | && ( !(attacks_bb(ksq[BLACK]) & ~(attacks_bb(ksq[WHITE]) | pawn_attacks_bb(WHITE, psq))) 127 | || (attacks_bb(ksq[BLACK]) & ~attacks_bb(ksq[WHITE]) & psq))) 128 | result = DRAW; 129 | 130 | // Position will be classified later 131 | else 132 | result = UNKNOWN; 133 | } 134 | 135 | Result KPKPosition::classify(const std::vector& db) { 136 | 137 | // White to move: If one move leads to a position classified as WIN, the result 138 | // of the current position is WIN. If all moves lead to positions classified 139 | // as DRAW, the current position is classified as DRAW, otherwise the current 140 | // position is classified as UNKNOWN. 141 | // 142 | // Black to move: If one move leads to a position classified as DRAW, the result 143 | // of the current position is DRAW. If all moves lead to positions classified 144 | // as WIN, the position is classified as WIN, otherwise the current position is 145 | // classified as UNKNOWN. 146 | const Result Good = (stm == WHITE ? WIN : DRAW); 147 | const Result Bad = (stm == WHITE ? DRAW : WIN); 148 | 149 | Result r = INVALID; 150 | Bitboard b = attacks_bb(ksq[stm]); 151 | 152 | while (b) 153 | r |= stm == WHITE ? db[index(BLACK, ksq[BLACK], pop_lsb(b), psq)] 154 | : db[index(WHITE, pop_lsb(b), ksq[WHITE], psq)]; 155 | 156 | if (stm == WHITE) 157 | { 158 | if (rank_of(psq) < RANK_7) // Single push 159 | r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH)]; 160 | 161 | if ( rank_of(psq) == RANK_2 // Double push 162 | && psq + NORTH != ksq[WHITE] 163 | && psq + NORTH != ksq[BLACK]) 164 | r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH + NORTH)]; 165 | } 166 | 167 | return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad; 168 | } 169 | 170 | } // namespace 171 | 172 | } // namespace Stockfish 173 | -------------------------------------------------------------------------------- /src/bitboard.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include "bitboard.h" 23 | #include "misc.h" 24 | 25 | namespace Stockfish { 26 | 27 | uint8_t PopCnt16[1 << 16]; 28 | uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; 29 | 30 | Bitboard LineBB[SQUARE_NB][SQUARE_NB]; 31 | Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; 32 | Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; 33 | Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; 34 | #ifdef GRID 35 | Bitboard GridBB[GRIDLAYOUT_NB][SQUARE_NB]; 36 | #endif 37 | 38 | Magic RookMagics[SQUARE_NB]; 39 | Magic BishopMagics[SQUARE_NB]; 40 | 41 | namespace { 42 | 43 | Bitboard RookTable[0x19000]; // To store rook attacks 44 | Bitboard BishopTable[0x1480]; // To store bishop attacks 45 | 46 | void init_magics(PieceType pt, Bitboard table[], Magic magics[]); 47 | 48 | } 49 | 50 | /// safe_destination() returns the bitboard of target square for the given step 51 | /// from the given square. If the step is off the board, returns empty bitboard. 52 | 53 | inline Bitboard safe_destination(Square s, int step) { 54 | Square to = Square(s + step); 55 | return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); 56 | } 57 | 58 | 59 | /// Bitboards::pretty() returns an ASCII representation of a bitboard suitable 60 | /// to be printed to standard output. Useful for debugging. 61 | 62 | std::string Bitboards::pretty(Bitboard b) { 63 | 64 | std::string s = "+---+---+---+---+---+---+---+---+\n"; 65 | 66 | for (Rank r = RANK_8; r >= RANK_1; --r) 67 | { 68 | for (File f = FILE_A; f <= FILE_H; ++f) 69 | s += b & make_square(f, r) ? "| X " : "| "; 70 | 71 | s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+\n"; 72 | } 73 | s += " a b c d e f g h\n"; 74 | 75 | return s; 76 | } 77 | 78 | 79 | /// Bitboards::init() initializes various bitboard tables. It is called at 80 | /// startup and relies on global objects to be already zero-initialized. 81 | 82 | void Bitboards::init() { 83 | 84 | for (unsigned i = 0; i < (1 << 16); ++i) 85 | PopCnt16[i] = uint8_t(std::bitset<16>(i).count()); 86 | 87 | for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) 88 | for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) 89 | SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); 90 | 91 | init_magics(ROOK, RookTable, RookMagics); 92 | init_magics(BISHOP, BishopTable, BishopMagics); 93 | 94 | for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) 95 | { 96 | PawnAttacks[WHITE][s1] = pawn_attacks_bb(square_bb(s1)); 97 | PawnAttacks[BLACK][s1] = pawn_attacks_bb(square_bb(s1)); 98 | 99 | for (int step : {-9, -8, -7, -1, 1, 7, 8, 9} ) 100 | PseudoAttacks[KING][s1] |= safe_destination(s1, step); 101 | 102 | for (int step : {-17, -15, -10, -6, 6, 10, 15, 17} ) 103 | PseudoAttacks[KNIGHT][s1] |= safe_destination(s1, step); 104 | 105 | PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb(s1, 0); 106 | PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0); 107 | 108 | for (PieceType pt : { BISHOP, ROOK }) 109 | for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) 110 | { 111 | if (PseudoAttacks[pt][s1] & s2) 112 | { 113 | LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2; 114 | BetweenBB[s1][s2] = (attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1))); 115 | } 116 | BetweenBB[s1][s2] |= s2; 117 | } 118 | } 119 | 120 | #ifdef GRID 121 | for (Square s = SQ_A1; s <= SQ_H8; ++s) 122 | { 123 | GridBB[NORMAL_GRID][s] = square_bb(s) | square_bb(Square(int(s) ^ 8)) | square_bb(Square(int(s) ^ 1)) | square_bb(Square(int(s) ^ 9)); 124 | #ifdef DISPLACEDGRID 125 | GridBB[DISPLACED_GRID][s] = square_bb(s); 126 | if (!((FileABB | FileHBB) & s)) 127 | GridBB[DISPLACED_GRID][s] |= square_bb(Square(((s + 1) ^ 1) - 1)); 128 | if (!((Rank1BB | Rank8BB) & s)) 129 | GridBB[DISPLACED_GRID][s] |= square_bb(Square(((s + 8) ^ 8) - 8)); 130 | if (!((Rank1BB | Rank8BB | FileABB | FileHBB) & s)) 131 | GridBB[DISPLACED_GRID][s] |= square_bb(Square(((s + 9) ^ 9) - 9)); 132 | #endif 133 | #ifdef SLIPPEDGRID 134 | GridBB[SLIPPED_GRID][s] = square_bb(s) | square_bb(Square(int(s) ^ 8)); 135 | if (!((FileABB | FileHBB) & s)) 136 | { 137 | GridBB[SLIPPED_GRID][s] |= square_bb(Square(((s + 1) ^ 1) - 1)); 138 | GridBB[SLIPPED_GRID][s] |= square_bb(Square((((s + 1) ^ 1) - 1) ^ 8)); 139 | } 140 | #endif 141 | } 142 | #endif 143 | } 144 | 145 | namespace { 146 | 147 | Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) { 148 | 149 | Bitboard attacks = 0; 150 | Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST}; 151 | Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST}; 152 | 153 | for (Direction d : (pt == ROOK ? RookDirections : BishopDirections)) 154 | { 155 | Square s = sq; 156 | while (safe_destination(s, d) && !(occupied & s)) 157 | attacks |= (s += d); 158 | } 159 | 160 | return attacks; 161 | } 162 | 163 | 164 | // init_magics() computes all rook and bishop attacks at startup. Magic 165 | // bitboards are used to look up attacks of sliding pieces. As a reference see 166 | // www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so 167 | // called "fancy" approach. 168 | 169 | void init_magics(PieceType pt, Bitboard table[], Magic magics[]) { 170 | 171 | // Optimal PRNG seeds to pick the correct magics in the shortest time 172 | int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 }, 173 | { 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } }; 174 | 175 | Bitboard occupancy[4096], reference[4096], edges, b; 176 | int epoch[4096] = {}, cnt = 0, size = 0; 177 | 178 | for (Square s = SQ_A1; s <= SQ_H8; ++s) 179 | { 180 | // Board edges are not considered in the relevant occupancies 181 | edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s)); 182 | 183 | // Given a square 's', the mask is the bitboard of sliding attacks from 184 | // 's' computed on an empty board. The index must be big enough to contain 185 | // all the attacks for each possible subset of the mask and so is 2 power 186 | // the number of 1s of the mask. Hence we deduce the size of the shift to 187 | // apply to the 64 or 32 bits word to get the index. 188 | Magic& m = magics[s]; 189 | m.mask = sliding_attack(pt, s, 0) & ~edges; 190 | m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask); 191 | 192 | // Set the offset for the attacks table of the square. We have individual 193 | // table sizes for each square with "Fancy Magic Bitboards". 194 | m.attacks = s == SQ_A1 ? table : magics[s - 1].attacks + size; 195 | 196 | // Use Carry-Rippler trick to enumerate all subsets of masks[s] and 197 | // store the corresponding sliding attack bitboard in reference[]. 198 | b = size = 0; 199 | do { 200 | occupancy[size] = b; 201 | reference[size] = sliding_attack(pt, s, b); 202 | 203 | if (HasPext) 204 | m.attacks[pext(b, m.mask)] = reference[size]; 205 | 206 | size++; 207 | b = (b - m.mask) & m.mask; 208 | } while (b); 209 | 210 | if (HasPext) 211 | continue; 212 | 213 | PRNG rng(seeds[Is64Bit][rank_of(s)]); 214 | 215 | // Find a magic for square 's' picking up an (almost) random number 216 | // until we find the one that passes the verification test. 217 | for (int i = 0; i < size; ) 218 | { 219 | for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6; ) 220 | m.magic = rng.sparse_rand(); 221 | 222 | // A good magic must map every possible occupancy to an index that 223 | // looks up the correct sliding attack in the attacks[s] database. 224 | // Note that we build up the database for square 's' as a side 225 | // effect of verifying the magic. Keep track of the attempt count 226 | // and save it in epoch[], little speed-up trick to avoid resetting 227 | // m.attacks[] after every failed attempt. 228 | for (++cnt, i = 0; i < size; ++i) 229 | { 230 | unsigned idx = m.index(occupancy[i]); 231 | 232 | if (epoch[idx] < cnt) 233 | { 234 | epoch[idx] = cnt; 235 | m.attacks[idx] = reference[i]; 236 | } 237 | else if (m.attacks[idx] != reference[i]) 238 | break; 239 | } 240 | } 241 | } 242 | } 243 | } 244 | 245 | } // namespace Stockfish 246 | -------------------------------------------------------------------------------- /src/endgame.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef ENDGAME_H_INCLUDED 20 | #define ENDGAME_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "position.h" 29 | #include "types.h" 30 | 31 | namespace Stockfish { 32 | 33 | /// EndgameCode lists all supported endgame functions by corresponding codes 34 | 35 | enum EndgameCode { 36 | 37 | EVALUATION_FUNCTIONS, 38 | #ifdef ANTI 39 | RK, 40 | KN, 41 | NN, 42 | #endif 43 | #ifdef ATOMIC 44 | KQK, 45 | KRK, 46 | KBK, 47 | KNK, 48 | #endif 49 | KNNK, // KNN vs K 50 | KNNKP, // KNN vs KP 51 | KXK, // Generic "mate lone king" eval 52 | KBNK, // KBN vs K 53 | KPK, // KP vs K 54 | KRKP, // KR vs KP 55 | KRKB, // KR vs KB 56 | KRKN, // KR vs KN 57 | KQKP, // KQ vs KP 58 | KQKR, // KQ vs KR 59 | 60 | SCALING_FUNCTIONS, 61 | KBPsK, // KB and pawns vs K 62 | KQKRPs, // KQ vs KR and pawns 63 | KRPKR, // KRP vs KR 64 | KRPKB, // KRP vs KB 65 | KRPPKRP, // KRPP vs KRP 66 | KPsK, // K and pawns vs K 67 | KBPKB, // KBP vs KB 68 | KBPPKB, // KBPP vs KB 69 | KBPKN, // KBP vs KN 70 | KPKP // KP vs KP 71 | }; 72 | 73 | 74 | /// Endgame functions can be of two types depending on whether they return a 75 | /// Value or a ScaleFactor. 76 | 77 | template using 78 | eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type; 79 | 80 | 81 | /// Base and derived functors for endgame evaluation and scaling functions 82 | 83 | template 84 | struct EndgameBase { 85 | 86 | explicit EndgameBase(Color c) : strongSide(c), weakSide(~c) {} 87 | virtual ~EndgameBase() = default; 88 | virtual T operator()(const Position&) const = 0; 89 | 90 | const Color strongSide, weakSide; 91 | }; 92 | 93 | 94 | template> 95 | struct Endgame : public EndgameBase { 96 | 97 | explicit Endgame(Color c) : EndgameBase(c) {} 98 | T operator()(const Position&) const override; 99 | }; 100 | 101 | 102 | /// The Endgames namespace handles the pointers to endgame evaluation and scaling 103 | /// base objects in two std::map. We use polymorphism to invoke the actual 104 | /// endgame function by calling its virtual operator(). 105 | 106 | namespace Endgames { 107 | 108 | template using Ptr = std::unique_ptr>; 109 | template using Map = std::unordered_map>; 110 | 111 | extern std::pair, Map> maps; 112 | 113 | void init(); 114 | 115 | template 116 | Map& map() { 117 | return std::get::value>(maps); 118 | } 119 | 120 | template> 121 | void add(const std::string& code) { 122 | 123 | StateInfo st; 124 | map()[Position().set(code, WHITE, V, &st).material_key()] = Ptr(new Endgame(WHITE)); 125 | map()[Position().set(code, BLACK, V, &st).material_key()] = Ptr(new Endgame(BLACK)); 126 | } 127 | 128 | template 129 | const EndgameBase* probe(Key key) { 130 | auto it = map().find(key); 131 | return it != map().end() ? it->second.get() : nullptr; 132 | } 133 | } 134 | 135 | } // namespace Stockfish 136 | 137 | #endif // #ifndef ENDGAME_H_INCLUDED 138 | -------------------------------------------------------------------------------- /src/evaluate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef EVALUATE_H_INCLUDED 20 | #define EVALUATE_H_INCLUDED 21 | 22 | #include 23 | #include 24 | 25 | #include "types.h" 26 | 27 | namespace Stockfish { 28 | 29 | class Position; 30 | 31 | namespace Eval { 32 | 33 | std::string trace(Position& pos); 34 | Value evaluate(const Position& pos); 35 | 36 | #ifdef USE_NNUE 37 | extern bool useNNUE; 38 | extern std::string currentEvalFileName; 39 | 40 | // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue 41 | // for the build process (profile-build and fishtest) to work. Do not change the 42 | // name of the macro, as it is used in the Makefile. 43 | #define EvalFileDefaultName "nn-c38c3d8d3920.nnue" 44 | 45 | namespace NNUE { 46 | 47 | void init(); 48 | void verify(); 49 | 50 | } // namespace NNUE 51 | #endif 52 | 53 | } // namespace Eval 54 | 55 | } // namespace Stockfish 56 | 57 | #endif // #ifndef EVALUATE_H_INCLUDED 58 | -------------------------------------------------------------------------------- /src/incbin/UNLICENCE: -------------------------------------------------------------------------------- 1 | The file "incbin.h" is free and unencumbered software released into 2 | the public domain by Dale Weiler, see: 3 | 4 | 5 | Anyone is free to copy, modify, publish, use, compile, sell, or 6 | distribute this software, either in source code form or as a compiled 7 | binary, for any purpose, commercial or non-commercial, and by any 8 | means. 9 | 10 | In jurisdictions that recognize copyright laws, the author or authors 11 | of this software dedicate any and all copyright interest in the 12 | software to the public domain. We make this dedication for the benefit 13 | of the public at large and to the detriment of our heirs and 14 | successors. We intend this dedication to be an overt act of 15 | relinquishment in perpetuity of all present and future rights to this 16 | software under copyright law. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | For more information, please refer to 27 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include "bitboard.h" 22 | #include "endgame.h" 23 | #include "position.h" 24 | #include "psqt.h" 25 | #include "search.h" 26 | #include "syzygy/tbprobe.h" 27 | #include "thread.h" 28 | #include "tt.h" 29 | #include "uci.h" 30 | 31 | using namespace Stockfish; 32 | 33 | int main(int argc, char* argv[]) { 34 | 35 | std::cout << engine_info() << std::endl; 36 | 37 | CommandLine::init(argc, argv); 38 | UCI::init(Options); 39 | Tune::init(); 40 | PSQT::init(); 41 | Bitboards::init(); 42 | Position::init(); 43 | Bitbases::init(); 44 | Endgames::init(); 45 | Threads.set(size_t(Options["Threads"])); 46 | Search::clear(); // After threads are up 47 | #ifdef USE_NNUE 48 | Eval::NNUE::init(); 49 | #endif 50 | 51 | UCI::loop(argc, argv); 52 | 53 | Threads.set(0); 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /src/material.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef MATERIAL_H_INCLUDED 20 | #define MATERIAL_H_INCLUDED 21 | 22 | #include "endgame.h" 23 | #include "misc.h" 24 | #include "position.h" 25 | #include "types.h" 26 | 27 | namespace Stockfish::Material { 28 | 29 | /// Material::Entry contains various information about a material configuration. 30 | /// It contains a material imbalance evaluation, a function pointer to a special 31 | /// endgame evaluation function (which in most cases is nullptr, meaning that the 32 | /// standard evaluation function will be used), and scale factors. 33 | /// 34 | /// The scale factors are used to scale the evaluation score up or down. For 35 | /// instance, in KRB vs KR endgames, the score is scaled down by a factor of 4, 36 | /// which will result in scores of absolute value less than one pawn. 37 | 38 | struct Entry { 39 | 40 | Score imbalance() const { return score; } 41 | Phase game_phase() const { return (Phase)gamePhase; } 42 | bool specialized_eval_exists() const { return evaluationFunction != nullptr; } 43 | Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); } 44 | 45 | // scale_factor() takes a position and a color as input and returns a scale factor 46 | // for the given color. We have to provide the position in addition to the color 47 | // because the scale factor may also be a function which should be applied to 48 | // the position. For instance, in KBP vs K endgames, the scaling function looks 49 | // for rook pawns and wrong-colored bishops. 50 | ScaleFactor scale_factor(const Position& pos, Color c) const { 51 | ScaleFactor sf = scalingFunction[c] ? (*scalingFunction[c])(pos) 52 | : SCALE_FACTOR_NONE; 53 | return sf != SCALE_FACTOR_NONE ? sf : ScaleFactor(factor[c]); 54 | } 55 | 56 | Key key; 57 | const EndgameBase* evaluationFunction; 58 | const EndgameBase* scalingFunction[COLOR_NB]; // Could be one for each 59 | // side (e.g. KPKP, KBPsK) 60 | Score score; 61 | int16_t gamePhase; 62 | uint8_t factor[COLOR_NB]; 63 | }; 64 | 65 | using Table = HashTable; 66 | 67 | Entry* probe(const Position& pos); 68 | 69 | } // namespace Stockfish::Material 70 | 71 | #endif // #ifndef MATERIAL_H_INCLUDED 72 | -------------------------------------------------------------------------------- /src/misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef MISC_H_INCLUDED 20 | #define MISC_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "types.h" 30 | 31 | #define stringify2(x) #x 32 | #define stringify(x) stringify2(x) 33 | 34 | namespace Stockfish { 35 | 36 | std::string engine_info(bool to_uci = false); 37 | std::string compiler_info(); 38 | void prefetch(void* addr); 39 | void start_logger(const std::string& fname); 40 | void* std_aligned_alloc(size_t alignment, size_t size); 41 | void std_aligned_free(void* ptr); 42 | void* aligned_large_pages_alloc(size_t size); // memory aligned by page size, min alignment: 4096 bytes 43 | void aligned_large_pages_free(void* mem); // nop if mem == nullptr 44 | 45 | void dbg_hit_on(bool cond, int slot = 0); 46 | void dbg_mean_of(int64_t value, int slot = 0); 47 | void dbg_stdev_of(int64_t value, int slot = 0); 48 | void dbg_correl_of(int64_t value1, int64_t value2, int slot = 0); 49 | void dbg_print(); 50 | 51 | using TimePoint = std::chrono::milliseconds::rep; // A value in milliseconds 52 | static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits"); 53 | inline TimePoint now() { 54 | return std::chrono::duration_cast 55 | (std::chrono::steady_clock::now().time_since_epoch()).count(); 56 | } 57 | 58 | template 59 | struct HashTable { 60 | Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; } 61 | 62 | private: 63 | std::vector table = std::vector(Size); // Allocate on the heap 64 | }; 65 | 66 | 67 | enum SyncCout { IO_LOCK, IO_UNLOCK }; 68 | std::ostream& operator<<(std::ostream&, SyncCout); 69 | 70 | #define sync_cout std::cout << IO_LOCK 71 | #define sync_endl std::endl << IO_UNLOCK 72 | 73 | 74 | // align_ptr_up() : get the first aligned element of an array. 75 | // ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes, 76 | // where N is the number of elements in the array. 77 | template 78 | T* align_ptr_up(T* ptr) 79 | { 80 | static_assert(alignof(T) < Alignment); 81 | 82 | const uintptr_t ptrint = reinterpret_cast(reinterpret_cast(ptr)); 83 | return reinterpret_cast(reinterpret_cast((ptrint + (Alignment - 1)) / Alignment * Alignment)); 84 | } 85 | 86 | 87 | // IsLittleEndian : true if and only if the binary is compiled on a little endian machine 88 | static inline const union { uint32_t i; char c[4]; } Le = { 0x01020304 }; 89 | static inline const bool IsLittleEndian = (Le.c[0] == 4); 90 | 91 | 92 | template 93 | class ValueList { 94 | 95 | public: 96 | std::size_t size() const { return size_; } 97 | void push_back(const T& value) { values_[size_++] = value; } 98 | const T* begin() const { return values_; } 99 | const T* end() const { return values_ + size_; } 100 | 101 | private: 102 | T values_[MaxSize]; 103 | std::size_t size_ = 0; 104 | }; 105 | 106 | 107 | /// xorshift64star Pseudo-Random Number Generator 108 | /// This class is based on original code written and dedicated 109 | /// to the public domain by Sebastiano Vigna (2014). 110 | /// It has the following characteristics: 111 | /// 112 | /// - Outputs 64-bit numbers 113 | /// - Passes Dieharder and SmallCrush test batteries 114 | /// - Does not require warm-up, no zeroland to escape 115 | /// - Internal state is a single 64-bit integer 116 | /// - Period is 2^64 - 1 117 | /// - Speed: 1.60 ns/call (Core i7 @3.40GHz) 118 | /// 119 | /// For further analysis see 120 | /// 121 | 122 | class PRNG { 123 | 124 | uint64_t s; 125 | 126 | uint64_t rand64() { 127 | 128 | s ^= s >> 12, s ^= s << 25, s ^= s >> 27; 129 | return s * 2685821657736338717LL; 130 | } 131 | 132 | public: 133 | PRNG(uint64_t seed) : s(seed) { assert(seed); } 134 | 135 | template T rand() { return T(rand64()); } 136 | 137 | /// Special generator used to fast init magic numbers. 138 | /// Output values only have 1/8th of their bits set on average. 139 | template T sparse_rand() 140 | { return T(rand64() & rand64() & rand64()); } 141 | }; 142 | 143 | inline uint64_t mul_hi64(uint64_t a, uint64_t b) { 144 | #if defined(__GNUC__) && defined(IS_64BIT) 145 | __extension__ using uint128 = unsigned __int128; 146 | return ((uint128)a * (uint128)b) >> 64; 147 | #else 148 | uint64_t aL = (uint32_t)a, aH = a >> 32; 149 | uint64_t bL = (uint32_t)b, bH = b >> 32; 150 | uint64_t c1 = (aL * bL) >> 32; 151 | uint64_t c2 = aH * bL + c1; 152 | uint64_t c3 = aL * bH + (uint32_t)c2; 153 | return aH * bH + (c2 >> 32) + (c3 >> 32); 154 | #endif 155 | } 156 | 157 | /// Under Windows it is not possible for a process to run on more than one 158 | /// logical processor group. This usually means to be limited to use max 64 159 | /// cores. To overcome this, some special platform specific API should be 160 | /// called to set group affinity for each thread. Original code from Texel by 161 | /// Peter Österlund. 162 | 163 | namespace WinProcGroup { 164 | void bindThisThread(size_t idx); 165 | } 166 | 167 | namespace CommandLine { 168 | void init(int argc, char* argv[]); 169 | 170 | extern std::string binaryDirectory; // path of the executable directory 171 | extern std::string workingDirectory; // path of the working directory 172 | } 173 | 174 | } // namespace Stockfish 175 | 176 | #endif // #ifndef MISC_H_INCLUDED 177 | -------------------------------------------------------------------------------- /src/movegen.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef MOVEGEN_H_INCLUDED 20 | #define MOVEGEN_H_INCLUDED 21 | 22 | #include 23 | 24 | #include "types.h" 25 | 26 | namespace Stockfish { 27 | 28 | class Position; 29 | 30 | enum GenType { 31 | CAPTURES, 32 | QUIETS, 33 | QUIET_CHECKS, 34 | EVASIONS, 35 | NON_EVASIONS, 36 | LEGAL 37 | }; 38 | 39 | struct ExtMove { 40 | Move move; 41 | int value; 42 | 43 | operator Move() const { return move; } 44 | void operator=(Move m) { move = m; } 45 | 46 | // Inhibit unwanted implicit conversions to Move 47 | // with an ambiguity that yields to a compile error. 48 | operator float() const = delete; 49 | }; 50 | 51 | inline bool operator<(const ExtMove& f, const ExtMove& s) { 52 | return f.value < s.value; 53 | } 54 | 55 | template 56 | ExtMove* generate(const Position& pos, ExtMove* moveList); 57 | 58 | /// The MoveList struct is a simple wrapper around generate(). It sometimes comes 59 | /// in handy to use this class instead of the low level generate() function. 60 | template 61 | struct MoveList { 62 | 63 | explicit MoveList(const Position& pos) : last(generate(pos, moveList)) {} 64 | const ExtMove* begin() const { return moveList; } 65 | const ExtMove* end() const { return last; } 66 | size_t size() const { return last - moveList; } 67 | bool contains(Move move) const { 68 | return std::find(begin(), end(), move) != end(); 69 | } 70 | 71 | private: 72 | ExtMove moveList[MAX_MOVES], *last; 73 | }; 74 | 75 | } // namespace Stockfish 76 | 77 | #endif // #ifndef MOVEGEN_H_INCLUDED 78 | -------------------------------------------------------------------------------- /src/movepick.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef MOVEPICK_H_INCLUDED 20 | #define MOVEPICK_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "movegen.h" 27 | #include "position.h" 28 | #include "types.h" 29 | 30 | namespace Stockfish { 31 | 32 | /// StatsEntry stores the stat table value. It is usually a number but could 33 | /// be a move or even a nested history. We use a class instead of naked value 34 | /// to directly call history update operator<<() on the entry so to use stats 35 | /// tables at caller sites as simple multi-dim arrays. 36 | template 37 | class StatsEntry { 38 | 39 | T entry; 40 | 41 | public: 42 | void operator=(const T& v) { entry = v; } 43 | T* operator&() { return &entry; } 44 | T* operator->() { return &entry; } 45 | operator const T&() const { return entry; } 46 | 47 | void operator<<(int bonus) { 48 | assert(abs(bonus) <= D); // Ensure range is [-D, D] 49 | static_assert(D <= std::numeric_limits::max(), "D overflows T"); 50 | 51 | entry += bonus - entry * abs(bonus) / D; 52 | 53 | assert(abs(entry) <= D); 54 | } 55 | }; 56 | 57 | /// Stats is a generic N-dimensional array used to store various statistics. 58 | /// The first template parameter T is the base type of the array, the second 59 | /// template parameter D limits the range of updates in [-D, D] when we update 60 | /// values with the << operator, while the last parameters (Size and Sizes) 61 | /// encode the dimensions of the array. 62 | template 63 | struct Stats : public std::array, Size> 64 | { 65 | using stats = Stats; 66 | 67 | void fill(const T& v) { 68 | 69 | // For standard-layout 'this' points to first struct member 70 | assert(std::is_standard_layout::value); 71 | 72 | using entry = StatsEntry; 73 | entry* p = reinterpret_cast(this); 74 | std::fill(p, p + sizeof(*this) / sizeof(entry), v); 75 | } 76 | }; 77 | 78 | template 79 | struct Stats : public std::array, Size> {}; 80 | 81 | /// In stats table, D=0 means that the template parameter is not used 82 | enum StatsParams { NOT_USED = 0 }; 83 | enum StatsType { NoCaptures, Captures }; 84 | 85 | /// ButterflyHistory records how often quiet moves have been successful or 86 | /// unsuccessful during the current search, and is used for reduction and move 87 | /// ordering decisions. It uses 2 tables (one for each color) indexed by 88 | /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards 89 | /// (~11 elo) 90 | using ButterflyHistory = Stats; 91 | 92 | /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous 93 | /// move, see www.chessprogramming.org/Countermove_Heuristic 94 | using CounterMoveHistory = Stats; 95 | 96 | /// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] 97 | using CapturePieceToHistory = Stats; 98 | 99 | /// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] 100 | using PieceToHistory = Stats; 101 | 102 | /// ContinuationHistory is the combined history of a given pair of moves, usually 103 | /// the current one given a previous one. The nested history table is based on 104 | /// PieceToHistory instead of ButterflyBoards. 105 | /// (~63 elo) 106 | using ContinuationHistory = Stats; 107 | 108 | 109 | /// MovePicker class is used to pick one pseudo-legal move at a time from the 110 | /// current position. The most important method is next_move(), which returns a 111 | /// new pseudo-legal move each time it is called, until there are no moves left, 112 | /// when MOVE_NONE is returned. In order to improve the efficiency of the 113 | /// alpha-beta algorithm, MovePicker attempts to return the moves which are most 114 | /// likely to get a cut-off first. 115 | class MovePicker { 116 | 117 | enum PickType { Next, Best }; 118 | 119 | public: 120 | MovePicker(const MovePicker&) = delete; 121 | MovePicker& operator=(const MovePicker&) = delete; 122 | MovePicker(const Position&, Move, Depth, const ButterflyHistory*, 123 | const CapturePieceToHistory*, 124 | const PieceToHistory**, 125 | Move, 126 | const Move*); 127 | MovePicker(const Position&, Move, Depth, const ButterflyHistory*, 128 | const CapturePieceToHistory*, 129 | const PieceToHistory**, 130 | Square); 131 | MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); 132 | Move next_move(bool skipQuiets = false); 133 | 134 | private: 135 | template Move select(Pred); 136 | template void score(); 137 | ExtMove* begin() { return cur; } 138 | ExtMove* end() { return endMoves; } 139 | 140 | const Position& pos; 141 | const ButterflyHistory* mainHistory; 142 | const CapturePieceToHistory* captureHistory; 143 | const PieceToHistory** continuationHistory; 144 | Move ttMove; 145 | ExtMove refutations[3], *cur, *endMoves, *endBadCaptures; 146 | int stage; 147 | Square recaptureSquare; 148 | Value threshold; 149 | Depth depth; 150 | ExtMove moves[MAX_MOVES]; 151 | }; 152 | 153 | } // namespace Stockfish 154 | 155 | #endif // #ifndef MOVEPICK_H_INCLUDED 156 | -------------------------------------------------------------------------------- /src/nnue/evaluate_nnue.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | // header used in NNUE evaluation function 20 | 21 | #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED 22 | #define NNUE_EVALUATE_NNUE_H_INCLUDED 23 | 24 | #include "nnue_feature_transformer.h" 25 | 26 | #include 27 | 28 | namespace Stockfish::Eval::NNUE { 29 | 30 | // Hash value of evaluation function structure 31 | constexpr std::uint32_t HashValue = 32 | FeatureTransformer::get_hash_value() ^ Network::get_hash_value(); 33 | 34 | 35 | // Deleter for automating release of memory area 36 | template 37 | struct AlignedDeleter { 38 | void operator()(T* ptr) const { 39 | ptr->~T(); 40 | std_aligned_free(ptr); 41 | } 42 | }; 43 | 44 | template 45 | struct LargePageDeleter { 46 | void operator()(T* ptr) const { 47 | ptr->~T(); 48 | aligned_large_pages_free(ptr); 49 | } 50 | }; 51 | 52 | template 53 | using AlignedPtr = std::unique_ptr>; 54 | 55 | template 56 | using LargePagePtr = std::unique_ptr>; 57 | 58 | std::string trace(Position& pos); 59 | Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr); 60 | void hint_common_parent_position(const Position& pos); 61 | 62 | bool load_eval(std::string name, std::istream& stream); 63 | bool save_eval(std::ostream& stream); 64 | bool save_eval(const std::optional& filename); 65 | 66 | } // namespace Stockfish::Eval::NNUE 67 | 68 | #endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED 69 | -------------------------------------------------------------------------------- /src/nnue/features/half_ka_v2_hm.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | //Definition of input features HalfKAv2_hm of NNUE evaluation function 20 | 21 | #include "half_ka_v2_hm.h" 22 | 23 | #include "../../position.h" 24 | 25 | namespace Stockfish::Eval::NNUE::Features { 26 | 27 | // Index of a feature for a given king position and another piece on some square 28 | template 29 | inline IndexType HalfKAv2_hm::make_index(Square s, Piece pc, Square ksq) { 30 | return IndexType((int(s) ^ OrientTBL[Perspective][ksq]) + PieceSquareIndex[Perspective][pc] + KingBuckets[Perspective][ksq]); 31 | } 32 | 33 | // Get a list of indices for active features 34 | template 35 | void HalfKAv2_hm::append_active_indices( 36 | const Position& pos, 37 | IndexList& active 38 | ) { 39 | Square ksq = pos.square(Perspective); 40 | Bitboard bb = pos.pieces(); 41 | while (bb) 42 | { 43 | Square s = pop_lsb(bb); 44 | active.push_back(make_index(s, pos.piece_on(s), ksq)); 45 | } 46 | } 47 | 48 | // Explicit template instantiations 49 | template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); 50 | template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); 51 | 52 | // append_changed_indices() : get a list of indices for recently changed features 53 | template 54 | void HalfKAv2_hm::append_changed_indices( 55 | Square ksq, 56 | const DirtyPiece& dp, 57 | IndexList& removed, 58 | IndexList& added 59 | ) { 60 | for (int i = 0; i < dp.dirty_num; ++i) { 61 | if (dp.from[i] != SQ_NONE) 62 | removed.push_back(make_index(dp.from[i], dp.piece[i], ksq)); 63 | if (dp.to[i] != SQ_NONE) 64 | added.push_back(make_index(dp.to[i], dp.piece[i], ksq)); 65 | } 66 | } 67 | 68 | // Explicit template instantiations 69 | template void HalfKAv2_hm::append_changed_indices(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added); 70 | template void HalfKAv2_hm::append_changed_indices(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added); 71 | 72 | int HalfKAv2_hm::update_cost(const StateInfo* st) { 73 | return st->dirtyPiece.dirty_num; 74 | } 75 | 76 | int HalfKAv2_hm::refresh_cost(const Position& pos) { 77 | return pos.count(); 78 | } 79 | 80 | bool HalfKAv2_hm::requires_refresh(const StateInfo* st, Color perspective) { 81 | return st->dirtyPiece.piece[0] == make_piece(perspective, KING); 82 | } 83 | 84 | } // namespace Stockfish::Eval::NNUE::Features 85 | -------------------------------------------------------------------------------- /src/nnue/features/half_ka_v2_hm.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | //Definition of input features HalfKP of NNUE evaluation function 20 | 21 | #ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED 22 | #define NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED 23 | 24 | #include "../nnue_common.h" 25 | 26 | #include "../../evaluate.h" 27 | #include "../../misc.h" 28 | 29 | namespace Stockfish { 30 | struct StateInfo; 31 | } 32 | 33 | namespace Stockfish::Eval::NNUE::Features { 34 | 35 | // Feature HalfKAv2_hm: Combination of the position of own king 36 | // and the position of pieces. Position mirrored such that king always on e..h files. 37 | class HalfKAv2_hm { 38 | 39 | // unique number for each piece type on each square 40 | enum { 41 | PS_NONE = 0, 42 | PS_W_PAWN = 0, 43 | PS_B_PAWN = 1 * SQUARE_NB, 44 | PS_W_KNIGHT = 2 * SQUARE_NB, 45 | PS_B_KNIGHT = 3 * SQUARE_NB, 46 | PS_W_BISHOP = 4 * SQUARE_NB, 47 | PS_B_BISHOP = 5 * SQUARE_NB, 48 | PS_W_ROOK = 6 * SQUARE_NB, 49 | PS_B_ROOK = 7 * SQUARE_NB, 50 | PS_W_QUEEN = 8 * SQUARE_NB, 51 | PS_B_QUEEN = 9 * SQUARE_NB, 52 | PS_KING = 10 * SQUARE_NB, 53 | PS_NB = 11 * SQUARE_NB 54 | }; 55 | 56 | static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = { 57 | // convention: W - us, B - them 58 | // viewed from other side, W and B are reversed 59 | { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE, 60 | PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE }, 61 | { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE, 62 | PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE } 63 | }; 64 | 65 | // Index of a feature for a given king position and another piece on some square 66 | template 67 | static IndexType make_index(Square s, Piece pc, Square ksq); 68 | 69 | public: 70 | // Feature name 71 | static constexpr const char* Name = "HalfKAv2_hm(Friend)"; 72 | 73 | // Hash value embedded in the evaluation file 74 | static constexpr std::uint32_t HashValue = 0x7f234cb8u; 75 | 76 | // Number of feature dimensions 77 | static constexpr IndexType Dimensions = 78 | static_cast(SQUARE_NB) * static_cast(PS_NB) / 2; 79 | 80 | #define B(v) (v * PS_NB) 81 | static constexpr int KingBuckets[COLOR_NB][SQUARE_NB] = { 82 | { B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28), 83 | B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24), 84 | B(20), B(21), B(22), B(23), B(23), B(22), B(21), B(20), 85 | B(16), B(17), B(18), B(19), B(19), B(18), B(17), B(16), 86 | B(12), B(13), B(14), B(15), B(15), B(14), B(13), B(12), 87 | B( 8), B( 9), B(10), B(11), B(11), B(10), B( 9), B( 8), 88 | B( 4), B( 5), B( 6), B( 7), B( 7), B( 6), B( 5), B( 4), 89 | B( 0), B( 1), B( 2), B( 3), B( 3), B( 2), B( 1), B( 0) }, 90 | { B( 0), B( 1), B( 2), B( 3), B( 3), B( 2), B( 1), B( 0), 91 | B( 4), B( 5), B( 6), B( 7), B( 7), B( 6), B( 5), B( 4), 92 | B( 8), B( 9), B(10), B(11), B(11), B(10), B( 9), B( 8), 93 | B(12), B(13), B(14), B(15), B(15), B(14), B(13), B(12), 94 | B(16), B(17), B(18), B(19), B(19), B(18), B(17), B(16), 95 | B(20), B(21), B(22), B(23), B(23), B(22), B(21), B(20), 96 | B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24), 97 | B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28) } 98 | }; 99 | #undef B 100 | 101 | // Orient a square according to perspective (rotates by 180 for black) 102 | static constexpr int OrientTBL[COLOR_NB][SQUARE_NB] = { 103 | { SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, 104 | SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, 105 | SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, 106 | SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, 107 | SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, 108 | SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, 109 | SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, 110 | SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1 }, 111 | { SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, 112 | SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, 113 | SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, 114 | SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, 115 | SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, 116 | SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, 117 | SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, 118 | SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8 } 119 | }; 120 | 121 | // Maximum number of simultaneously active features. 122 | static constexpr IndexType MaxActiveDimensions = 32; 123 | using IndexList = ValueList; 124 | 125 | // Get a list of indices for active features 126 | template 127 | static void append_active_indices( 128 | const Position& pos, 129 | IndexList& active); 130 | 131 | // Get a list of indices for recently changed features 132 | template 133 | static void append_changed_indices( 134 | Square ksq, 135 | const DirtyPiece& dp, 136 | IndexList& removed, 137 | IndexList& added 138 | ); 139 | 140 | // Returns the cost of updating one perspective, the most costly one. 141 | // Assumes no refresh needed. 142 | static int update_cost(const StateInfo* st); 143 | static int refresh_cost(const Position& pos); 144 | 145 | // Returns whether the change stored in this StateInfo means that 146 | // a full accumulator refresh is required. 147 | static bool requires_refresh(const StateInfo* st, Color perspective); 148 | }; 149 | 150 | } // namespace Stockfish::Eval::NNUE::Features 151 | 152 | #endif // #ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED 153 | -------------------------------------------------------------------------------- /src/nnue/layers/clipped_relu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | // Definition of layer ClippedReLU of NNUE evaluation function 20 | 21 | #ifndef NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED 22 | #define NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED 23 | 24 | #include "../nnue_common.h" 25 | 26 | namespace Stockfish::Eval::NNUE::Layers { 27 | 28 | // Clipped ReLU 29 | template 30 | class ClippedReLU { 31 | public: 32 | // Input/output type 33 | using InputType = std::int32_t; 34 | using OutputType = std::uint8_t; 35 | 36 | // Number of input/output dimensions 37 | static constexpr IndexType InputDimensions = InDims; 38 | static constexpr IndexType OutputDimensions = InputDimensions; 39 | static constexpr IndexType PaddedOutputDimensions = 40 | ceil_to_multiple(OutputDimensions, 32); 41 | 42 | using OutputBuffer = OutputType[PaddedOutputDimensions]; 43 | 44 | // Hash value embedded in the evaluation file 45 | static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { 46 | std::uint32_t hashValue = 0x538D24C7u; 47 | hashValue += prevHash; 48 | return hashValue; 49 | } 50 | 51 | // Read network parameters 52 | bool read_parameters(std::istream&) { 53 | return true; 54 | } 55 | 56 | // Write network parameters 57 | bool write_parameters(std::ostream&) const { 58 | return true; 59 | } 60 | 61 | // Forward propagation 62 | const OutputType* propagate( 63 | const InputType* input, OutputType* output) const { 64 | 65 | #if defined(USE_AVX2) 66 | if constexpr (InputDimensions % SimdWidth == 0) { 67 | constexpr IndexType NumChunks = InputDimensions / SimdWidth; 68 | const __m256i Zero = _mm256_setzero_si256(); 69 | const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); 70 | const auto in = reinterpret_cast(input); 71 | const auto out = reinterpret_cast<__m256i*>(output); 72 | for (IndexType i = 0; i < NumChunks; ++i) { 73 | const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( 74 | _mm256_load_si256(&in[i * 4 + 0]), 75 | _mm256_load_si256(&in[i * 4 + 1])), WeightScaleBits); 76 | const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( 77 | _mm256_load_si256(&in[i * 4 + 2]), 78 | _mm256_load_si256(&in[i * 4 + 3])), WeightScaleBits); 79 | _mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( 80 | _mm256_packs_epi16(words0, words1), Zero), Offsets)); 81 | } 82 | } else { 83 | constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); 84 | const __m128i Zero = _mm_setzero_si128(); 85 | const auto in = reinterpret_cast(input); 86 | const auto out = reinterpret_cast<__m128i*>(output); 87 | for (IndexType i = 0; i < NumChunks; ++i) { 88 | const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32( 89 | _mm_load_si128(&in[i * 4 + 0]), 90 | _mm_load_si128(&in[i * 4 + 1])), WeightScaleBits); 91 | const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32( 92 | _mm_load_si128(&in[i * 4 + 2]), 93 | _mm_load_si128(&in[i * 4 + 3])), WeightScaleBits); 94 | const __m128i packedbytes = _mm_packs_epi16(words0, words1); 95 | _mm_store_si128(&out[i], _mm_max_epi8(packedbytes, Zero)); 96 | } 97 | } 98 | constexpr IndexType Start = 99 | InputDimensions % SimdWidth == 0 100 | ? InputDimensions / SimdWidth * SimdWidth 101 | : InputDimensions / (SimdWidth / 2) * (SimdWidth / 2); 102 | 103 | #elif defined(USE_SSE2) 104 | constexpr IndexType NumChunks = InputDimensions / SimdWidth; 105 | 106 | #ifdef USE_SSE41 107 | const __m128i Zero = _mm_setzero_si128(); 108 | #else 109 | const __m128i k0x80s = _mm_set1_epi8(-128); 110 | #endif 111 | 112 | const auto in = reinterpret_cast(input); 113 | const auto out = reinterpret_cast<__m128i*>(output); 114 | for (IndexType i = 0; i < NumChunks; ++i) { 115 | const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32( 116 | _mm_load_si128(&in[i * 4 + 0]), 117 | _mm_load_si128(&in[i * 4 + 1])), WeightScaleBits); 118 | const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32( 119 | _mm_load_si128(&in[i * 4 + 2]), 120 | _mm_load_si128(&in[i * 4 + 3])), WeightScaleBits); 121 | const __m128i packedbytes = _mm_packs_epi16(words0, words1); 122 | _mm_store_si128(&out[i], 123 | 124 | #ifdef USE_SSE41 125 | _mm_max_epi8(packedbytes, Zero) 126 | #else 127 | _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) 128 | #endif 129 | 130 | ); 131 | } 132 | constexpr IndexType Start = NumChunks * SimdWidth; 133 | 134 | #elif defined(USE_MMX) 135 | constexpr IndexType NumChunks = InputDimensions / SimdWidth; 136 | const __m64 k0x80s = _mm_set1_pi8(-128); 137 | const auto in = reinterpret_cast(input); 138 | const auto out = reinterpret_cast<__m64*>(output); 139 | for (IndexType i = 0; i < NumChunks; ++i) { 140 | const __m64 words0 = _mm_srai_pi16( 141 | _mm_packs_pi32(in[i * 4 + 0], in[i * 4 + 1]), 142 | WeightScaleBits); 143 | const __m64 words1 = _mm_srai_pi16( 144 | _mm_packs_pi32(in[i * 4 + 2], in[i * 4 + 3]), 145 | WeightScaleBits); 146 | const __m64 packedbytes = _mm_packs_pi16(words0, words1); 147 | out[i] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s); 148 | } 149 | _mm_empty(); 150 | constexpr IndexType Start = NumChunks * SimdWidth; 151 | 152 | #elif defined(USE_NEON) 153 | constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); 154 | const int8x8_t Zero = {0}; 155 | const auto in = reinterpret_cast(input); 156 | const auto out = reinterpret_cast(output); 157 | for (IndexType i = 0; i < NumChunks; ++i) { 158 | int16x8_t shifted; 159 | const auto pack = reinterpret_cast(&shifted); 160 | pack[0] = vqshrn_n_s32(in[i * 2 + 0], WeightScaleBits); 161 | pack[1] = vqshrn_n_s32(in[i * 2 + 1], WeightScaleBits); 162 | out[i] = vmax_s8(vqmovn_s16(shifted), Zero); 163 | } 164 | constexpr IndexType Start = NumChunks * (SimdWidth / 2); 165 | #else 166 | constexpr IndexType Start = 0; 167 | #endif 168 | 169 | for (IndexType i = Start; i < InputDimensions; ++i) { 170 | output[i] = static_cast( 171 | std::max(0, std::min(127, input[i] >> WeightScaleBits))); 172 | } 173 | 174 | return output; 175 | } 176 | }; 177 | 178 | } // namespace Stockfish::Eval::NNUE::Layers 179 | 180 | #endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED 181 | -------------------------------------------------------------------------------- /src/nnue/layers/sqr_clipped_relu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | // Definition of layer ClippedReLU of NNUE evaluation function 20 | 21 | #ifndef NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED 22 | #define NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED 23 | 24 | #include "../nnue_common.h" 25 | 26 | namespace Stockfish::Eval::NNUE::Layers { 27 | 28 | // Clipped ReLU 29 | template 30 | class SqrClippedReLU { 31 | public: 32 | // Input/output type 33 | using InputType = std::int32_t; 34 | using OutputType = std::uint8_t; 35 | 36 | // Number of input/output dimensions 37 | static constexpr IndexType InputDimensions = InDims; 38 | static constexpr IndexType OutputDimensions = InputDimensions; 39 | static constexpr IndexType PaddedOutputDimensions = 40 | ceil_to_multiple(OutputDimensions, 32); 41 | 42 | using OutputBuffer = OutputType[PaddedOutputDimensions]; 43 | 44 | // Hash value embedded in the evaluation file 45 | static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { 46 | std::uint32_t hashValue = 0x538D24C7u; 47 | hashValue += prevHash; 48 | return hashValue; 49 | } 50 | 51 | // Read network parameters 52 | bool read_parameters(std::istream&) { 53 | return true; 54 | } 55 | 56 | // Write network parameters 57 | bool write_parameters(std::ostream&) const { 58 | return true; 59 | } 60 | 61 | // Forward propagation 62 | const OutputType* propagate( 63 | const InputType* input, OutputType* output) const { 64 | 65 | #if defined(USE_SSE2) 66 | constexpr IndexType NumChunks = InputDimensions / 16; 67 | 68 | #ifdef USE_SSE41 69 | const __m128i Zero = _mm_setzero_si128(); 70 | #else 71 | const __m128i k0x80s = _mm_set1_epi8(-128); 72 | #endif 73 | 74 | static_assert(WeightScaleBits == 6); 75 | const auto in = reinterpret_cast(input); 76 | const auto out = reinterpret_cast<__m128i*>(output); 77 | for (IndexType i = 0; i < NumChunks; ++i) { 78 | __m128i words0 = _mm_packs_epi32( 79 | _mm_load_si128(&in[i * 4 + 0]), 80 | _mm_load_si128(&in[i * 4 + 1])); 81 | __m128i words1 = _mm_packs_epi32( 82 | _mm_load_si128(&in[i * 4 + 2]), 83 | _mm_load_si128(&in[i * 4 + 3])); 84 | 85 | // Not sure if 86 | words0 = _mm_srli_epi16(_mm_mulhi_epi16(words0, words0), 3); 87 | words1 = _mm_srli_epi16(_mm_mulhi_epi16(words1, words1), 3); 88 | 89 | const __m128i packedbytes = _mm_packs_epi16(words0, words1); 90 | 91 | _mm_store_si128(&out[i], 92 | 93 | #ifdef USE_SSE41 94 | _mm_max_epi8(packedbytes, Zero) 95 | #else 96 | _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) 97 | #endif 98 | 99 | ); 100 | } 101 | constexpr IndexType Start = NumChunks * 16; 102 | 103 | #else 104 | constexpr IndexType Start = 0; 105 | #endif 106 | 107 | for (IndexType i = Start; i < InputDimensions; ++i) { 108 | output[i] = static_cast( 109 | // really should be /127 but we need to make it fast 110 | // needs to be accounted for in the trainer 111 | std::max(0ll, std::min(127ll, (((long long)input[i] * input[i]) >> (2 * WeightScaleBits)) / 128))); 112 | } 113 | 114 | return output; 115 | } 116 | }; 117 | 118 | } // namespace Stockfish::Eval::NNUE::Layers 119 | 120 | #endif // NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED 121 | -------------------------------------------------------------------------------- /src/nnue/nnue_accumulator.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | // Class for difference calculation of NNUE evaluation function 20 | 21 | #ifndef NNUE_ACCUMULATOR_H_INCLUDED 22 | #define NNUE_ACCUMULATOR_H_INCLUDED 23 | 24 | #include "nnue_architecture.h" 25 | 26 | namespace Stockfish::Eval::NNUE { 27 | 28 | // Class that holds the result of affine transformation of input features 29 | struct alignas(CacheLineSize) Accumulator { 30 | std::int16_t accumulation[2][TransformedFeatureDimensions]; 31 | std::int32_t psqtAccumulation[2][PSQTBuckets]; 32 | bool computed[2]; 33 | }; 34 | 35 | } // namespace Stockfish::Eval::NNUE 36 | 37 | #endif // NNUE_ACCUMULATOR_H_INCLUDED 38 | -------------------------------------------------------------------------------- /src/nnue/nnue_architecture.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | // Input features and network structure used in NNUE evaluation function 20 | 21 | #ifndef NNUE_ARCHITECTURE_H_INCLUDED 22 | #define NNUE_ARCHITECTURE_H_INCLUDED 23 | 24 | #include 25 | 26 | #include "nnue_common.h" 27 | 28 | #include "features/half_ka_v2_hm.h" 29 | 30 | #include "layers/affine_transform_sparse_input.h" 31 | #include "layers/affine_transform.h" 32 | #include "layers/clipped_relu.h" 33 | #include "layers/sqr_clipped_relu.h" 34 | 35 | #include "../misc.h" 36 | 37 | namespace Stockfish::Eval::NNUE { 38 | 39 | // Input features used in evaluation function 40 | using FeatureSet = Features::HalfKAv2_hm; 41 | 42 | // Number of input feature dimensions after conversion 43 | constexpr IndexType TransformedFeatureDimensions = 2048; 44 | constexpr IndexType PSQTBuckets = 8; 45 | constexpr IndexType LayerStacks = 8; 46 | 47 | struct Network 48 | { 49 | static constexpr int FC_0_OUTPUTS = 15; 50 | static constexpr int FC_1_OUTPUTS = 32; 51 | 52 | Layers::AffineTransformSparseInput fc_0; 53 | Layers::SqrClippedReLU ac_sqr_0; 54 | Layers::ClippedReLU ac_0; 55 | Layers::AffineTransform fc_1; 56 | Layers::ClippedReLU ac_1; 57 | Layers::AffineTransform fc_2; 58 | 59 | // Hash value embedded in the evaluation file 60 | static constexpr std::uint32_t get_hash_value() { 61 | // input slice hash 62 | std::uint32_t hashValue = 0xEC42E90Du; 63 | hashValue ^= TransformedFeatureDimensions * 2; 64 | 65 | hashValue = decltype(fc_0)::get_hash_value(hashValue); 66 | hashValue = decltype(ac_0)::get_hash_value(hashValue); 67 | hashValue = decltype(fc_1)::get_hash_value(hashValue); 68 | hashValue = decltype(ac_1)::get_hash_value(hashValue); 69 | hashValue = decltype(fc_2)::get_hash_value(hashValue); 70 | 71 | return hashValue; 72 | } 73 | 74 | // Read network parameters 75 | bool read_parameters(std::istream& stream) { 76 | return fc_0.read_parameters(stream) 77 | && ac_0.read_parameters(stream) 78 | && fc_1.read_parameters(stream) 79 | && ac_1.read_parameters(stream) 80 | && fc_2.read_parameters(stream); 81 | } 82 | 83 | // Write network parameters 84 | bool write_parameters(std::ostream& stream) const { 85 | return fc_0.write_parameters(stream) 86 | && ac_0.write_parameters(stream) 87 | && fc_1.write_parameters(stream) 88 | && ac_1.write_parameters(stream) 89 | && fc_2.write_parameters(stream); 90 | } 91 | 92 | std::int32_t propagate(const TransformedFeatureType* transformedFeatures) 93 | { 94 | struct alignas(CacheLineSize) Buffer 95 | { 96 | alignas(CacheLineSize) decltype(fc_0)::OutputBuffer fc_0_out; 97 | alignas(CacheLineSize) decltype(ac_sqr_0)::OutputType ac_sqr_0_out[ceil_to_multiple(FC_0_OUTPUTS * 2, 32)]; 98 | alignas(CacheLineSize) decltype(ac_0)::OutputBuffer ac_0_out; 99 | alignas(CacheLineSize) decltype(fc_1)::OutputBuffer fc_1_out; 100 | alignas(CacheLineSize) decltype(ac_1)::OutputBuffer ac_1_out; 101 | alignas(CacheLineSize) decltype(fc_2)::OutputBuffer fc_2_out; 102 | 103 | Buffer() 104 | { 105 | std::memset(this, 0, sizeof(*this)); 106 | } 107 | }; 108 | 109 | #if defined(__clang__) && (__APPLE__) 110 | // workaround for a bug reported with xcode 12 111 | static thread_local auto tlsBuffer = std::make_unique(); 112 | // Access TLS only once, cache result. 113 | Buffer& buffer = *tlsBuffer; 114 | #else 115 | alignas(CacheLineSize) static thread_local Buffer buffer; 116 | #endif 117 | 118 | fc_0.propagate(transformedFeatures, buffer.fc_0_out); 119 | ac_sqr_0.propagate(buffer.fc_0_out, buffer.ac_sqr_0_out); 120 | ac_0.propagate(buffer.fc_0_out, buffer.ac_0_out); 121 | std::memcpy(buffer.ac_sqr_0_out + FC_0_OUTPUTS, buffer.ac_0_out, FC_0_OUTPUTS * sizeof(decltype(ac_0)::OutputType)); 122 | fc_1.propagate(buffer.ac_sqr_0_out, buffer.fc_1_out); 123 | ac_1.propagate(buffer.fc_1_out, buffer.ac_1_out); 124 | fc_2.propagate(buffer.ac_1_out, buffer.fc_2_out); 125 | 126 | // buffer.fc_0_out[FC_0_OUTPUTS] is such that 1.0 is equal to 127*(1<. 17 | */ 18 | 19 | // Constants used in NNUE evaluation function 20 | 21 | #ifndef NNUE_COMMON_H_INCLUDED 22 | #define NNUE_COMMON_H_INCLUDED 23 | 24 | #include 25 | #include 26 | 27 | #include "../misc.h" // for IsLittleEndian 28 | 29 | #if defined(USE_AVX2) 30 | #include 31 | 32 | #elif defined(USE_SSE41) 33 | #include 34 | 35 | #elif defined(USE_SSSE3) 36 | #include 37 | 38 | #elif defined(USE_SSE2) 39 | #include 40 | 41 | #elif defined(USE_MMX) 42 | #include 43 | 44 | #elif defined(USE_NEON) 45 | #include 46 | #endif 47 | 48 | namespace Stockfish::Eval::NNUE { 49 | 50 | // Version of the evaluation file 51 | constexpr std::uint32_t Version = 0x7AF32F20u; 52 | 53 | // Constant used in evaluation value calculation 54 | constexpr int OutputScale = 16; 55 | constexpr int WeightScaleBits = 6; 56 | 57 | // Size of cache line (in bytes) 58 | constexpr std::size_t CacheLineSize = 64; 59 | 60 | constexpr const char Leb128MagicString[] = "COMPRESSED_LEB128"; 61 | constexpr const std::size_t Leb128MagicStringSize = sizeof(Leb128MagicString) - 1; 62 | 63 | // SIMD width (in bytes) 64 | #if defined(USE_AVX2) 65 | constexpr std::size_t SimdWidth = 32; 66 | 67 | #elif defined(USE_SSE2) 68 | constexpr std::size_t SimdWidth = 16; 69 | 70 | #elif defined(USE_MMX) 71 | constexpr std::size_t SimdWidth = 8; 72 | 73 | #elif defined(USE_NEON) 74 | constexpr std::size_t SimdWidth = 16; 75 | #endif 76 | 77 | constexpr std::size_t MaxSimdWidth = 32; 78 | 79 | // Type of input feature after conversion 80 | using TransformedFeatureType = std::uint8_t; 81 | using IndexType = std::uint32_t; 82 | 83 | // Round n up to be a multiple of base 84 | template 85 | constexpr IntType ceil_to_multiple(IntType n, IntType base) { 86 | return (n + base - 1) / base * base; 87 | } 88 | 89 | 90 | // read_little_endian() is our utility to read an integer (signed or unsigned, any size) 91 | // from a stream in little-endian order. We swap the byte order after the read if 92 | // necessary to return a result with the byte ordering of the compiling machine. 93 | template 94 | inline IntType read_little_endian(std::istream& stream) { 95 | IntType result; 96 | 97 | if (IsLittleEndian) 98 | stream.read(reinterpret_cast(&result), sizeof(IntType)); 99 | else 100 | { 101 | std::uint8_t u[sizeof(IntType)]; 102 | typename std::make_unsigned::type v = 0; 103 | 104 | stream.read(reinterpret_cast(u), sizeof(IntType)); 105 | for (std::size_t i = 0; i < sizeof(IntType); ++i) 106 | v = (v << 8) | u[sizeof(IntType) - i - 1]; 107 | 108 | std::memcpy(&result, &v, sizeof(IntType)); 109 | } 110 | 111 | return result; 112 | } 113 | 114 | 115 | // write_little_endian() is our utility to write an integer (signed or unsigned, any size) 116 | // to a stream in little-endian order. We swap the byte order before the write if 117 | // necessary to always write in little endian order, independently of the byte 118 | // ordering of the compiling machine. 119 | template 120 | inline void write_little_endian(std::ostream& stream, IntType value) { 121 | 122 | if (IsLittleEndian) 123 | stream.write(reinterpret_cast(&value), sizeof(IntType)); 124 | else 125 | { 126 | std::uint8_t u[sizeof(IntType)]; 127 | typename std::make_unsigned::type v = value; 128 | 129 | std::size_t i = 0; 130 | // if constexpr to silence the warning about shift by 8 131 | if constexpr (sizeof(IntType) > 1) 132 | { 133 | for (; i + 1 < sizeof(IntType); ++i) 134 | { 135 | u[i] = (std::uint8_t)v; 136 | v >>= 8; 137 | } 138 | } 139 | u[i] = (std::uint8_t)v; 140 | 141 | stream.write(reinterpret_cast(u), sizeof(IntType)); 142 | } 143 | } 144 | 145 | 146 | // read_little_endian(s, out, N) : read integers in bulk from a little indian stream. 147 | // This reads N integers from stream s and put them in array out. 148 | template 149 | inline void read_little_endian(std::istream& stream, IntType* out, std::size_t count) { 150 | if (IsLittleEndian) 151 | stream.read(reinterpret_cast(out), sizeof(IntType) * count); 152 | else 153 | for (std::size_t i = 0; i < count; ++i) 154 | out[i] = read_little_endian(stream); 155 | } 156 | 157 | 158 | // write_little_endian(s, values, N) : write integers in bulk to a little indian stream. 159 | // This takes N integers from array values and writes them on stream s. 160 | template 161 | inline void write_little_endian(std::ostream& stream, const IntType* values, std::size_t count) { 162 | if (IsLittleEndian) 163 | stream.write(reinterpret_cast(values), sizeof(IntType) * count); 164 | else 165 | for (std::size_t i = 0; i < count; ++i) 166 | write_little_endian(stream, values[i]); 167 | } 168 | 169 | 170 | // read_leb_128(s, out, N) : read N signed integers from the stream s, putting them in 171 | // the array out. The stream is assumed to be compressed using the signed LEB128 format. 172 | // See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme. 173 | template 174 | inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count) { 175 | 176 | // Check the presence of our LEB128 magic string 177 | char leb128MagicString[Leb128MagicStringSize]; 178 | stream.read(leb128MagicString, Leb128MagicStringSize); 179 | assert(strncmp(Leb128MagicString, leb128MagicString, Leb128MagicStringSize) == 0); 180 | 181 | static_assert(std::is_signed_v, "Not implemented for unsigned types"); 182 | 183 | const std::uint32_t BUF_SIZE = 4096; 184 | std::uint8_t buf[BUF_SIZE]; 185 | 186 | auto bytes_left = read_little_endian(stream); 187 | 188 | std::uint32_t buf_pos = BUF_SIZE; 189 | for (std::size_t i = 0; i < count; ++i) 190 | { 191 | IntType result = 0; 192 | size_t shift = 0; 193 | do 194 | { 195 | if (buf_pos == BUF_SIZE) 196 | { 197 | stream.read(reinterpret_cast(buf), std::min(bytes_left, BUF_SIZE)); 198 | buf_pos = 0; 199 | } 200 | 201 | std::uint8_t byte = buf[buf_pos++]; 202 | --bytes_left; 203 | result |= (byte & 0x7f) << shift; 204 | shift += 7; 205 | 206 | if ((byte & 0x80) == 0) 207 | { 208 | out[i] = (sizeof(IntType) * 8 <= shift || (byte & 0x40) == 0) ? result 209 | : result | ~((1 << shift) - 1); 210 | break; 211 | } 212 | } 213 | while (shift < sizeof(IntType) * 8); 214 | } 215 | 216 | assert(bytes_left == 0); 217 | } 218 | 219 | 220 | // write_leb_128(s, values, N) : write signed integers to a stream with LEB128 compression. 221 | // This takes N integers from array values, compress them with the LEB128 algorithm and 222 | // writes the result on the stream s. 223 | // See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme. 224 | template 225 | inline void write_leb_128(std::ostream& stream, const IntType* values, std::size_t count) { 226 | 227 | // Write our LEB128 magic string 228 | stream.write(Leb128MagicString, Leb128MagicStringSize); 229 | 230 | static_assert(std::is_signed_v, "Not implemented for unsigned types"); 231 | 232 | std::uint32_t byte_count = 0; 233 | for (std::size_t i = 0; i < count; ++i) 234 | { 235 | IntType value = values[i]; 236 | std::uint8_t byte; 237 | do 238 | { 239 | byte = value & 0x7f; 240 | value >>= 7; 241 | ++byte_count; 242 | } 243 | while ((byte & 0x40) == 0 ? value != 0 : value != -1); 244 | } 245 | 246 | write_little_endian(stream, byte_count); 247 | 248 | const std::uint32_t BUF_SIZE = 4096; 249 | std::uint8_t buf[BUF_SIZE]; 250 | std::uint32_t buf_pos = 0; 251 | 252 | auto flush = [&]() { 253 | if (buf_pos > 0) 254 | { 255 | stream.write(reinterpret_cast(buf), buf_pos); 256 | buf_pos = 0; 257 | } 258 | }; 259 | 260 | auto write = [&](std::uint8_t byte) { 261 | buf[buf_pos++] = byte; 262 | if (buf_pos == BUF_SIZE) 263 | flush(); 264 | }; 265 | 266 | for (std::size_t i = 0; i < count; ++i) 267 | { 268 | IntType value = values[i]; 269 | while (true) 270 | { 271 | std::uint8_t byte = value & 0x7f; 272 | value >>= 7; 273 | if ((byte & 0x40) == 0 ? value == 0 : value == -1) 274 | { 275 | write(byte); 276 | break; 277 | } 278 | write(byte | 0x80); 279 | } 280 | } 281 | 282 | flush(); 283 | } 284 | 285 | } // namespace Stockfish::Eval::NNUE 286 | 287 | #endif // #ifndef NNUE_COMMON_H_INCLUDED 288 | -------------------------------------------------------------------------------- /src/pawns.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef PAWNS_H_INCLUDED 20 | #define PAWNS_H_INCLUDED 21 | 22 | #include "misc.h" 23 | #include "position.h" 24 | #include "types.h" 25 | 26 | namespace Stockfish::Pawns { 27 | 28 | /// Pawns::Entry contains various information about a pawn structure. A lookup 29 | /// to the pawn hash table (performed by calling the probe function) returns a 30 | /// pointer to an Entry object. 31 | 32 | struct Entry { 33 | 34 | Score pawn_score(Color c) const { return scores[c]; } 35 | Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } 36 | Bitboard passed_pawns(Color c) const { return passedPawns[c]; } 37 | Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } 38 | int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); } 39 | int blocked_count() const { return blockedCount; } 40 | 41 | template 42 | Score king_safety(const Position& pos) { 43 | return kingSquares[Us] == pos.square(Us) && castlingRights[Us] == pos.castling_rights(Us) 44 | ? kingSafety[Us] : (kingSafety[Us] = do_king_safety(pos)); 45 | } 46 | 47 | template 48 | Score do_king_safety(const Position& pos); 49 | 50 | template 51 | Score evaluate_shelter(const Position& pos, Square ksq) const; 52 | 53 | Key key; 54 | Score scores[COLOR_NB]; 55 | Bitboard passedPawns[COLOR_NB]; 56 | Bitboard pawnAttacks[COLOR_NB]; 57 | Bitboard pawnAttacksSpan[COLOR_NB]; 58 | Square kingSquares[COLOR_NB]; 59 | Score kingSafety[COLOR_NB]; 60 | int castlingRights[COLOR_NB]; 61 | int blockedCount; 62 | }; 63 | 64 | using Table = HashTable; 65 | 66 | Entry* probe(const Position& pos); 67 | 68 | } // namespace Stockfish::Pawns 69 | 70 | #endif // #ifndef PAWNS_H_INCLUDED 71 | -------------------------------------------------------------------------------- /src/psqt.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | 20 | #ifndef PSQT_H_INCLUDED 21 | #define PSQT_H_INCLUDED 22 | 23 | 24 | #include "types.h" 25 | 26 | 27 | namespace Stockfish::PSQT 28 | { 29 | 30 | #ifdef CRAZYHOUSE 31 | extern Score psq[VARIANT_NB][PIECE_NB][SQUARE_NB+1]; 32 | #else 33 | extern Score psq[VARIANT_NB][PIECE_NB][SQUARE_NB]; 34 | #endif 35 | 36 | // Fill psqt array from a set of internally linked parameters 37 | void init(); 38 | 39 | } // namespace Stockfish::PSQT 40 | 41 | 42 | #endif // PSQT_H_INCLUDED 43 | -------------------------------------------------------------------------------- /src/search.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef SEARCH_H_INCLUDED 20 | #define SEARCH_H_INCLUDED 21 | 22 | #include 23 | 24 | #include "misc.h" 25 | #include "movepick.h" 26 | #include "types.h" 27 | 28 | namespace Stockfish { 29 | 30 | class Position; 31 | 32 | namespace Search { 33 | 34 | 35 | /// Stack struct keeps track of the information we need to remember from nodes 36 | /// shallower and deeper in the tree during the search. Each search thread has 37 | /// its own array of Stack objects, indexed by the current ply. 38 | 39 | struct Stack { 40 | Move* pv; 41 | PieceToHistory* continuationHistory; 42 | int ply; 43 | Move currentMove; 44 | Move excludedMove; 45 | Move killers[2]; 46 | Value staticEval; 47 | int statScore; 48 | int moveCount; 49 | bool inCheck; 50 | bool ttPv; 51 | bool ttHit; 52 | int doubleExtensions; 53 | int cutoffCnt; 54 | }; 55 | 56 | 57 | /// RootMove struct is used for moves at the root of the tree. For each root move 58 | /// we store a score and a PV (really a refutation in the case of moves which 59 | /// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves. 60 | 61 | struct RootMove { 62 | 63 | explicit RootMove(Move m) : pv(1, m) {} 64 | bool extract_ponder_from_tt(Position& pos); 65 | bool operator==(const Move& m) const { return pv[0] == m; } 66 | bool operator<(const RootMove& m) const { // Sort in descending order 67 | return m.score != score ? m.score < score 68 | : m.previousScore < previousScore; 69 | } 70 | 71 | Value score = -VALUE_INFINITE; 72 | Value previousScore = -VALUE_INFINITE; 73 | Value averageScore = -VALUE_INFINITE; 74 | Value uciScore = -VALUE_INFINITE; 75 | bool scoreLowerbound = false; 76 | bool scoreUpperbound = false; 77 | int selDepth = 0; 78 | int tbRank = 0; 79 | Value tbScore; 80 | std::vector pv; 81 | }; 82 | 83 | using RootMoves = std::vector; 84 | 85 | 86 | /// LimitsType struct stores information sent by GUI about available time to 87 | /// search the current move, maximum depth/time, or if we are in analysis mode. 88 | 89 | struct LimitsType { 90 | 91 | LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC 92 | time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movetime = TimePoint(0); 93 | movestogo = depth = mate = perft = infinite = 0; 94 | nodes = 0; 95 | } 96 | 97 | bool use_time_management() const { 98 | return time[WHITE] || time[BLACK]; 99 | } 100 | 101 | std::vector searchmoves; 102 | TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime; 103 | int movestogo, depth, mate, perft, infinite; 104 | int64_t nodes; 105 | }; 106 | 107 | extern LimitsType Limits; 108 | 109 | void init(); 110 | void clear(); 111 | 112 | } // namespace Search 113 | 114 | } // namespace Stockfish 115 | 116 | #endif // #ifndef SEARCH_H_INCLUDED 117 | -------------------------------------------------------------------------------- /src/syzygy/tbprobe.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef TBPROBE_H 20 | #define TBPROBE_H 21 | 22 | #include 23 | 24 | #include "../search.h" 25 | 26 | namespace Stockfish::Tablebases { 27 | 28 | enum WDLScore { 29 | WDLLoss = -2, // Loss 30 | WDLBlessedLoss = -1, // Loss, but draw under 50-move rule 31 | WDLDraw = 0, // Draw 32 | WDLCursedWin = 1, // Win, but draw under 50-move rule 33 | WDLWin = 2, // Win 34 | }; 35 | 36 | // Possible states after a probing operation 37 | enum ProbeState { 38 | FAIL = 0, // Probe failed (missing file table) 39 | OK = 1, // Probe successful 40 | CHANGE_STM = -1, // DTZ should check the other side 41 | ZEROING_BEST_MOVE = 2, // Best move zeroes DTZ (capture or pawn move) 42 | THREAT = 3 // Threatening to force capture in giveaway 43 | }; 44 | 45 | extern int MaxCardinality; 46 | 47 | void init(Variant v, const std::string& paths); 48 | WDLScore probe_wdl(Position& pos, ProbeState* result); 49 | int probe_dtz(Position& pos, ProbeState* result); 50 | bool root_probe(Position& pos, Search::RootMoves& rootMoves); 51 | bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves); 52 | void rank_root_moves(Position& pos, Search::RootMoves& rootMoves); 53 | 54 | inline std::ostream& operator<<(std::ostream& os, const WDLScore v) { 55 | 56 | os << (v == WDLLoss ? "Loss" : 57 | v == WDLBlessedLoss ? "Blessed loss" : 58 | v == WDLDraw ? "Draw" : 59 | v == WDLCursedWin ? "Cursed win" : 60 | v == WDLWin ? "Win" : "None"); 61 | 62 | return os; 63 | } 64 | 65 | inline std::ostream& operator<<(std::ostream& os, const ProbeState v) { 66 | 67 | os << (v == FAIL ? "Failed" : 68 | v == OK ? "Success" : 69 | v == CHANGE_STM ? "Probed opponent side" : 70 | v == ZEROING_BEST_MOVE ? "Best move zeroes DTZ" : 71 | v == THREAT ? "Threat" : "None"); 72 | 73 | return os; 74 | } 75 | 76 | } // namespace Stockfish::Tablebases 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/thread.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include // For std::count 22 | #include "movegen.h" 23 | #include "search.h" 24 | #include "thread.h" 25 | #include "uci.h" 26 | #include "syzygy/tbprobe.h" 27 | #include "tt.h" 28 | 29 | namespace Stockfish { 30 | 31 | ThreadPool Threads; // Global object 32 | 33 | 34 | /// Thread constructor launches the thread and waits until it goes to sleep 35 | /// in idle_loop(). Note that 'searching' and 'exit' should be already set. 36 | 37 | Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) { 38 | 39 | wait_for_search_finished(); 40 | } 41 | 42 | 43 | /// Thread destructor wakes up the thread in idle_loop() and waits 44 | /// for its termination. Thread should be already waiting. 45 | 46 | Thread::~Thread() { 47 | 48 | assert(!searching); 49 | 50 | exit = true; 51 | start_searching(); 52 | stdThread.join(); 53 | } 54 | 55 | 56 | /// Thread::clear() reset histories, usually before a new game 57 | 58 | void Thread::clear() { 59 | 60 | counterMoves.fill(MOVE_NONE); 61 | mainHistory.fill(0); 62 | captureHistory.fill(0); 63 | 64 | for (bool inCheck : { false, true }) 65 | for (StatsType c : { NoCaptures, Captures }) 66 | for (auto& to : continuationHistory[inCheck][c]) 67 | for (auto& h : to) 68 | h->fill(-71); 69 | } 70 | 71 | 72 | /// Thread::start_searching() wakes up the thread that will start the search 73 | 74 | void Thread::start_searching() { 75 | mutex.lock(); 76 | searching = true; 77 | mutex.unlock(); // Unlock before notifying saves a few CPU-cycles 78 | cv.notify_one(); // Wake up the thread in idle_loop() 79 | } 80 | 81 | 82 | /// Thread::wait_for_search_finished() blocks on the condition variable 83 | /// until the thread has finished searching. 84 | 85 | void Thread::wait_for_search_finished() { 86 | 87 | std::unique_lock lk(mutex); 88 | cv.wait(lk, [&]{ return !searching; }); 89 | } 90 | 91 | 92 | /// Thread::idle_loop() is where the thread is parked, blocked on the 93 | /// condition variable, when it has no work to do. 94 | 95 | void Thread::idle_loop() { 96 | 97 | // If OS already scheduled us on a different group than 0 then don't overwrite 98 | // the choice, eventually we are one of many one-threaded processes running on 99 | // some Windows NUMA hardware, for instance in fishtest. To make it simple, 100 | // just check if running threads are below a threshold, in this case all this 101 | // NUMA machinery is not needed. 102 | if (Options["Threads"] > 8) 103 | WinProcGroup::bindThisThread(idx); 104 | 105 | while (true) 106 | { 107 | std::unique_lock lk(mutex); 108 | searching = false; 109 | cv.notify_one(); // Wake up anyone waiting for search finished 110 | cv.wait(lk, [&]{ return searching; }); 111 | 112 | if (exit) 113 | return; 114 | 115 | lk.unlock(); 116 | 117 | search(); 118 | } 119 | } 120 | 121 | /// ThreadPool::set() creates/destroys threads to match the requested number. 122 | /// Created and launched threads will immediately go to sleep in idle_loop. 123 | /// Upon resizing, threads are recreated to allow for binding if necessary. 124 | 125 | void ThreadPool::set(size_t requested) { 126 | 127 | if (threads.size() > 0) // destroy any existing thread(s) 128 | { 129 | main()->wait_for_search_finished(); 130 | 131 | while (threads.size() > 0) 132 | delete threads.back(), threads.pop_back(); 133 | } 134 | 135 | if (requested > 0) // create new thread(s) 136 | { 137 | threads.push_back(new MainThread(0)); 138 | 139 | while (threads.size() < requested) 140 | threads.push_back(new Thread(threads.size())); 141 | clear(); 142 | 143 | // Reallocate the hash with the new threadpool size 144 | TT.resize(size_t(Options["Hash"])); 145 | 146 | // Init thread number dependent search params. 147 | Search::init(); 148 | } 149 | } 150 | 151 | 152 | /// ThreadPool::clear() sets threadPool data to initial values 153 | 154 | void ThreadPool::clear() { 155 | 156 | for (Thread* th : threads) 157 | th->clear(); 158 | 159 | main()->callsCnt = 0; 160 | main()->bestPreviousScore = VALUE_INFINITE; 161 | main()->bestPreviousAverageScore = VALUE_INFINITE; 162 | main()->previousTimeReduction = 1.0; 163 | } 164 | 165 | 166 | /// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and 167 | /// returns immediately. Main thread will wake up other threads and start the search. 168 | 169 | void ThreadPool::start_thinking(Position& pos, StateListPtr& states, 170 | const Search::LimitsType& limits, bool ponderMode) { 171 | 172 | main()->wait_for_search_finished(); 173 | 174 | main()->stopOnPonderhit = stop = false; 175 | increaseDepth = true; 176 | main()->ponder = ponderMode; 177 | Search::Limits = limits; 178 | Search::RootMoves rootMoves; 179 | 180 | for (const auto& m : MoveList(pos)) 181 | if ( limits.searchmoves.empty() 182 | || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m)) 183 | rootMoves.emplace_back(m); 184 | 185 | if (!rootMoves.empty()) 186 | Tablebases::rank_root_moves(pos, rootMoves); 187 | 188 | // After ownership transfer 'states' becomes empty, so if we stop the search 189 | // and call 'go' again without setting a new position states.get() == nullptr. 190 | assert(states.get() || setupStates.get()); 191 | 192 | if (states.get()) 193 | setupStates = std::move(states); // Ownership transfer, states is now empty 194 | 195 | // We use Position::set() to set root position across threads. But there are 196 | // some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot 197 | // be deduced from a fen string, so set() clears them and they are set from 198 | // setupStates->back() later. The rootState is per thread, earlier states are shared 199 | // since they are read-only. 200 | for (Thread* th : threads) 201 | { 202 | th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0; 203 | th->rootDepth = th->completedDepth = 0; 204 | th->rootMoves = rootMoves; 205 | th->rootPos.set(pos.fen(), pos.is_chess960(), pos.subvariant(), &th->rootState, th); 206 | th->rootState = setupStates->back(); 207 | } 208 | 209 | main()->start_searching(); 210 | } 211 | 212 | Thread* ThreadPool::get_best_thread() const { 213 | 214 | Thread* bestThread = threads.front(); 215 | std::map votes; 216 | Value minScore = VALUE_NONE; 217 | 218 | // Find minimum score of all threads 219 | for (Thread* th: threads) 220 | minScore = std::min(minScore, th->rootMoves[0].score); 221 | 222 | // Vote according to score and depth, and select the best thread 223 | auto thread_value = [minScore](Thread* th) { 224 | return (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); 225 | }; 226 | 227 | for (Thread* th : threads) 228 | votes[th->rootMoves[0].pv[0]] += thread_value(th); 229 | 230 | for (Thread* th : threads) 231 | if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) 232 | { 233 | // Make sure we pick the shortest mate / TB conversion or stave off mate the longest 234 | if (th->rootMoves[0].score > bestThread->rootMoves[0].score) 235 | bestThread = th; 236 | } 237 | else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY 238 | || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY 239 | && ( votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]] 240 | || ( votes[th->rootMoves[0].pv[0]] == votes[bestThread->rootMoves[0].pv[0]] 241 | && thread_value(th) * int(th->rootMoves[0].pv.size() > 2) 242 | > thread_value(bestThread) * int(bestThread->rootMoves[0].pv.size() > 2))))) 243 | bestThread = th; 244 | 245 | return bestThread; 246 | } 247 | 248 | 249 | /// Start non-main threads 250 | 251 | void ThreadPool::start_searching() { 252 | 253 | for (Thread* th : threads) 254 | if (th != threads.front()) 255 | th->start_searching(); 256 | } 257 | 258 | 259 | /// Wait for non-main threads 260 | 261 | void ThreadPool::wait_for_search_finished() const { 262 | 263 | for (Thread* th : threads) 264 | if (th != threads.front()) 265 | th->wait_for_search_finished(); 266 | } 267 | 268 | } // namespace Stockfish 269 | -------------------------------------------------------------------------------- /src/thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef THREAD_H_INCLUDED 20 | #define THREAD_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "material.h" 29 | #include "movepick.h" 30 | #include "pawns.h" 31 | #include "position.h" 32 | #include "search.h" 33 | #include "thread_win32_osx.h" 34 | 35 | namespace Stockfish { 36 | 37 | /// Thread class keeps together all the thread-related stuff. We use 38 | /// per-thread pawn and material hash tables so that once we get a 39 | /// pointer to an entry its life time is unlimited and we don't have 40 | /// to care about someone changing the entry under our feet. 41 | 42 | class Thread { 43 | 44 | std::mutex mutex; 45 | std::condition_variable cv; 46 | size_t idx; 47 | bool exit = false, searching = true; // Set before starting std::thread 48 | NativeThread stdThread; 49 | 50 | public: 51 | explicit Thread(size_t); 52 | virtual ~Thread(); 53 | virtual void search(); 54 | void clear(); 55 | void idle_loop(); 56 | void start_searching(); 57 | void wait_for_search_finished(); 58 | size_t id() const { return idx; } 59 | 60 | Pawns::Table pawnsTable; 61 | Material::Table materialTable; 62 | size_t pvIdx, pvLast; 63 | std::atomic nodes, tbHits, bestMoveChanges; 64 | int selDepth, nmpMinPly; 65 | Value bestValue, optimism[COLOR_NB]; 66 | 67 | Position rootPos; 68 | StateInfo rootState; 69 | Search::RootMoves rootMoves; 70 | Depth rootDepth, completedDepth; 71 | Value rootDelta; 72 | CounterMoveHistory counterMoves; 73 | ButterflyHistory mainHistory; 74 | CapturePieceToHistory captureHistory; 75 | ContinuationHistory continuationHistory[2][2]; 76 | }; 77 | 78 | 79 | /// MainThread is a derived class specific for main thread 80 | 81 | struct MainThread : public Thread { 82 | 83 | using Thread::Thread; 84 | 85 | void search() override; 86 | void check_time(); 87 | 88 | double previousTimeReduction; 89 | Value bestPreviousScore; 90 | Value bestPreviousAverageScore; 91 | Value iterValue[4]; 92 | int callsCnt; 93 | bool stopOnPonderhit; 94 | std::atomic_bool ponder; 95 | }; 96 | 97 | 98 | /// ThreadPool struct handles all the threads-related stuff like init, starting, 99 | /// parking and, most importantly, launching a thread. All the access to threads 100 | /// is done through this class. 101 | 102 | struct ThreadPool { 103 | 104 | void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false); 105 | void clear(); 106 | void set(size_t); 107 | 108 | MainThread* main() const { return static_cast(threads.front()); } 109 | uint64_t nodes_searched() const { return accumulate(&Thread::nodes); } 110 | uint64_t tb_hits() const { return accumulate(&Thread::tbHits); } 111 | Thread* get_best_thread() const; 112 | void start_searching(); 113 | void wait_for_search_finished() const; 114 | 115 | std::atomic_bool stop, increaseDepth; 116 | 117 | auto cbegin() const noexcept { return threads.cbegin(); } 118 | auto begin() noexcept { return threads.begin(); } 119 | auto end() noexcept { return threads.end(); } 120 | auto cend() const noexcept { return threads.cend(); } 121 | auto size() const noexcept { return threads.size(); } 122 | auto empty() const noexcept { return threads.empty(); } 123 | 124 | private: 125 | StateListPtr setupStates; 126 | std::vector threads; 127 | 128 | uint64_t accumulate(std::atomic Thread::* member) const { 129 | 130 | uint64_t sum = 0; 131 | for (Thread* th : threads) 132 | sum += (th->*member).load(std::memory_order_relaxed); 133 | return sum; 134 | } 135 | }; 136 | 137 | extern ThreadPool Threads; 138 | 139 | } // namespace Stockfish 140 | 141 | #endif // #ifndef THREAD_H_INCLUDED 142 | -------------------------------------------------------------------------------- /src/thread_win32_osx.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef THREAD_WIN32_OSX_H_INCLUDED 20 | #define THREAD_WIN32_OSX_H_INCLUDED 21 | 22 | #include 23 | 24 | /// On OSX threads other than the main thread are created with a reduced stack 25 | /// size of 512KB by default, this is too low for deep searches, which require 26 | /// somewhat more than 1MB stack, so adjust it to TH_STACK_SIZE. 27 | /// The implementation calls pthread_create() with the stack size parameter 28 | /// equal to the linux 8MB default, on platforms that support it. 29 | 30 | #if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(USE_PTHREADS) 31 | 32 | #include 33 | 34 | namespace Stockfish { 35 | 36 | static const size_t TH_STACK_SIZE = 8 * 1024 * 1024; 37 | 38 | template > 39 | void* start_routine(void* ptr) 40 | { 41 | P* p = reinterpret_cast(ptr); 42 | (p->first->*(p->second))(); // Call member function pointer 43 | delete p; 44 | return nullptr; 45 | } 46 | 47 | class NativeThread { 48 | 49 | pthread_t thread; 50 | 51 | public: 52 | template> 53 | explicit NativeThread(void(T::*fun)(), T* obj) { 54 | pthread_attr_t attr_storage, *attr = &attr_storage; 55 | pthread_attr_init(attr); 56 | pthread_attr_setstacksize(attr, TH_STACK_SIZE); 57 | pthread_create(&thread, attr, start_routine, new P(obj, fun)); 58 | } 59 | void join() { pthread_join(thread, nullptr); } 60 | }; 61 | 62 | } // namespace Stockfish 63 | 64 | #else // Default case: use STL classes 65 | 66 | namespace Stockfish { 67 | 68 | using NativeThread = std::thread; 69 | 70 | } // namespace Stockfish 71 | 72 | #endif 73 | 74 | #endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED 75 | -------------------------------------------------------------------------------- /src/timeman.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "search.h" 24 | #include "timeman.h" 25 | #include "uci.h" 26 | 27 | namespace Stockfish { 28 | 29 | TimeManagement Time; // Our global time management object 30 | 31 | constexpr int MoveHorizon[VARIANT_NB] = { // Plan time management at most this many moves ahead 32 | 50, 33 | #ifdef ANTI 34 | 29, 35 | #endif 36 | #ifdef ATOMIC 37 | 29, 38 | #endif 39 | #ifdef CRAZYHOUSE 40 | 50, 41 | #endif 42 | #ifdef EXTINCTION 43 | 50, 44 | #endif 45 | #ifdef GRID 46 | 50, 47 | #endif 48 | #ifdef HORDE 49 | 50, 50 | #endif 51 | #ifdef KOTH 52 | 50, 53 | #endif 54 | #ifdef LOSERS 55 | 50, 56 | #endif 57 | #ifdef RACE 58 | 20, 59 | #endif 60 | #ifdef THREECHECK 61 | 50, 62 | #endif 63 | #ifdef TWOKINGS 64 | 50, 65 | #endif 66 | }; 67 | 68 | /// TimeManagement::init() is called at the beginning of the search and calculates 69 | /// the bounds of time allowed for the current game ply. We currently support: 70 | // 1) x basetime (+ z increment) 71 | // 2) x moves in y seconds (+ z increment) 72 | 73 | void TimeManagement::init(Variant var, Search::LimitsType& limits, Color us, int ply) { 74 | 75 | // if we have no time, no need to initialize TM, except for the start time, 76 | // which is used by movetime. 77 | startTime = limits.startTime; 78 | if (limits.time[us] == 0) 79 | return; 80 | 81 | TimePoint moveOverhead = TimePoint(Options["Move Overhead"]); 82 | TimePoint slowMover = TimePoint(Options["Slow Mover"]); 83 | TimePoint npmsec = TimePoint(Options["nodestime"]); 84 | 85 | // optScale is a percentage of available time to use for the current move. 86 | // maxScale is a multiplier applied to optimumTime. 87 | double optScale, maxScale; 88 | 89 | // If we have to play in 'nodes as time' mode, then convert from time 90 | // to nodes, and use resulting values in time management formulas. 91 | // WARNING: to avoid time losses, the given npmsec (nodes per millisecond) 92 | // must be much lower than the real engine speed. 93 | if (npmsec) 94 | { 95 | if (!availableNodes) // Only once at game start 96 | availableNodes = npmsec * limits.time[us]; // Time is in msec 97 | 98 | // Convert from milliseconds to nodes 99 | limits.time[us] = TimePoint(availableNodes); 100 | limits.inc[us] *= npmsec; 101 | limits.npmsec = npmsec; 102 | } 103 | 104 | // Maximum move horizon of 50 moves 105 | int mtg = limits.movestogo ? std::min(limits.movestogo, MoveHorizon[var]) : MoveHorizon[var]; 106 | 107 | // Make sure timeLeft is > 0 since we may use it as a divisor 108 | TimePoint timeLeft = std::max(TimePoint(1), 109 | limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg)); 110 | 111 | // Use extra time with larger increments 112 | double optExtra = std::clamp(1.0 + 12.0 * limits.inc[us] / limits.time[us], 1.0, 1.12); 113 | 114 | // A user may scale time usage by setting UCI option "Slow Mover" 115 | // Default is 100 and changing this value will probably lose elo. 116 | timeLeft = slowMover * timeLeft / 100; 117 | 118 | // x basetime (+ z increment) 119 | // If there is a healthy increment, timeLeft can exceed actual available 120 | // game time for the current move, so also cap to 20% of available game time. 121 | if (limits.movestogo == 0) 122 | { 123 | optScale = std::min(0.0120 + std::pow(ply + 3.0, 0.45) * 0.0039, 124 | 0.2 * limits.time[us] / double(timeLeft)) 125 | * optExtra; 126 | maxScale = std::min(7.0, 4.0 + ply / 12.0); 127 | } 128 | 129 | // x moves in y seconds (+ z increment) 130 | else 131 | { 132 | optScale = std::min((0.88 + ply / 116.4) / mtg, 133 | 0.88 * limits.time[us] / double(timeLeft)); 134 | maxScale = std::min(6.3, 1.5 + 0.11 * mtg); 135 | } 136 | 137 | // Never use more than 80% of the available time for this move 138 | optimumTime = TimePoint(optScale * timeLeft); 139 | maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime)); 140 | 141 | if (Options["Ponder"]) 142 | optimumTime += optimumTime / 4; 143 | } 144 | 145 | } // namespace Stockfish 146 | -------------------------------------------------------------------------------- /src/timeman.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef TIMEMAN_H_INCLUDED 20 | #define TIMEMAN_H_INCLUDED 21 | 22 | #include "misc.h" 23 | #include "search.h" 24 | #include "thread.h" 25 | 26 | namespace Stockfish { 27 | 28 | /// The TimeManagement class computes the optimal time to think depending on 29 | /// the maximum available time, the game move number and other parameters. 30 | 31 | class TimeManagement { 32 | public: 33 | void init(Variant var, Search::LimitsType& limits, Color us, int ply); 34 | TimePoint optimum() const { return optimumTime; } 35 | TimePoint maximum() const { return maximumTime; } 36 | TimePoint elapsed() const { return Search::Limits.npmsec ? 37 | TimePoint(Threads.nodes_searched()) : now() - startTime; } 38 | 39 | int64_t availableNodes; // When in 'nodes as time' mode 40 | 41 | private: 42 | TimePoint startTime; 43 | TimePoint optimumTime; 44 | TimePoint maximumTime; 45 | }; 46 | 47 | extern TimeManagement Time; 48 | 49 | } // namespace Stockfish 50 | 51 | #endif // #ifndef TIMEMAN_H_INCLUDED 52 | -------------------------------------------------------------------------------- /src/tt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include // For std::memset 20 | #include 21 | #include 22 | 23 | #include "bitboard.h" 24 | #include "misc.h" 25 | #include "thread.h" 26 | #include "tt.h" 27 | #include "uci.h" 28 | 29 | namespace Stockfish { 30 | 31 | TranspositionTable TT; // Our global transposition table 32 | 33 | /// TTEntry::save() populates the TTEntry with a new node's data, possibly 34 | /// overwriting an old position. Update is not atomic and can be racy. 35 | 36 | void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) { 37 | 38 | // Preserve any existing move for the same position 39 | if (m || (uint16_t)k != key16) 40 | move16 = (uint16_t)m; 41 | 42 | // Overwrite less valuable entries (cheapest checks first) 43 | if ( b == BOUND_EXACT 44 | || (uint16_t)k != key16 45 | || d - DEPTH_OFFSET + 2 * pv > depth8 - 4) 46 | { 47 | assert(d > DEPTH_OFFSET); 48 | assert(d < 256 + DEPTH_OFFSET); 49 | 50 | key16 = (uint16_t)k; 51 | depth8 = (uint8_t)(d - DEPTH_OFFSET); 52 | genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); 53 | value16 = (int16_t)v; 54 | eval16 = (int16_t)ev; 55 | } 56 | } 57 | 58 | 59 | /// TranspositionTable::resize() sets the size of the transposition table, 60 | /// measured in megabytes. Transposition table consists of a power of 2 number 61 | /// of clusters and each cluster consists of ClusterSize number of TTEntry. 62 | 63 | void TranspositionTable::resize(size_t mbSize) { 64 | 65 | Threads.main()->wait_for_search_finished(); 66 | 67 | aligned_large_pages_free(table); 68 | 69 | clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); 70 | 71 | table = static_cast(aligned_large_pages_alloc(clusterCount * sizeof(Cluster))); 72 | if (!table) 73 | { 74 | std::cerr << "Failed to allocate " << mbSize 75 | << "MB for transposition table." << std::endl; 76 | exit(EXIT_FAILURE); 77 | } 78 | 79 | clear(); 80 | } 81 | 82 | 83 | /// TranspositionTable::clear() initializes the entire transposition table to zero, 84 | // in a multi-threaded way. 85 | 86 | void TranspositionTable::clear() { 87 | 88 | std::vector threads; 89 | 90 | for (size_t idx = 0; idx < size_t(Options["Threads"]); ++idx) 91 | { 92 | threads.emplace_back([this, idx]() { 93 | 94 | // Thread binding gives faster search on systems with a first-touch policy 95 | if (Options["Threads"] > 8) 96 | WinProcGroup::bindThisThread(idx); 97 | 98 | // Each thread will zero its part of the hash table 99 | const size_t stride = size_t(clusterCount / Options["Threads"]), 100 | start = size_t(stride * idx), 101 | len = idx != size_t(Options["Threads"]) - 1 ? 102 | stride : clusterCount - start; 103 | 104 | std::memset(&table[start], 0, len * sizeof(Cluster)); 105 | }); 106 | } 107 | 108 | for (std::thread& th : threads) 109 | th.join(); 110 | } 111 | 112 | 113 | /// TranspositionTable::probe() looks up the current position in the transposition 114 | /// table. It returns true and a pointer to the TTEntry if the position is found. 115 | /// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry 116 | /// to be replaced later. The replace value of an entry is calculated as its depth 117 | /// minus 8 times its relative age. TTEntry t1 is considered more valuable than 118 | /// TTEntry t2 if its replace value is greater than that of t2. 119 | 120 | TTEntry* TranspositionTable::probe(const Key key, bool& found) const { 121 | 122 | TTEntry* const tte = first_entry(key); 123 | const uint16_t key16 = (uint16_t)key; // Use the low 16 bits as key inside the cluster 124 | 125 | for (int i = 0; i < ClusterSize; ++i) 126 | if (tte[i].key16 == key16 || !tte[i].depth8) 127 | { 128 | tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & (GENERATION_DELTA - 1))); // Refresh 129 | 130 | return found = (bool)tte[i].depth8, &tte[i]; 131 | } 132 | 133 | // Find an entry to be replaced according to the replacement strategy 134 | TTEntry* replace = tte; 135 | for (int i = 1; i < ClusterSize; ++i) 136 | // Due to our packed storage format for generation and its cyclic 137 | // nature we add GENERATION_CYCLE (256 is the modulus, plus what 138 | // is needed to keep the unrelated lowest n bits from affecting 139 | // the result) to calculate the entry age correctly even after 140 | // generation8 overflows into the next cycle. 141 | if ( replace->depth8 - ((GENERATION_CYCLE + generation8 - replace->genBound8) & GENERATION_MASK) 142 | > tte[i].depth8 - ((GENERATION_CYCLE + generation8 - tte[i].genBound8) & GENERATION_MASK)) 143 | replace = &tte[i]; 144 | 145 | return found = false, replace; 146 | } 147 | 148 | 149 | /// TranspositionTable::hashfull() returns an approximation of the hashtable 150 | /// occupation during a search. The hash is x permill full, as per UCI protocol. 151 | 152 | int TranspositionTable::hashfull() const { 153 | 154 | int cnt = 0; 155 | for (int i = 0; i < 1000; ++i) 156 | for (int j = 0; j < ClusterSize; ++j) 157 | cnt += table[i].entry[j].depth8 && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8; 158 | 159 | return cnt / ClusterSize; 160 | } 161 | 162 | } // namespace Stockfish 163 | -------------------------------------------------------------------------------- /src/tt.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef TT_H_INCLUDED 20 | #define TT_H_INCLUDED 21 | 22 | #include "misc.h" 23 | #include "types.h" 24 | 25 | namespace Stockfish { 26 | 27 | /// TTEntry struct is the 10 bytes transposition table entry, defined as below: 28 | /// 29 | /// key 16 bit 30 | /// depth 8 bit 31 | /// generation 5 bit 32 | /// pv node 1 bit 33 | /// bound type 2 bit 34 | /// move 16 bit 35 | /// value 16 bit 36 | /// eval value 16 bit 37 | 38 | struct TTEntry { 39 | 40 | Move move() const { return (Move )move16; } 41 | Value value() const { return (Value)value16; } 42 | Value eval() const { return (Value)eval16; } 43 | Depth depth() const { return (Depth)depth8 + DEPTH_OFFSET; } 44 | bool is_pv() const { return (bool)(genBound8 & 0x4); } 45 | Bound bound() const { return (Bound)(genBound8 & 0x3); } 46 | void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev); 47 | 48 | private: 49 | friend class TranspositionTable; 50 | 51 | uint16_t key16; 52 | uint8_t depth8; 53 | uint8_t genBound8; 54 | uint16_t move16; 55 | int16_t value16; 56 | int16_t eval16; 57 | }; 58 | 59 | 60 | /// A TranspositionTable is an array of Cluster, of size clusterCount. Each 61 | /// cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry 62 | /// contains information on exactly one position. The size of a Cluster should 63 | /// divide the size of a cache line for best performance, as the cacheline is 64 | /// prefetched when possible. 65 | 66 | class TranspositionTable { 67 | 68 | static constexpr int ClusterSize = 3; 69 | 70 | struct Cluster { 71 | TTEntry entry[ClusterSize]; 72 | char padding[2]; // Pad to 32 bytes 73 | }; 74 | 75 | static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size"); 76 | 77 | // Constants used to refresh the hash table periodically 78 | static constexpr unsigned GENERATION_BITS = 3; // nb of bits reserved for other things 79 | static constexpr int GENERATION_DELTA = (1 << GENERATION_BITS); // increment for generation field 80 | static constexpr int GENERATION_CYCLE = 255 + (1 << GENERATION_BITS); // cycle length 81 | static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF; // mask to pull out generation number 82 | 83 | public: 84 | ~TranspositionTable() { aligned_large_pages_free(table); } 85 | void new_search() { generation8 += GENERATION_DELTA; } // Lower bits are used for other things 86 | TTEntry* probe(const Key key, bool& found) const; 87 | int hashfull() const; 88 | void resize(size_t mbSize); 89 | void clear(); 90 | 91 | TTEntry* first_entry(const Key key) const { 92 | return &table[mul_hi64(key, clusterCount)].entry[0]; 93 | } 94 | 95 | private: 96 | friend struct TTEntry; 97 | 98 | size_t clusterCount; 99 | Cluster* table; 100 | uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 101 | }; 102 | 103 | extern TranspositionTable TT; 104 | 105 | } // namespace Stockfish 106 | 107 | #endif // #ifndef TT_H_INCLUDED 108 | -------------------------------------------------------------------------------- /src/tune.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "types.h" 24 | #include "misc.h" 25 | #include "uci.h" 26 | 27 | using std::string; 28 | 29 | namespace Stockfish { 30 | 31 | bool Tune::update_on_last; 32 | const UCI::Option* LastOption = nullptr; 33 | static std::map TuneResults; 34 | 35 | string Tune::next(string& names, bool pop) { 36 | 37 | string name; 38 | 39 | do { 40 | string token = names.substr(0, names.find(',')); 41 | 42 | if (pop) 43 | names.erase(0, token.size() + 1); 44 | 45 | std::stringstream ws(token); 46 | name += (ws >> token, token); // Remove trailing whitespace 47 | 48 | } while ( std::count(name.begin(), name.end(), '(') 49 | - std::count(name.begin(), name.end(), ')')); 50 | 51 | return name; 52 | } 53 | 54 | static void on_tune(const UCI::Option& o) { 55 | 56 | if (!Tune::update_on_last || LastOption == &o) 57 | Tune::read_options(); 58 | } 59 | 60 | static void make_option(const string& n, int v, const SetRange& r) { 61 | 62 | // Do not generate option when there is nothing to tune (ie. min = max) 63 | if (r(v).first == r(v).second) 64 | return; 65 | 66 | if (TuneResults.count(n)) 67 | v = TuneResults[n]; 68 | 69 | Options[n] << UCI::Option(v, r(v).first, r(v).second, on_tune); 70 | LastOption = &Options[n]; 71 | 72 | // Print formatted parameters, ready to be copy-pasted in Fishtest 73 | std::cout << n << "," 74 | << v << "," 75 | << r(v).first << "," << r(v).second << "," 76 | << (r(v).second - r(v).first) / 20.0 << "," 77 | << "0.0020" 78 | << std::endl; 79 | } 80 | 81 | template<> void Tune::Entry::init_option() { make_option(name, value, range); } 82 | 83 | template<> void Tune::Entry::read_option() { 84 | if (Options.count(name)) 85 | value = int(Options[name]); 86 | } 87 | 88 | template<> void Tune::Entry::init_option() { make_option(name, value, range); } 89 | 90 | template<> void Tune::Entry::read_option() { 91 | if (Options.count(name)) 92 | value = Value(int(Options[name])); 93 | } 94 | 95 | template<> void Tune::Entry::init_option() { 96 | make_option("m" + name, mg_value(value), range); 97 | make_option("e" + name, eg_value(value), range); 98 | } 99 | 100 | template<> void Tune::Entry::read_option() { 101 | if (Options.count("m" + name)) 102 | value = make_score(int(Options["m" + name]), eg_value(value)); 103 | 104 | if (Options.count("e" + name)) 105 | value = make_score(mg_value(value), int(Options["e" + name])); 106 | } 107 | 108 | // Instead of a variable here we have a PostUpdate function: just call it 109 | template<> void Tune::Entry::init_option() {} 110 | template<> void Tune::Entry::read_option() { value(); } 111 | 112 | } // namespace Stockfish 113 | 114 | 115 | // Init options with tuning session results instead of default values. Useful to 116 | // get correct bench signature after a tuning session or to test tuned values. 117 | // Just copy fishtest tuning results in a result.txt file and extract the 118 | // values with: 119 | // 120 | // cat results.txt | sed 's/^param: \([^,]*\), best: \([^,]*\).*/ TuneResults["\1"] = int(round(\2));/' 121 | // 122 | // Then paste the output below, as the function body 123 | 124 | #include 125 | 126 | namespace Stockfish { 127 | 128 | void Tune::read_results() { 129 | 130 | /* ...insert your values here... */ 131 | } 132 | 133 | } // namespace Stockfish 134 | -------------------------------------------------------------------------------- /src/tune.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef TUNE_H_INCLUDED 20 | #define TUNE_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace Stockfish { 28 | 29 | using Range = std::pair; // Option's min-max values 30 | using RangeFun = Range (int); 31 | 32 | // Default Range function, to calculate Option's min-max values 33 | inline Range default_range(int v) { 34 | return v > 0 ? Range(0, 2 * v) : Range(2 * v, 0); 35 | } 36 | 37 | struct SetRange { 38 | explicit SetRange(RangeFun f) : fun(f) {} 39 | SetRange(int min, int max) : fun(nullptr), range(min, max) {} 40 | Range operator()(int v) const { return fun ? fun(v) : range; } 41 | 42 | RangeFun* fun; 43 | Range range; 44 | }; 45 | 46 | #define SetDefaultRange SetRange(default_range) 47 | 48 | 49 | /// Tune class implements the 'magic' code that makes the setup of a fishtest 50 | /// tuning session as easy as it can be. Mainly you have just to remove const 51 | /// qualifiers from the variables you want to tune and flag them for tuning, so 52 | /// if you have: 53 | /// 54 | /// const Score myScore = S(10, 15); 55 | /// const Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } }; 56 | /// 57 | /// If you have a my_post_update() function to run after values have been updated, 58 | /// and a my_range() function to set custom Option's min-max values, then you just 59 | /// remove the 'const' qualifiers and write somewhere below in the file: 60 | /// 61 | /// TUNE(SetRange(my_range), myScore, myValue, my_post_update); 62 | /// 63 | /// You can also set the range directly, and restore the default at the end 64 | /// 65 | /// TUNE(SetRange(-100, 100), myScore, SetDefaultRange); 66 | /// 67 | /// In case update function is slow and you have many parameters, you can add: 68 | /// 69 | /// UPDATE_ON_LAST(); 70 | /// 71 | /// And the values update, including post update function call, will be done only 72 | /// once, after the engine receives the last UCI option, that is the one defined 73 | /// and created as the last one, so the GUI should send the options in the same 74 | /// order in which have been defined. 75 | 76 | class Tune { 77 | 78 | using PostUpdate = void (); // Post-update function 79 | 80 | Tune() { read_results(); } 81 | Tune(const Tune&) = delete; 82 | void operator=(const Tune&) = delete; 83 | void read_results(); 84 | 85 | static Tune& instance() { static Tune t; return t; } // Singleton 86 | 87 | // Use polymorphism to accommodate Entry of different types in the same vector 88 | struct EntryBase { 89 | virtual ~EntryBase() = default; 90 | virtual void init_option() = 0; 91 | virtual void read_option() = 0; 92 | }; 93 | 94 | template 95 | struct Entry : public EntryBase { 96 | 97 | static_assert(!std::is_const::value, "Parameter cannot be const!"); 98 | 99 | static_assert( std::is_same::value 100 | || std::is_same::value 101 | || std::is_same::value 102 | || std::is_same::value, "Parameter type not supported!"); 103 | 104 | Entry(const std::string& n, T& v, const SetRange& r) : name(n), value(v), range(r) {} 105 | void operator=(const Entry&) = delete; // Because 'value' is a reference 106 | void init_option() override; 107 | void read_option() override; 108 | 109 | std::string name; 110 | T& value; 111 | SetRange range; 112 | }; 113 | 114 | // Our facility to fill the container, each Entry corresponds to a parameter 115 | // to tune. We use variadic templates to deal with an unspecified number of 116 | // entries, each one of a possible different type. 117 | static std::string next(std::string& names, bool pop = true); 118 | 119 | int add(const SetRange&, std::string&&) { return 0; } 120 | 121 | template 122 | int add(const SetRange& range, std::string&& names, T& value, Args&&... args) { 123 | list.push_back(std::unique_ptr(new Entry(next(names), value, range))); 124 | return add(range, std::move(names), args...); 125 | } 126 | 127 | // Template specialization for arrays: recursively handle multi-dimensional arrays 128 | template 129 | int add(const SetRange& range, std::string&& names, T (&value)[N], Args&&... args) { 130 | for (size_t i = 0; i < N; i++) 131 | add(range, next(names, i == N - 1) + "[" + std::to_string(i) + "]", value[i]); 132 | return add(range, std::move(names), args...); 133 | } 134 | 135 | // Template specialization for SetRange 136 | template 137 | int add(const SetRange&, std::string&& names, SetRange& value, Args&&... args) { 138 | return add(value, (next(names), std::move(names)), args...); 139 | } 140 | 141 | std::vector> list; 142 | 143 | public: 144 | template 145 | static int add(const std::string& names, Args&&... args) { 146 | return instance().add(SetDefaultRange, names.substr(1, names.size() - 2), args...); // Remove trailing parenthesis 147 | } 148 | static void init() { for (auto& e : instance().list) e->init_option(); read_options(); } // Deferred, due to UCI::Options access 149 | static void read_options() { for (auto& e : instance().list) e->read_option(); } 150 | static bool update_on_last; 151 | }; 152 | 153 | // Some macro magic :-) we define a dummy int variable that compiler initializes calling Tune::add() 154 | #define STRINGIFY(x) #x 155 | #define UNIQUE2(x, y) x ## y 156 | #define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__ 157 | #define TUNE(...) int UNIQUE(p, __LINE__) = Tune::add(STRINGIFY((__VA_ARGS__)), __VA_ARGS__) 158 | 159 | #define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true 160 | 161 | } // namespace Stockfish 162 | 163 | #endif // #ifndef TUNE_H_INCLUDED 164 | -------------------------------------------------------------------------------- /src/uci.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef UCI_H_INCLUDED 20 | #define UCI_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "types.h" 27 | 28 | namespace Stockfish { 29 | 30 | class Position; 31 | 32 | namespace UCI { 33 | 34 | // Normalizes the internal value as reported by evaluate or search 35 | // to the UCI centipawn result used in output. This value is derived from 36 | // the win_rate_model() such that Stockfish outputs an advantage of 37 | // "100 centipawns" for a position if the engine has a 50% probability to win 38 | // from this position in selfplay at fishtest LTC time control. 39 | const int NormalizeToPawnValue = 328; 40 | 41 | class Option; 42 | 43 | /// Define a custom comparator, because the UCI options should be case-insensitive 44 | struct CaseInsensitiveLess { 45 | bool operator() (const std::string&, const std::string&) const; 46 | }; 47 | 48 | /// The options container is defined as a std::map 49 | using OptionsMap = std::map; 50 | 51 | /// The Option class implements each option as specified by the UCI protocol 52 | class Option { 53 | 54 | using OnChange = void (*)(const Option&); 55 | 56 | public: 57 | Option(OnChange = nullptr); 58 | Option(bool v, OnChange = nullptr); 59 | Option(const char* v, OnChange = nullptr); 60 | Option(const char* v, const std::vector& variants, OnChange = nullptr); 61 | Option(double v, int minv, int maxv, OnChange = nullptr); 62 | Option(const char* v, const char* cur, OnChange = nullptr); 63 | 64 | Option& operator=(const std::string&); 65 | void operator<<(const Option&); 66 | operator int() const; 67 | operator std::string() const; 68 | bool operator==(const char*) const; 69 | 70 | private: 71 | friend std::ostream& operator<<(std::ostream&, const OptionsMap&); 72 | 73 | std::string defaultValue, currentValue, type; 74 | int min, max; 75 | std::vector comboValues; 76 | size_t idx; 77 | OnChange on_change; 78 | }; 79 | 80 | void init(OptionsMap&); 81 | void loop(int argc, char* argv[]); 82 | std::string value(Value v); 83 | std::string square(Square s); 84 | std::string move(Move m, bool chess960); 85 | std::string pv(const Position& pos, Depth depth); 86 | std::string wdl(Value v, int ply); 87 | Move to_move(const Position& pos, std::string& str); 88 | Variant variant_from_name(const std::string& str); 89 | 90 | } // namespace UCI 91 | 92 | extern UCI::OptionsMap Options; 93 | 94 | } // namespace Stockfish 95 | 96 | #endif // #ifndef UCI_H_INCLUDED 97 | -------------------------------------------------------------------------------- /src/ucioption.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "evaluate.h" 25 | #include "misc.h" 26 | #include "search.h" 27 | #include "thread.h" 28 | #include "tt.h" 29 | #include "uci.h" 30 | #include "syzygy/tbprobe.h" 31 | 32 | using std::string; 33 | 34 | namespace Stockfish { 35 | 36 | UCI::OptionsMap Options; // Global object 37 | 38 | namespace UCI { 39 | 40 | /// 'On change' actions, triggered by an option's value change 41 | static void on_clear_hash(const Option&) { Search::clear(); } 42 | static void on_hash_size(const Option& o) { TT.resize(size_t(o)); } 43 | static void on_logger(const Option& o) { start_logger(o); } 44 | static void on_threads(const Option& o) { Threads.set(size_t(o)); } 45 | static void on_tb_path(const Option& o) { Tablebases::init(UCI::variant_from_name(Options["UCI_Variant"]), o); } 46 | #ifdef USE_NNUE 47 | static void on_use_NNUE(const Option&) { Eval::NNUE::init(); } 48 | static void on_eval_file(const Option&) { Eval::NNUE::init(); } 49 | #endif 50 | 51 | /// Our case insensitive less() function as required by UCI protocol 52 | bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { 53 | 54 | return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), 55 | [](char c1, char c2) { return tolower(c1) < tolower(c2); }); 56 | } 57 | 58 | 59 | /// UCI::init() initializes the UCI options to their hard-coded default values 60 | 61 | void init(OptionsMap& o) { 62 | 63 | constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; 64 | 65 | o["Debug Log File"] << Option("", on_logger); 66 | o["Threads"] << Option(1, 1, 1024, on_threads); 67 | o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); 68 | o["Clear Hash"] << Option(on_clear_hash); 69 | o["Ponder"] << Option(false); 70 | o["MultiPV"] << Option(1, 1, 500); 71 | o["Skill Level"] << Option(20, -20, 20); 72 | o["Move Overhead"] << Option(10, 0, 5000); 73 | o["Slow Mover"] << Option(100, 10, 1000); 74 | o["nodestime"] << Option(0, 0, 10000); 75 | o["UCI_Chess960"] << Option(false); 76 | o["UCI_Variant"] << Option(variants.front().c_str(), variants); 77 | o["UCI_AnalyseMode"] << Option(false); 78 | o["UCI_LimitStrength"] << Option(false); 79 | o["UCI_Elo"] << Option(1320, 1320, 3190); 80 | o["UCI_ShowWDL"] << Option(false); 81 | o["SyzygyPath"] << Option("", on_tb_path); 82 | o["SyzygyProbeDepth"] << Option(1, 1, 100); 83 | o["Syzygy50MoveRule"] << Option(true); 84 | o["SyzygyProbeLimit"] << Option(7, 0, 7); 85 | #ifdef USE_NNUE 86 | o["Use NNUE"] << Option(true, on_use_NNUE); 87 | o["EvalFile"] << Option(EvalFileDefaultName, on_eval_file); 88 | #endif 89 | } 90 | 91 | 92 | /// operator<<() is used to print all the options default values in chronological 93 | /// insertion order (the idx field) and in the format defined by the UCI protocol. 94 | 95 | std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { 96 | 97 | for (size_t idx = 0; idx < om.size(); ++idx) 98 | for (const auto& it : om) 99 | if (it.second.idx == idx) 100 | { 101 | const Option& o = it.second; 102 | os << "\noption name " << it.first << " type " << o.type; 103 | 104 | if (o.type == "string" || o.type == "check" || o.type == "combo") 105 | os << " default " << o.defaultValue; 106 | 107 | if (o.type == "combo") 108 | for (string value : o.comboValues) 109 | os << " var " << value; 110 | 111 | if (o.type == "spin") 112 | os << " default " << int(stof(o.defaultValue)) 113 | << " min " << o.min 114 | << " max " << o.max; 115 | 116 | break; 117 | } 118 | 119 | return os; 120 | } 121 | 122 | 123 | /// Option class constructors and conversion operators 124 | 125 | Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f) 126 | { defaultValue = currentValue = v; } 127 | 128 | Option::Option(const char* v, const std::vector& values, OnChange f) : type("combo"), min(0), max(0), comboValues(values), on_change(f) 129 | { defaultValue = currentValue = v; } 130 | 131 | Option::Option(bool v, OnChange f) : type("check"), min(0), max(0), on_change(f) 132 | { defaultValue = currentValue = (v ? "true" : "false"); } 133 | 134 | Option::Option(OnChange f) : type("button"), min(0), max(0), on_change(f) 135 | {} 136 | 137 | Option::Option(double v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), on_change(f) 138 | { defaultValue = currentValue = std::to_string(v); } 139 | 140 | Option::Option(const char* v, const char* cur, OnChange f) : type("combo"), min(0), max(0), on_change(f) 141 | { defaultValue = v; currentValue = cur; } 142 | 143 | Option::operator int() const { 144 | assert(type == "check" || type == "spin"); 145 | return (type == "spin" ? std::stoi(currentValue) : currentValue == "true"); 146 | } 147 | 148 | Option::operator std::string() const { 149 | assert(type == "string" || type == "combo"); 150 | return currentValue; 151 | } 152 | 153 | bool Option::operator==(const char* s) const { 154 | assert(type == "combo"); 155 | return !CaseInsensitiveLess()(currentValue, s) 156 | && !CaseInsensitiveLess()(s, currentValue); 157 | } 158 | 159 | 160 | /// operator<<() inits options and assigns idx in the correct printing order 161 | 162 | void Option::operator<<(const Option& o) { 163 | 164 | static size_t insert_order = 0; 165 | 166 | *this = o; 167 | idx = insert_order++; 168 | } 169 | 170 | 171 | /// operator=() updates currentValue and triggers on_change() action. It's up to 172 | /// the GUI to check for option's limits, but we could receive the new value 173 | /// from the user by console window, so let's check the bounds anyway. 174 | 175 | Option& Option::operator=(const string& v) { 176 | 177 | assert(!type.empty()); 178 | 179 | if ( (type != "button" && type != "string" && v.empty()) 180 | || (type == "check" && v != "true" && v != "false") 181 | || (type == "spin" && (stof(v) < min || stof(v) > max))) 182 | return *this; 183 | 184 | if (type == "combo") 185 | { 186 | OptionsMap comboMap; // To have case insensitive compare 187 | string token; 188 | std::istringstream ss(defaultValue); 189 | while (ss >> token) 190 | comboMap[token] << Option(); 191 | for (auto &value : comboValues) 192 | comboMap[value] << Option(); 193 | if (!comboMap.count(v) || v == "var") 194 | return *this; 195 | } 196 | 197 | if (type != "button") 198 | currentValue = v; 199 | 200 | if (on_change) 201 | on_change(*this); 202 | 203 | return *this; 204 | } 205 | 206 | } // namespace UCI 207 | 208 | } // namespace Stockfish 209 | -------------------------------------------------------------------------------- /tests/instrumented.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # check for errors under valgrind or sanitizers. 3 | 4 | error() 5 | { 6 | echo "instrumented testing failed on line $1" 7 | exit 1 8 | } 9 | trap 'error ${LINENO}' ERR 10 | 11 | # define suitable post and prefixes for testing options 12 | case $1 in 13 | --valgrind) 14 | echo "valgrind testing started" 15 | prefix='' 16 | exeprefix='valgrind --error-exitcode=42 --errors-for-leak-kinds=all --leak-check=full' 17 | postfix='1>/dev/null' 18 | threads="1" 19 | ;; 20 | --valgrind-thread) 21 | echo "valgrind-thread testing started" 22 | prefix='' 23 | exeprefix='valgrind --fair-sched=try --error-exitcode=42' 24 | postfix='1>/dev/null' 25 | threads="2" 26 | ;; 27 | --sanitizer-undefined) 28 | echo "sanitizer-undefined testing started" 29 | prefix='!' 30 | exeprefix='' 31 | postfix='2>&1 | grep -A50 "runtime error:"' 32 | threads="1" 33 | ;; 34 | --sanitizer-thread) 35 | echo "sanitizer-thread testing started" 36 | prefix='!' 37 | exeprefix='' 38 | postfix='2>&1 | grep -A50 "WARNING: ThreadSanitizer:"' 39 | threads="2" 40 | 41 | cat << EOF > tsan.supp 42 | race:Stockfish::TTEntry::move 43 | race:Stockfish::TTEntry::depth 44 | race:Stockfish::TTEntry::bound 45 | race:Stockfish::TTEntry::save 46 | race:Stockfish::TTEntry::value 47 | race:Stockfish::TTEntry::eval 48 | race:Stockfish::TTEntry::is_pv 49 | 50 | race:Stockfish::TranspositionTable::probe 51 | race:Stockfish::TranspositionTable::hashfull 52 | 53 | EOF 54 | 55 | export TSAN_OPTIONS="suppressions=./tsan.supp" 56 | 57 | ;; 58 | *) 59 | echo "unknown testing started" 60 | prefix='' 61 | exeprefix='' 62 | postfix='' 63 | threads="1" 64 | ;; 65 | esac 66 | 67 | # simple command line testing 68 | for args in "eval" \ 69 | "go nodes 1000" \ 70 | "go depth 10" \ 71 | "go movetime 1000" \ 72 | "go wtime 8000 btime 8000 winc 500 binc 500" \ 73 | "bench 128 $threads 8 default depth" \ 74 | "export_net verify.nnue" 75 | do 76 | 77 | echo "$prefix $exeprefix ./stockfish $args $postfix" 78 | eval "$prefix $exeprefix ./stockfish $args $postfix" 79 | 80 | done 81 | 82 | # verify the generated net equals the base net 83 | network=`./stockfish uci | grep 'option name EvalFile type string default' | awk '{print $NF}'` 84 | echo "Comparing $network to the written verify.nnue" 85 | diff $network verify.nnue 86 | 87 | # more general testing, following an uci protocol exchange 88 | cat << EOF > game.exp 89 | set timeout 240 90 | spawn $exeprefix ./stockfish 91 | 92 | send "uci\n" 93 | expect "uciok" 94 | 95 | send "setoption name Threads value $threads\n" 96 | 97 | send "ucinewgame\n" 98 | send "position startpos\n" 99 | send "go nodes 1000\n" 100 | expect "bestmove" 101 | 102 | send "position startpos moves e2e4 e7e6\n" 103 | send "go nodes 1000\n" 104 | expect "bestmove" 105 | 106 | send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n" 107 | send "go depth 10\n" 108 | expect "bestmove" 109 | 110 | send "quit\n" 111 | expect eof 112 | 113 | # return error code of the spawned program, useful for valgrind 114 | lassign [wait] pid spawnid os_error_flag value 115 | exit \$value 116 | EOF 117 | 118 | #download TB as needed 119 | if [ ! -d ../tests/syzygy ]; then 120 | curl -sL https://api.github.com/repos/niklasf/python-chess/tarball/9b9aa13f9f36d08aadfabff872882f4ab1494e95 | tar -xzf - 121 | mv niklasf-python-chess-9b9aa13 ../tests/syzygy 122 | fi 123 | 124 | cat << EOF > syzygy.exp 125 | set timeout 240 126 | spawn $exeprefix ./stockfish 127 | send "uci\n" 128 | send "setoption name SyzygyPath value ../tests/syzygy/\n" 129 | expect "info string Found 35 tablebases" {} timeout {exit 1} 130 | send "bench 128 1 8 default depth\n" 131 | send "quit\n" 132 | expect eof 133 | 134 | # return error code of the spawned program, useful for valgrind 135 | lassign [wait] pid spawnid os_error_flag value 136 | exit \$value 137 | EOF 138 | 139 | for exp in game.exp syzygy.exp 140 | do 141 | 142 | echo "$prefix expect $exp $postfix" 143 | eval "$prefix expect $exp $postfix" 144 | 145 | rm $exp 146 | 147 | done 148 | 149 | rm -f tsan.supp 150 | 151 | echo "instrumented testing OK" 152 | -------------------------------------------------------------------------------- /tests/perft.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # verify perft numbers (positions from www.chessprogramming.org/Perft_Results) 3 | 4 | error() 5 | { 6 | echo "perft testing failed on line $1" 7 | exit 1 8 | } 9 | trap 'error ${LINENO}' ERR 10 | 11 | echo "perft testing started" 12 | 13 | cat << EOF > perft.exp 14 | set timeout 10 15 | lassign \$argv variant pos depth result 16 | spawn ./stockfish 17 | send "setoption name UCI_Variant value \$variant\\n" 18 | send "position \$pos\\ngo perft \$depth\\n" 19 | expect "Nodes searched? \$result" {} timeout {exit 1} 20 | send "quit\\n" 21 | expect eof 22 | EOF 23 | 24 | # chess 25 | if [[ $1 == "" || $1 == "chess" ]]; then 26 | expect perft.exp chess startpos 5 4865609 > /dev/null 27 | expect perft.exp chess "fen r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -" 5 193690690 > /dev/null 28 | expect perft.exp chess "fen 8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -" 6 11030083 > /dev/null 29 | expect perft.exp chess "fen r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1" 5 15833292 > /dev/null 30 | expect perft.exp chess "fen rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8" 5 89941194 > /dev/null 31 | expect perft.exp chess "fen r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10" 5 164075551 > /dev/null 32 | fi 33 | 34 | # variants 35 | if [[ $1 == "" || $1 == "variant" ]]; then 36 | expect perft.exp 3check startpos 5 4865609 > /dev/null 37 | expect perft.exp antichess startpos 5 2732672 > /dev/null 38 | expect perft.exp atomic startpos 5 4864979 > /dev/null 39 | expect perft.exp crazyhouse startpos 5 4888832 > /dev/null 40 | expect perft.exp horde startpos 6 5396554 > /dev/null 41 | expect perft.exp kingofthehill startpos 5 4865609 > /dev/null 42 | expect perft.exp racingkings startpos 5 9472927 > /dev/null 43 | fi 44 | 45 | rm perft.exp 46 | 47 | echo "perft testing OK" 48 | -------------------------------------------------------------------------------- /tests/puzzle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # evaluate performance in test positions 3 | 4 | error() 5 | { 6 | echo "puzzle testing failed on line $1" 7 | exit 1 8 | } 9 | trap 'error ${LINENO}' ERR 10 | 11 | echo "puzzle testing started" 12 | 13 | expect << EOF 14 | set timeout 600 15 | spawn python3 ../tests/chess-artist/chess_artist.py --infile ../tests/chess-artist/EPD/wacnew.epd --outfile wacnew_result.txt --enginefile ./stockfish --eval static --job test 16 | expect -re "Correct percentage : (100|9\\\\d)" 17 | expect eof 18 | EOF 19 | 20 | rm wacnew_result.txt 21 | 22 | echo "puzzle testing OK" 23 | -------------------------------------------------------------------------------- /tests/reprosearch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # verify reproducible search 3 | 4 | error() 5 | { 6 | echo "reprosearch testing failed on line $1" 7 | exit 1 8 | } 9 | trap 'error ${LINENO}' ERR 10 | 11 | echo "reprosearch testing started" 12 | 13 | # repeat two short games, separated by ucinewgame. 14 | # with go nodes $nodes they should result in exactly 15 | # the same node count for each iteration. 16 | cat << EOF > repeat.exp 17 | set timeout 10 18 | spawn ./stockfish 19 | lassign \$argv nodes 20 | 21 | send "uci\n" 22 | expect "uciok" 23 | 24 | send "ucinewgame\n" 25 | send "position startpos\n" 26 | send "go nodes \$nodes\n" 27 | expect "bestmove" 28 | 29 | send "position startpos moves e2e4 e7e6\n" 30 | send "go nodes \$nodes\n" 31 | expect "bestmove" 32 | 33 | send "ucinewgame\n" 34 | send "position startpos\n" 35 | send "go nodes \$nodes\n" 36 | expect "bestmove" 37 | 38 | send "position startpos moves e2e4 e7e6\n" 39 | send "go nodes \$nodes\n" 40 | expect "bestmove" 41 | 42 | send "quit\n" 43 | expect eof 44 | EOF 45 | 46 | # to increase the likelihood of finding a non-reproducible case, 47 | # the allowed number of nodes are varied systematically 48 | for i in `seq 1 20` 49 | do 50 | 51 | nodes=$((100*3**i/2**i)) 52 | echo "reprosearch testing with $nodes nodes" 53 | 54 | # each line should appear exactly an even number of times 55 | expect repeat.exp $nodes 2>&1 | grep -o "nodes [0-9]*" | sort | uniq -c | awk '{if ($1%2!=0) exit(1)}' 56 | 57 | done 58 | 59 | rm repeat.exp 60 | 61 | echo "reprosearch testing OK" 62 | -------------------------------------------------------------------------------- /tests/signature.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # obtain and optionally verify Bench / signature 3 | # if no reference is given, the output is deliberately limited to just the signature 4 | 5 | error() 6 | { 7 | echo "running bench for signature failed on line $1" 8 | exit 1 9 | } 10 | trap 'error ${LINENO}' ERR 11 | 12 | # obtain 13 | 14 | signature=`./stockfish bench all &>/dev/null && ./stockfish bench 2>&1 | grep "Nodes searched : " | awk '{print $4}'` 15 | 16 | if [ $# -gt 0 ]; then 17 | # compare to given reference 18 | if [ "$1" != "$signature" ]; then 19 | if [ -z "$signature" ]; then 20 | echo "No signature obtained from bench. Code crashed or assert triggered ?" 21 | else 22 | echo "signature mismatch: reference $1 obtained: $signature ." 23 | fi 24 | exit 1 25 | else 26 | echo "signature OK: $signature" 27 | fi 28 | else 29 | # just report signature 30 | echo $signature 31 | fi 32 | --------------------------------------------------------------------------------