├── .clang-format ├── .git-blame-ignore-revs ├── .github ├── ISSUE_TEMPLATE │ ├── BUG-REPORT.yml │ └── config.yml ├── ci │ ├── arm_matrix.json │ ├── libcxx17.imp │ └── matrix.json └── workflows │ ├── arm_compilation.yml │ ├── clang-format.yml │ ├── codeql.yml │ ├── compilation.yml │ ├── games.yml │ ├── iwyu.yml │ ├── matetrack.yml │ ├── sanitizers.yml │ ├── stockfish.yml │ ├── tests.yml │ └── upload_binaries.yml ├── .gitignore ├── AUTHORS ├── CITATION.cff ├── CONTRIBUTING.md ├── Copying.txt ├── README.md ├── Top CPU Contributors.txt ├── scripts ├── .gitattributes ├── get_native_properties.sh └── net.sh ├── src ├── Makefile ├── benchmark.cpp ├── benchmark.h ├── bitboard.cpp ├── bitboard.h ├── engine.cpp ├── engine.h ├── evaluate.cpp ├── evaluate.h ├── history.h ├── incbin │ ├── UNLICENCE │ └── incbin.h ├── main.cpp ├── memory.cpp ├── memory.h ├── misc.cpp ├── misc.h ├── movegen.cpp ├── movegen.h ├── movepick.cpp ├── movepick.h ├── nnue │ ├── 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 │ ├── network.cpp │ ├── network.h │ ├── nnue_accumulator.cpp │ ├── nnue_accumulator.h │ ├── nnue_architecture.h │ ├── nnue_common.h │ ├── nnue_feature_transformer.h │ ├── nnue_misc.cpp │ └── nnue_misc.h ├── numa.h ├── perft.h ├── position.cpp ├── position.h ├── score.cpp ├── score.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 └── ucioption.h └── tests ├── .gitattributes ├── instrumented.py ├── perft.sh ├── reprosearch.sh ├── signature.sh └── testing.py /.clang-format: -------------------------------------------------------------------------------- 1 | AccessModifierOffset: -1 2 | AlignAfterOpenBracket: Align 3 | AlignConsecutiveAssignments: Consecutive 4 | AlignConsecutiveDeclarations: Consecutive 5 | AlignEscapedNewlines: DontAlign 6 | AlignOperands: AlignAfterOperator 7 | AlignTrailingComments: true 8 | AllowAllParametersOfDeclarationOnNextLine: true 9 | AllowShortCaseLabelsOnASingleLine: false 10 | AllowShortEnumsOnASingleLine: false 11 | AllowShortIfStatementsOnASingleLine: false 12 | AlwaysBreakTemplateDeclarations: Yes 13 | BasedOnStyle: WebKit 14 | BitFieldColonSpacing: After 15 | BinPackParameters: false 16 | BreakBeforeBinaryOperators: NonAssignment 17 | BreakBeforeBraces: Custom 18 | BraceWrapping: 19 | AfterFunction: false 20 | AfterClass: false 21 | AfterControlStatement: true 22 | BeforeElse: true 23 | BreakBeforeTernaryOperators: true 24 | BreakConstructorInitializers: AfterColon 25 | BreakStringLiterals: false 26 | ColumnLimit: 100 27 | ContinuationIndentWidth: 2 28 | Cpp11BracedListStyle: true 29 | IndentGotoLabels: false 30 | IndentPPDirectives: BeforeHash 31 | IndentWidth: 4 32 | MaxEmptyLinesToKeep: 2 33 | NamespaceIndentation: None 34 | PackConstructorInitializers: Never 35 | ReflowComments: false 36 | SortIncludes: false 37 | SortUsingDeclarations: false 38 | SpaceAfterCStyleCast: true 39 | SpaceAfterTemplateKeyword: false 40 | SpaceBeforeCaseColon: true 41 | SpaceBeforeCpp11BracedList: false 42 | SpaceBeforeInheritanceColon: false 43 | SpaceInEmptyBlock: false 44 | SpacesBeforeTrailingComments: 2 45 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # .git-blame-ignore-revs 2 | # Ignore commit which added clang-format 3 | 2d0237db3f0e596fb06e3ffbadba84dcc4e018f6 4 | 5 | # Post commit formatting fixes 6 | 0fca5605fa2e5e7240fde5e1aae50952b2612231 7 | 08ed4c90db31959521b7ef3186c026edd1e90307 -------------------------------------------------------------------------------- /.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 on 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/ci/arm_matrix.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": [ 3 | { 4 | "name": "Android NDK aarch64", 5 | "os": "ubuntu-22.04", 6 | "simple_name": "android", 7 | "compiler": "aarch64-linux-android21-clang++", 8 | "emu": "qemu-aarch64", 9 | "comp": "ndk", 10 | "shell": "bash", 11 | "archive_ext": "tar" 12 | }, 13 | { 14 | "name": "Android NDK arm", 15 | "os": "ubuntu-22.04", 16 | "simple_name": "android", 17 | "compiler": "armv7a-linux-androideabi21-clang++", 18 | "emu": "qemu-arm", 19 | "comp": "ndk", 20 | "shell": "bash", 21 | "archive_ext": "tar" 22 | } 23 | ], 24 | "binaries": ["armv8-dotprod", "armv8", "armv7", "armv7-neon"], 25 | "exclude": [ 26 | { 27 | "binaries": "armv8-dotprod", 28 | "config": { 29 | "compiler": "armv7a-linux-androideabi21-clang++" 30 | } 31 | }, 32 | { 33 | "binaries": "armv8", 34 | "config": { 35 | "compiler": "armv7a-linux-androideabi21-clang++" 36 | } 37 | }, 38 | { 39 | "binaries": "armv7", 40 | "config": { 41 | "compiler": "aarch64-linux-android21-clang++" 42 | } 43 | }, 44 | { 45 | "binaries": "armv7-neon", 46 | "config": { 47 | "compiler": "aarch64-linux-android21-clang++" 48 | } 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /.github/ci/libcxx17.imp: -------------------------------------------------------------------------------- 1 | [ 2 | # Mappings for libcxx's internal headers 3 | { include: [ "<__fwd/fstream.h>", private, "", public ] }, 4 | { include: [ "<__fwd/ios.h>", private, "", public ] }, 5 | { include: [ "<__fwd/istream.h>", private, "", public ] }, 6 | { include: [ "<__fwd/ostream.h>", private, "", public ] }, 7 | { include: [ "<__fwd/sstream.h>", private, "", public ] }, 8 | { include: [ "<__fwd/streambuf.h>", private, "", public ] }, 9 | { include: [ "<__fwd/string_view.h>", private, "", public ] }, 10 | { include: [ "<__system_error/errc.h>", private, "", public ] }, 11 | 12 | # Mappings for includes between public headers 13 | { include: [ "", public, "", public ] }, 14 | { include: [ "", public, "", public ] }, 15 | { include: [ "", public, "", public ] }, 16 | { include: [ "", public, "", public ] }, 17 | { include: [ "", public, "", public ] }, 18 | 19 | # Missing mappings in include-what-you-use's libcxx.imp 20 | { include: ["@<__condition_variable/.*>", private, "", public ] }, 21 | { include: ["@<__mutex/.*>", private, "", public ] }, 22 | ] 23 | -------------------------------------------------------------------------------- /.github/ci/matrix.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": [ 3 | { 4 | "name": "Ubuntu 22.04 GCC", 5 | "os": "ubuntu-22.04", 6 | "simple_name": "ubuntu", 7 | "compiler": "g++", 8 | "comp": "gcc", 9 | "shell": "bash", 10 | "archive_ext": "tar", 11 | "sde": "/home/runner/work/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.27.0-2023-09-13-lin/sde -future --" 12 | }, 13 | { 14 | "name": "MacOS 13 Apple Clang", 15 | "os": "macos-13", 16 | "simple_name": "macos", 17 | "compiler": "clang++", 18 | "comp": "clang", 19 | "shell": "bash", 20 | "archive_ext": "tar" 21 | }, 22 | { 23 | "name": "MacOS 14 Apple Clang M1", 24 | "os": "macos-14", 25 | "simple_name": "macos-m1", 26 | "compiler": "clang++", 27 | "comp": "clang", 28 | "shell": "bash", 29 | "archive_ext": "tar" 30 | }, 31 | { 32 | "name": "Windows 2022 Mingw-w64 GCC x86_64", 33 | "os": "windows-2022", 34 | "simple_name": "windows", 35 | "compiler": "g++", 36 | "comp": "mingw", 37 | "msys_sys": "mingw64", 38 | "msys_env": "x86_64-gcc", 39 | "shell": "msys2 {0}", 40 | "ext": ".exe", 41 | "sde": "/d/a/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.27.0-2023-09-13-win/sde.exe -future --", 42 | "archive_ext": "zip" 43 | } 44 | ], 45 | "binaries": [ 46 | "x86-64", 47 | "x86-64-sse41-popcnt", 48 | "x86-64-avx2", 49 | "x86-64-bmi2", 50 | "x86-64-avxvnni", 51 | "x86-64-avx512", 52 | "x86-64-vnni256", 53 | "x86-64-vnni512", 54 | "apple-silicon" 55 | ], 56 | "exclude": [ 57 | { 58 | "binaries": "x86-64", 59 | "config": { 60 | "os": "macos-14" 61 | } 62 | }, 63 | { 64 | "binaries": "x86-64-sse41-popcnt", 65 | "config": { 66 | "os": "macos-14" 67 | } 68 | }, 69 | { 70 | "binaries": "x86-64-avx2", 71 | "config": { 72 | "os": "macos-14" 73 | } 74 | }, 75 | { 76 | "binaries": "x86-64-bmi2", 77 | "config": { 78 | "os": "macos-14" 79 | } 80 | }, 81 | { 82 | "binaries": "x86-64-avxvnni", 83 | "config": { 84 | "os": "macos-14" 85 | } 86 | }, 87 | { 88 | "binaries": "x86-64-avx512", 89 | "config": { 90 | "os": "macos-14" 91 | } 92 | }, 93 | { 94 | "binaries": "x86-64-vnni256", 95 | "config": { 96 | "os": "macos-14" 97 | } 98 | }, 99 | { 100 | "binaries": "x86-64-vnni512", 101 | "config": { 102 | "os": "macos-14" 103 | } 104 | }, 105 | { 106 | "binaries": "x86-64-avxvnni", 107 | "config": { 108 | "os": "macos-13" 109 | } 110 | }, 111 | { 112 | "binaries": "x86-64-avx512", 113 | "config": { 114 | "os": "macos-13" 115 | } 116 | }, 117 | { 118 | "binaries": "x86-64-vnni256", 119 | "config": { 120 | "os": "macos-13" 121 | } 122 | }, 123 | { 124 | "binaries": "x86-64-vnni512", 125 | "config": { 126 | "os": "macos-13" 127 | } 128 | }, 129 | { 130 | "binaries": "apple-silicon", 131 | "config": { 132 | "os": "windows-2022" 133 | } 134 | }, 135 | { 136 | "binaries": "apple-silicon", 137 | "config": { 138 | "os": "macos-13" 139 | } 140 | }, 141 | { 142 | "binaries": "apple-silicon", 143 | "config": { 144 | "os": "ubuntu-22.04" 145 | } 146 | } 147 | ] 148 | } 149 | -------------------------------------------------------------------------------- /.github/workflows/arm_compilation.yml: -------------------------------------------------------------------------------- 1 | name: Compilation 2 | on: 3 | workflow_call: 4 | inputs: 5 | matrix: 6 | type: string 7 | required: true 8 | jobs: 9 | Compilation: 10 | name: ${{ matrix.config.name }} ${{ matrix.binaries }} 11 | runs-on: ${{ matrix.config.os }} 12 | env: 13 | COMPCXX: ${{ matrix.config.compiler }} 14 | COMP: ${{ matrix.config.comp }} 15 | EMU: ${{ matrix.config.emu }} 16 | EXT: ${{ matrix.config.ext }} 17 | BINARY: ${{ matrix.binaries }} 18 | strategy: 19 | fail-fast: false 20 | matrix: ${{ fromJson(inputs.matrix) }} 21 | defaults: 22 | run: 23 | working-directory: src 24 | shell: ${{ matrix.config.shell }} 25 | steps: 26 | - uses: actions/checkout@v4 27 | with: 28 | fetch-depth: 0 29 | persist-credentials: false 30 | 31 | - name: Download required linux packages 32 | if: runner.os == 'Linux' 33 | run: | 34 | sudo apt update 35 | sudo apt install qemu-user 36 | 37 | - name: Install NDK 38 | if: runner.os == 'Linux' 39 | run: | 40 | if [ $COMP == ndk ]; then 41 | NDKV="21.4.7075529" 42 | ANDROID_ROOT=/usr/local/lib/android 43 | ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk 44 | SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager 45 | echo "y" | $SDKMANAGER "ndk;$NDKV" 46 | ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$NDKV 47 | ANDROID_NDK_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin 48 | echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV 49 | fi 50 | 51 | - name: Extract the bench number from the commit history 52 | run: | 53 | for hash in $(git rev-list -100 HEAD); do 54 | 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 55 | done 56 | [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $hash" && echo "Reference bench: $benchref" || echo "No bench found" 57 | 58 | - name: Download the used network from the fishtest framework 59 | run: make net 60 | 61 | - name: Check compiler 62 | run: | 63 | if [ $COMP == ndk ]; then 64 | export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH 65 | fi 66 | $COMPCXX -v 67 | 68 | - name: Test help target 69 | run: make help 70 | 71 | - name: Check git 72 | run: git --version 73 | 74 | # Compile profile guided builds 75 | 76 | - name: Compile ${{ matrix.binaries }} build 77 | run: | 78 | if [ $COMP == ndk ]; then 79 | export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH 80 | export LDFLAGS="-static -Wno-unused-command-line-argument" 81 | fi 82 | make clean 83 | make -j4 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH=$EMU 84 | make strip ARCH=$BINARY COMP=$COMP 85 | WINE_PATH=$EMU ../tests/signature.sh $benchref 86 | mv ./stockfish$EXT ../stockfish-android-$BINARY$EXT 87 | 88 | - name: Remove non src files 89 | run: git clean -fx 90 | 91 | - name: Upload artifact for (pre)-release 92 | uses: actions/upload-artifact@v4 93 | with: 94 | name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} 95 | path: | 96 | . 97 | !.git 98 | !.output 99 | -------------------------------------------------------------------------------- /.github/workflows/clang-format.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run clang-format and comment on the PR. 2 | # Because of security reasons, it is crucial that this workflow 3 | # executes no shell script nor runs make. 4 | # Read this before editing: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ 5 | 6 | name: Clang-Format 7 | on: 8 | pull_request_target: 9 | branches: 10 | - "master" 11 | paths: 12 | - "**.cpp" 13 | - "**.h" 14 | 15 | permissions: 16 | pull-requests: write 17 | 18 | jobs: 19 | Clang-Format: 20 | name: Clang-Format 21 | runs-on: ubuntu-22.04 22 | steps: 23 | - uses: actions/checkout@v4 24 | with: 25 | ref: ${{ github.event.pull_request.head.sha }} 26 | 27 | - name: Run clang-format style check 28 | uses: jidicula/clang-format-action@f62da5e3d3a2d88ff364771d9d938773a618ab5e # @v4.11.0 29 | id: clang-format 30 | continue-on-error: true 31 | with: 32 | clang-format-version: "18" 33 | exclude-regex: "incbin" 34 | 35 | - name: Comment on PR 36 | if: steps.clang-format.outcome == 'failure' 37 | uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # @v2.5.0 38 | with: 39 | message: | 40 | clang-format 18 needs to be run on this PR. 41 | If you do not have clang-format installed, the maintainer will run it when merging. 42 | For the exact version please see https://packages.ubuntu.com/noble/clang-format-18. 43 | 44 | _(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_ 45 | comment_tag: execution 46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 47 | 48 | - name: Comment on PR 49 | if: steps.clang-format.outcome != 'failure' 50 | uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # @v2.5.0 51 | with: 52 | message: | 53 | _(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_ 54 | create_if_not_exists: false 55 | comment_tag: execution 56 | mode: delete 57 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 58 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: ["master"] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: ["master"] 9 | schedule: 10 | - cron: "17 18 * * 1" 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | permissions: 17 | actions: read 18 | contents: read 19 | security-events: write 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | language: ["cpp"] 25 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 26 | # Use only 'java' to analyze code written in Java, Kotlin, or both 27 | # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both 28 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 29 | 30 | steps: 31 | - name: Checkout repository 32 | uses: actions/checkout@v4 33 | with: 34 | persist-credentials: false 35 | 36 | # Initializes the CodeQL tools for scanning. 37 | - name: Initialize CodeQL 38 | uses: github/codeql-action/init@v3 39 | with: 40 | languages: ${{ matrix.language }} 41 | # If you wish to specify custom queries, you can do so here or in a config file. 42 | # By default, queries listed here will override any specified in a config file. 43 | # Prefix the list here with "+" to use these queries and those in the config file. 44 | 45 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 46 | # queries: security-extended,security-and-quality 47 | 48 | - name: Build 49 | working-directory: src 50 | run: make -j build ARCH=x86-64-modern 51 | 52 | - name: Perform CodeQL Analysis 53 | uses: github/codeql-action/analyze@v3 54 | with: 55 | category: "/language:${{matrix.language}}" 56 | -------------------------------------------------------------------------------- /.github/workflows/compilation.yml: -------------------------------------------------------------------------------- 1 | name: Compilation 2 | on: 3 | workflow_call: 4 | inputs: 5 | matrix: 6 | type: string 7 | required: true 8 | jobs: 9 | Compilation: 10 | name: ${{ matrix.config.name }} ${{ matrix.binaries }} 11 | runs-on: ${{ matrix.config.os }} 12 | env: 13 | COMPCXX: ${{ matrix.config.compiler }} 14 | COMP: ${{ matrix.config.comp }} 15 | EXT: ${{ matrix.config.ext }} 16 | NAME: ${{ matrix.config.simple_name }} 17 | BINARY: ${{ matrix.binaries }} 18 | SDE: ${{ matrix.config.sde }} 19 | strategy: 20 | fail-fast: false 21 | matrix: ${{ fromJson(inputs.matrix) }} 22 | defaults: 23 | run: 24 | working-directory: src 25 | shell: ${{ matrix.config.shell }} 26 | steps: 27 | - uses: actions/checkout@v4 28 | with: 29 | persist-credentials: false 30 | 31 | - name: Install fixed GCC on Linux 32 | if: runner.os == 'Linux' 33 | uses: egor-tensin/setup-gcc@eaa888eb19115a521fa72b65cd94fe1f25bbcaac # @v1.3 34 | with: 35 | version: 11 36 | 37 | - name: Setup msys and install required packages 38 | if: runner.os == 'Windows' 39 | uses: msys2/setup-msys2@v2 40 | with: 41 | msystem: ${{ matrix.config.msys_sys }} 42 | install: mingw-w64-${{ matrix.config.msys_env }} make git zip 43 | 44 | - name: Download SDE package 45 | if: runner.os == 'Linux' || runner.os == 'Windows' 46 | uses: petarpetrovt/setup-sde@91a1a03434384e064706634125a15f7446d2aafb # @v2.3 47 | with: 48 | environmentVariableName: SDE_DIR 49 | sdeVersion: 9.27.0 50 | 51 | - name: Download the used network from the fishtest framework 52 | run: make net 53 | 54 | - name: Check compiler 55 | run: $COMPCXX -v 56 | 57 | - name: Test help target 58 | run: make help 59 | 60 | - name: Check git 61 | run: git --version 62 | 63 | - name: Check compiler 64 | run: $COMPCXX -v 65 | 66 | - name: Show g++ cpu info 67 | if: runner.os != 'macOS' 68 | run: g++ -Q -march=native --help=target 69 | 70 | - name: Show clang++ cpu info 71 | if: runner.os == 'macOS' 72 | run: clang++ -E - -march=native -### 73 | 74 | # x86-64 with newer extensions tests 75 | 76 | - name: Compile ${{ matrix.config.binaries }} build 77 | run: | 78 | make clean 79 | make -j4 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH="$SDE" 80 | make strip ARCH=$BINARY COMP=$COMP 81 | WINE_PATH="$SDE" ../tests/signature.sh $benchref 82 | mv ./stockfish$EXT ../stockfish-$NAME-$BINARY$EXT 83 | 84 | - name: Remove non src files 85 | run: git clean -fx 86 | 87 | - name: Upload artifact for (pre)-release 88 | uses: actions/upload-artifact@v4 89 | with: 90 | name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} 91 | path: | 92 | . 93 | !.git 94 | !.output 95 | -------------------------------------------------------------------------------- /.github/workflows/games.yml: -------------------------------------------------------------------------------- 1 | # This workflow will play games with a debug enabled SF using the PR 2 | 3 | name: Games 4 | on: 5 | workflow_call: 6 | jobs: 7 | Matetrack: 8 | name: Games 9 | runs-on: ubuntu-22.04 10 | steps: 11 | - name: Checkout SF repo 12 | uses: actions/checkout@v4 13 | with: 14 | ref: ${{ github.event.pull_request.head.sha }} 15 | path: Stockfish 16 | persist-credentials: false 17 | 18 | - name: build debug enabled version of SF 19 | working-directory: Stockfish/src 20 | run: make -j build debug=yes 21 | 22 | - name: Checkout fastchess repo 23 | uses: actions/checkout@v4 24 | with: 25 | repository: Disservin/fastchess 26 | path: fastchess 27 | ref: 894616028492ae6114835195f14a899f6fa237d3 28 | persist-credentials: false 29 | 30 | - name: fastchess build 31 | working-directory: fastchess 32 | run: make -j 33 | 34 | - name: Run games 35 | working-directory: fastchess 36 | run: | 37 | ./fastchess -rounds 4 -games 2 -repeat -concurrency 4 -openings file=app/tests/data/openings.epd format=epd order=random -srand $RANDOM\ 38 | -engine name=sf1 cmd=/home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish\ 39 | -engine name=sf2 cmd=/home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish\ 40 | -ratinginterval 1 -report penta=true -each proto=uci tc=4+0.04 -log file=fast.log | tee fast.out 41 | cat fast.log 42 | ! grep "Assertion" fast.log > /dev/null 43 | ! grep "disconnect" fast.out > /dev/null 44 | -------------------------------------------------------------------------------- /.github/workflows/iwyu.yml: -------------------------------------------------------------------------------- 1 | name: IWYU 2 | on: 3 | workflow_call: 4 | jobs: 5 | Analyzers: 6 | name: Check includes 7 | runs-on: ubuntu-22.04 8 | defaults: 9 | run: 10 | working-directory: Stockfish/src 11 | shell: bash 12 | steps: 13 | - name: Checkout Stockfish 14 | uses: actions/checkout@v4 15 | with: 16 | path: Stockfish 17 | persist-credentials: false 18 | 19 | - name: Checkout include-what-you-use 20 | uses: actions/checkout@v4 21 | with: 22 | repository: include-what-you-use/include-what-you-use 23 | ref: f25caa280dc3277c4086ec345ad279a2463fea0f 24 | path: include-what-you-use 25 | persist-credentials: false 26 | 27 | - name: Download required linux packages 28 | run: | 29 | sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main' 30 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - 31 | sudo apt update 32 | sudo apt install -y libclang-17-dev clang-17 libc++-17-dev 33 | 34 | - name: Set up include-what-you-use 35 | run: | 36 | mkdir build && cd build 37 | cmake -G "Unix Makefiles" -DCMAKE_PREFIX_PATH="/usr/lib/llvm-17" .. 38 | sudo make install 39 | working-directory: include-what-you-use 40 | 41 | - name: Check include-what-you-use 42 | run: include-what-you-use --version 43 | 44 | - name: Check includes 45 | run: > 46 | make analyze 47 | COMP=clang 48 | CXX=include-what-you-use 49 | CXXFLAGS="-stdlib=libc++ -Xiwyu --comment_style=long -Xiwyu --mapping='${{ github.workspace }}/Stockfish/.github/ci/libcxx17.imp' -Xiwyu --error" 50 | -------------------------------------------------------------------------------- /.github/workflows/matetrack.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run matetrack on the PR 2 | 3 | name: Matetrack 4 | on: 5 | workflow_call: 6 | jobs: 7 | Matetrack: 8 | name: Matetrack 9 | runs-on: ubuntu-22.04 10 | steps: 11 | - name: Checkout SF repo 12 | uses: actions/checkout@v4 13 | with: 14 | ref: ${{ github.event.pull_request.head.sha }} 15 | path: Stockfish 16 | persist-credentials: false 17 | 18 | - name: build SF 19 | working-directory: Stockfish/src 20 | run: make -j profile-build 21 | 22 | - name: Checkout matetrack repo 23 | uses: actions/checkout@v4 24 | with: 25 | repository: vondele/matetrack 26 | path: matetrack 27 | ref: 4f8a80860ed8f3607f05a9195df8b40203bdc360 28 | persist-credentials: false 29 | 30 | - name: matetrack install deps 31 | working-directory: matetrack 32 | run: pip install -r requirements.txt 33 | 34 | - name: cache syzygy 35 | id: cache-syzygy 36 | uses: actions/cache@v4 37 | with: 38 | path: | 39 | matetrack/3-4-5-wdl/ 40 | matetrack/3-4-5-dtz/ 41 | key: key-syzygy 42 | 43 | - name: download syzygy 3-4-5 if needed 44 | working-directory: matetrack 45 | if: steps.cache-syzygy.outputs.cache-hit != 'true' 46 | run: | 47 | wget --no-verbose -r -nH --cut-dirs=2 --no-parent --reject="index.html*" -e robots=off https://tablebase.lichess.ovh/tables/standard/3-4-5-wdl/ 48 | wget --no-verbose -r -nH --cut-dirs=2 --no-parent --reject="index.html*" -e robots=off https://tablebase.lichess.ovh/tables/standard/3-4-5-dtz/ 49 | 50 | - name: Run matetrack 51 | working-directory: matetrack 52 | run: | 53 | python matecheck.py --syzygyPath 3-4-5-wdl/:3-4-5-dtz/ --engine /home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish --epdFile mates2000.epd --nodes 100000 | tee matecheckout.out 54 | ! grep "issues were detected" matecheckout.out > /dev/null 55 | 56 | - name: Run matetrack with --syzygy50MoveRule false 57 | working-directory: matetrack 58 | run: | 59 | grep 5men cursed.epd > cursed5.epd 60 | python matecheck.py --syzygyPath 3-4-5-wdl/:3-4-5-dtz/ --engine /home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish --epdFile cursed5.epd --nodes 100000 --syzygy50MoveRule false | tee matecheckcursed.out 61 | ! grep "issues were detected" matecheckcursed.out > /dev/null 62 | 63 | - name: Verify mate and TB win count for matecheckcursed.out 64 | working-directory: matetrack 65 | run: | 66 | mates=$(grep "Found mates:" matecheckcursed.out | awk '{print $3}') 67 | tbwins=$(grep "Found TB wins:" matecheckcursed.out | awk '{print $4}') 68 | if [ $(($mates + $tbwins)) -ne 32 ]; then 69 | echo "Sum of mates and TB wins is not 32 in matecheckcursed.out" >&2 70 | exit 1 71 | fi 72 | -------------------------------------------------------------------------------- /.github/workflows/sanitizers.yml: -------------------------------------------------------------------------------- 1 | name: Sanitizers 2 | on: 3 | workflow_call: 4 | jobs: 5 | Test-under-sanitizers: 6 | name: ${{ matrix.sanitizers.name }} 7 | runs-on: ${{ matrix.config.os }} 8 | env: 9 | COMPCXX: ${{ matrix.config.compiler }} 10 | COMP: ${{ matrix.config.comp }} 11 | CXXFLAGS: "-Werror" 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | config: 16 | - name: Ubuntu 22.04 GCC 17 | os: ubuntu-22.04 18 | compiler: g++ 19 | comp: gcc 20 | shell: bash 21 | sanitizers: 22 | - name: Run with thread sanitizer 23 | make_option: sanitize=thread 24 | cxx_extra_flags: "" 25 | instrumented_option: sanitizer-thread 26 | - name: Run with UB sanitizer 27 | make_option: sanitize=undefined 28 | cxx_extra_flags: "" 29 | instrumented_option: sanitizer-undefined 30 | - name: Run under valgrind 31 | make_option: "" 32 | cxx_extra_flags: "" 33 | instrumented_option: valgrind 34 | - name: Run under valgrind-thread 35 | make_option: "" 36 | cxx_extra_flags: "" 37 | instrumented_option: valgrind-thread 38 | - name: Run non-instrumented 39 | make_option: "" 40 | cxx_extra_flags: "" 41 | instrumented_option: none 42 | - name: Run with glibcxx assertions 43 | make_option: "" 44 | cxx_extra_flags: -D_GLIBCXX_ASSERTIONS 45 | instrumented_option: non 46 | defaults: 47 | run: 48 | working-directory: src 49 | shell: ${{ matrix.config.shell }} 50 | steps: 51 | - uses: actions/checkout@v4 52 | with: 53 | persist-credentials: false 54 | 55 | - name: Download required linux packages 56 | run: | 57 | sudo apt update 58 | sudo apt install expect valgrind g++-multilib 59 | 60 | - name: Download the used network from the fishtest framework 61 | run: make net 62 | 63 | - name: Check compiler 64 | run: $COMPCXX -v 65 | 66 | - name: Test help target 67 | run: make help 68 | 69 | - name: Check git 70 | run: git --version 71 | 72 | # Since Linux Kernel 6.5 we are getting false positives from the ci, 73 | # lower the ALSR entropy to disable ALSR, which works as a temporary workaround. 74 | # https://github.com/google/sanitizers/issues/1716 75 | # https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2056762 76 | 77 | - name: Lower ALSR entropy 78 | run: sudo sysctl -w vm.mmap_rnd_bits=28 79 | 80 | # Sanitizers 81 | 82 | - name: ${{ matrix.sanitizers.name }} 83 | run: | 84 | export CXXFLAGS="-O1 -fno-inline ${{ matrix.sanitizers.cxx_extra_flags }}" 85 | make clean 86 | make -j4 ARCH=x86-64-sse41-popcnt ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null 87 | python3 ../tests/instrumented.py --${{ matrix.sanitizers.instrumented_option }} ./stockfish 88 | -------------------------------------------------------------------------------- /.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.repository == 'official-stockfish/Stockfish' && (github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag')) 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: write # For deleting/creating a prerelease 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | persist-credentials: false 24 | 25 | # returns null if no pre-release exists 26 | - name: Get Commit SHA of Latest Pre-release 27 | run: | 28 | # Install required packages 29 | sudo apt-get update 30 | sudo apt-get install -y curl jq 31 | 32 | echo "COMMIT_SHA_TAG=$(jq -r 'map(select(.prerelease)) | first | .tag_name' <<< $(curl -s https://api.github.com/repos/${{ github.repository_owner }}/Stockfish/releases))" >> $GITHUB_ENV 33 | 34 | # delete old previous pre-release and tag 35 | - run: gh release delete ${{ env.COMMIT_SHA_TAG }} --cleanup-tag 36 | if: env.COMMIT_SHA_TAG != 'null' 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | 40 | # Make sure that an old ci that still runs on master doesn't recreate a prerelease 41 | - name: Check Pullable Commits 42 | id: check_commits 43 | run: | 44 | git fetch 45 | CHANGES=$(git rev-list HEAD..origin/master --count) 46 | echo "CHANGES=$CHANGES" >> $GITHUB_ENV 47 | 48 | - name: Get last commit SHA 49 | id: last_commit 50 | run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV 51 | 52 | - name: Get commit date 53 | id: commit_date 54 | run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV 55 | 56 | # Create a new pre-release, the other upload_binaries.yml will upload the binaries 57 | # to this pre-release. 58 | - name: Create Prerelease 59 | if: github.ref_name == 'master' && env.CHANGES == '0' 60 | uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981 61 | with: 62 | name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} 63 | tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} 64 | prerelease: true 65 | 66 | Matrix: 67 | runs-on: ubuntu-latest 68 | outputs: 69 | matrix: ${{ steps.set-matrix.outputs.matrix }} 70 | arm_matrix: ${{ steps.set-arm-matrix.outputs.arm_matrix }} 71 | steps: 72 | - uses: actions/checkout@v4 73 | with: 74 | persist-credentials: false 75 | - id: set-matrix 76 | run: | 77 | TASKS=$(echo $(cat .github/ci/matrix.json) ) 78 | echo "MATRIX=$TASKS" >> $GITHUB_OUTPUT 79 | - id: set-arm-matrix 80 | run: | 81 | TASKS_ARM=$(echo $(cat .github/ci/arm_matrix.json) ) 82 | echo "ARM_MATRIX=$TASKS_ARM" >> $GITHUB_OUTPUT 83 | Compilation: 84 | needs: [Matrix] 85 | uses: ./.github/workflows/compilation.yml 86 | with: 87 | matrix: ${{ needs.Matrix.outputs.matrix }} 88 | ARMCompilation: 89 | needs: [Matrix] 90 | uses: ./.github/workflows/arm_compilation.yml 91 | with: 92 | matrix: ${{ needs.Matrix.outputs.arm_matrix }} 93 | IWYU: 94 | uses: ./.github/workflows/iwyu.yml 95 | Sanitizers: 96 | uses: ./.github/workflows/sanitizers.yml 97 | Tests: 98 | uses: ./.github/workflows/tests.yml 99 | Matetrack: 100 | uses: ./.github/workflows/matetrack.yml 101 | Games: 102 | uses: ./.github/workflows/games.yml 103 | Binaries: 104 | if: github.repository == 'official-stockfish/Stockfish' 105 | needs: [Matrix, Prerelease, Compilation] 106 | uses: ./.github/workflows/upload_binaries.yml 107 | with: 108 | matrix: ${{ needs.Matrix.outputs.matrix }} 109 | permissions: 110 | contents: write # For deleting/creating a (pre)release 111 | secrets: 112 | token: ${{ secrets.GITHUB_TOKEN }} 113 | ARM_Binaries: 114 | if: github.repository == 'official-stockfish/Stockfish' 115 | needs: [Matrix, Prerelease, ARMCompilation] 116 | uses: ./.github/workflows/upload_binaries.yml 117 | with: 118 | matrix: ${{ needs.Matrix.outputs.arm_matrix }} 119 | permissions: 120 | contents: write # For deleting/creating a (pre)release 121 | secrets: 122 | token: ${{ secrets.GITHUB_TOKEN }} 123 | -------------------------------------------------------------------------------- /.github/workflows/upload_binaries.yml: -------------------------------------------------------------------------------- 1 | name: Upload Binaries 2 | on: 3 | workflow_call: 4 | inputs: 5 | matrix: 6 | type: string 7 | required: true 8 | secrets: 9 | token: 10 | required: true 11 | 12 | jobs: 13 | Artifacts: 14 | name: ${{ matrix.config.name }} ${{ matrix.binaries }} 15 | runs-on: ${{ matrix.config.os }} 16 | env: 17 | COMPCXX: ${{ matrix.config.compiler }} 18 | COMP: ${{ matrix.config.comp }} 19 | EXT: ${{ matrix.config.ext }} 20 | NAME: ${{ matrix.config.simple_name }} 21 | BINARY: ${{ matrix.binaries }} 22 | SDE: ${{ matrix.config.sde }} 23 | strategy: 24 | fail-fast: false 25 | matrix: ${{ fromJson(inputs.matrix) }} 26 | defaults: 27 | run: 28 | shell: ${{ matrix.config.shell }} 29 | steps: 30 | - uses: actions/checkout@v4 31 | with: 32 | persist-credentials: false 33 | 34 | - name: Download artifact from compilation 35 | uses: actions/download-artifact@v4 36 | with: 37 | name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} 38 | path: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} 39 | 40 | - name: Setup msys and install required packages 41 | if: runner.os == 'Windows' 42 | uses: msys2/setup-msys2@v2 43 | with: 44 | msystem: ${{ matrix.config.msys_sys }} 45 | install: mingw-w64-${{ matrix.config.msys_env }} make git zip 46 | 47 | - name: Create Package 48 | run: | 49 | mkdir stockfish 50 | 51 | - name: Download wiki 52 | run: | 53 | git clone https://github.com/official-stockfish/Stockfish.wiki.git wiki 54 | rm -rf wiki/.git 55 | mv wiki stockfish/ 56 | 57 | - name: Copy files 58 | run: | 59 | mv "${{ matrix.config.simple_name }} ${{ matrix.binaries }}" stockfish-workflow 60 | cd stockfish-workflow 61 | cp -r src ../stockfish/ 62 | cp -r scripts ../stockfish/ 63 | cp stockfish-$NAME-$BINARY$EXT ../stockfish/ 64 | cp "Top CPU Contributors.txt" ../stockfish/ 65 | cp Copying.txt ../stockfish/ 66 | cp AUTHORS ../stockfish/ 67 | cp CITATION.cff ../stockfish/ 68 | cp README.md ../stockfish/ 69 | cp CONTRIBUTING.md ../stockfish/ 70 | 71 | - name: Create tar 72 | if: runner.os != 'Windows' 73 | run: | 74 | chmod +x ./stockfish/stockfish-$NAME-$BINARY$EXT 75 | tar -cvf stockfish-$NAME-$BINARY.tar stockfish 76 | 77 | - name: Create zip 78 | if: runner.os == 'Windows' 79 | run: | 80 | zip -r stockfish-$NAME-$BINARY.zip stockfish 81 | 82 | - name: Release 83 | if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag' 84 | uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981 85 | with: 86 | files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} 87 | token: ${{ secrets.token }} 88 | 89 | - name: Get last commit sha 90 | id: last_commit 91 | run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV 92 | 93 | - name: Get commit date 94 | id: commit_date 95 | run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV 96 | 97 | # Make sure that an old ci that still runs on master doesn't recreate a prerelease 98 | - name: Check Pullable Commits 99 | id: check_commits 100 | run: | 101 | git fetch 102 | CHANGES=$(git rev-list HEAD..origin/master --count) 103 | echo "CHANGES=$CHANGES" >> $GITHUB_ENV 104 | 105 | - name: Prerelease 106 | if: github.ref_name == 'master' && env.CHANGES == '0' 107 | continue-on-error: true 108 | uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981 109 | with: 110 | name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} 111 | tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} 112 | prerelease: true 113 | files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} 114 | token: ${{ secrets.token }} 115 | -------------------------------------------------------------------------------- /.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 | # Files generated by the instrumented tests 14 | tsan.supp 15 | __pycache__/ 16 | tests/syzygy 17 | tests/bench_tmp.epd -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # Founders of the Stockfish project and Fishtest infrastructure 2 | Tord Romstad (romstad) 3 | Marco Costalba (mcostalba) 4 | Joona Kiiski (zamar) 5 | Gary Linscott (glinscott) 6 | 7 | # Authors and inventors of NNUE, training, and NNUE port 8 | Yu Nasu (ynasu87) 9 | Motohiro Isozaki (yaneurao) 10 | Hisayori Noda (nodchip) 11 | 12 | # All other authors of Stockfish code (in alphabetical order) 13 | Aditya (absimaldata) 14 | Adrian Petrescu (apetresc) 15 | Ahmed Kerimov (wcdbmv) 16 | Ajith Chandy Jose (ajithcj) 17 | Alain Savard (Rocky640) 18 | Alayan Feh (Alayan-stk-2) 19 | Alexander Kure 20 | Alexander Pagel (Lolligerhans) 21 | Alfredo Menezes (lonfom169) 22 | Ali AlZhrani (Cooffe) 23 | AliceRoselia 24 | Andreas Jan van der Meulen (Andyson007) 25 | Andreas Matthies (Matthies) 26 | Andrei Vetrov (proukornew) 27 | Andrew Grant (AndyGrant) 28 | Andrey Neporada (nepal) 29 | Andy Duplain 30 | Antoine Champion (antoinechampion) 31 | Aram Tumanian (atumanian) 32 | Arjun Temurnikar 33 | Artem Solopiy (EntityFX) 34 | Auguste Pop 35 | Balazs Szilagyi 36 | Balint Pfliegel 37 | Baptiste Rech (breatn) 38 | Ben Chaney (Chaneybenjamini) 39 | Ben Koshy (BKSpurgeon) 40 | Bill Henry (VoyagerOne) 41 | Bojun Guo (noobpwnftw, Nooby) 42 | borg323 43 | Boštjan Mejak (PedanticHacker) 44 | braich 45 | Brian Sheppard (SapphireBrand, briansheppard-toast) 46 | Bruno de Melo Costa (BM123499) 47 | Bruno Pellanda (pellanda) 48 | Bryan Cross (crossbr) 49 | candirufish 50 | Carlos Esparza Sánchez (ces42) 51 | Chess13234 52 | Chris Bao (sscg13) 53 | Chris Cain (ceebo) 54 | Ciekce 55 | clefrks 56 | Clemens L. (rn5f107s2) 57 | Cody Ho (aesrentai) 58 | Dale Weiler (graphitemaster) 59 | Daniel Axtens (daxtens) 60 | Daniel Dugovic (ddugovic) 61 | Daniel Monroe (Ergodice) 62 | Daniel Samek (DanSamek) 63 | Dan Schmidt (dfannius) 64 | Dariusz Orzechowski (dorzechowski) 65 | David (dav1312) 66 | David Zar 67 | Daylen Yang (daylen) 68 | Deshawn Mohan-Smith (GoldenRare) 69 | Dieter Dobbelaere (ddobbelaere) 70 | DiscanX 71 | Dominik Schlösser (domschl) 72 | double-beep 73 | Douglas Matos Gomes (dsmsgms) 74 | Dubslow 75 | Eduardo Cáceres (eduherminio) 76 | Eelco de Groot (KingDefender) 77 | Ehsan Rashid (erashid) 78 | Elvin Liu (solarlight2) 79 | erbsenzaehler 80 | Ernesto Gatti 81 | evqsx 82 | Fabian Beuke (madnight) 83 | Fabian Fichter (ianfab) 84 | Fanael Linithien (Fanael) 85 | fanon 86 | Fauzi Akram Dabat (fauzi2) 87 | Felix Wittmann 88 | gamander 89 | Gabriele Lombardo (gabe) 90 | Gahtan Nahdi 91 | Gary Heckman (gheckman) 92 | George Sobala (gsobala) 93 | gguliash 94 | Giacomo Lorenzetti (G-Lorenz) 95 | Gian-Carlo Pascutto (gcp) 96 | Goh CJ (cj5716) 97 | Gontran Lemaire (gonlem) 98 | Goodkov Vasiliy Aleksandrovich (goodkov) 99 | Gregor Cramer 100 | GuardianRM 101 | Guy Vreuls (gvreuls) 102 | Günther Demetz (pb00067, pb00068) 103 | Henri Wiechers 104 | Hiraoka Takuya (HiraokaTakuya) 105 | homoSapiensSapiens 106 | Hongzhi Cheng 107 | Ivan Ivec (IIvec) 108 | Jacques B. (Timshel) 109 | Jake Senne (w1wwwwww) 110 | Jan Ondruš (hxim) 111 | Jared Kish (Kurtbusch, kurt22i) 112 | Jarrod Torriero (DU-jdto) 113 | Jasper Shovelton (Beanie496) 114 | Jean-Francois Romang (jromang) 115 | Jean Gauthier (OuaisBla) 116 | Jekaa 117 | Jerry Donald Watson (jerrydonaldwatson) 118 | jjoshua2 119 | Jonathan Buladas Dumale (SFisGOD) 120 | Jonathan Calovski (Mysseno) 121 | Jonathan McDermid (jonathanmcdermid) 122 | Joost VandeVondele (vondele) 123 | Joseph Ellis (jhellis3) 124 | Joseph R. Prostko 125 | Jörg Oster (joergoster) 126 | Julian Willemer (NightlyKing) 127 | jundery 128 | Justin Blanchard (UncombedCoconut) 129 | Kelly Wilson 130 | Ken Takusagawa 131 | Kenneth Lee (kennethlee33) 132 | Kian E (KJE-98) 133 | kinderchocolate 134 | Kiran Panditrao (Krgp) 135 | Kojirion 136 | Krisztián Peőcz 137 | Krystian Kuzniarek (kuzkry) 138 | Leonardo Ljubičić (ICCF World Champion) 139 | Leonid Pechenik (lp--) 140 | Li Ying (yl25946) 141 | Liam Keegan (lkeegan) 142 | Linmiao Xu (linrock) 143 | Linus Arver (listx) 144 | loco-loco 145 | Lub van den Berg (ElbertoOne) 146 | Luca Brivio (lucabrivio) 147 | Lucas Braesch (lucasart) 148 | Lyudmil Antonov (lantonov) 149 | Maciej Żenczykowski (zenczykowski) 150 | Malcolm Campbell (xoto10) 151 | Mark Tenzer (31m059) 152 | marotear 153 | Mathias Parnaudeau (mparnaudeau) 154 | Matt Ginsberg (mattginsberg) 155 | Matthew Lai (matthewlai) 156 | Matthew Sullivan (Matt14916) 157 | Max A. (Disservin) 158 | Maxim Masiutin (maximmasiutin) 159 | Maxim Molchanov (Maxim) 160 | Michael An (man) 161 | Michael Byrne (MichaelB7) 162 | Michael Chaly (Vizvezdenec) 163 | Michael Stembera (mstembera) 164 | Michael Whiteley (protonspring) 165 | Michel Van den Bergh (vdbergh) 166 | Miguel Lahoz (miguel-l) 167 | Mikael Bäckman (mbootsector) 168 | Mike Babigian (Farseer) 169 | Mira 170 | Miroslav Fontán (Hexik) 171 | Moez Jellouli (MJZ1977) 172 | Mohammed Li (tthsqe12) 173 | Muzhen J (XInTheDark) 174 | Nathan Rugg (nmrugg) 175 | Nguyen Pham (nguyenpham) 176 | Nicklas Persson (NicklasPersson) 177 | Nick Pelling (nickpelling) 178 | Niklas Fiekas (niklasf) 179 | Nikolay Kostov (NikolayIT) 180 | Norman Schmidt (FireFather) 181 | notruck 182 | Nour Berakdar (Nonlinear) 183 | Ofek Shochat (OfekShochat, ghostway) 184 | Ondrej Mosnáček (WOnder93) 185 | Ondřej Mišina (AndrovT) 186 | Oskar Werkelin Ahlin 187 | Ömer Faruk Tutkun (OmerFarukTutkun) 188 | Pablo Vazquez 189 | Panthee 190 | Pascal Romaret 191 | Pasquale Pigazzini (ppigazzini) 192 | Patrick Jansen (mibere) 193 | Peter Schneider (pschneider1968) 194 | Peter Zsifkovits (CoffeeOne) 195 | PikaCat 196 | Praveen Kumar Tummala (praveentml) 197 | Prokop Randáček (ProkopRandacek) 198 | Rahul Dsilva (silversolver1) 199 | Ralph Stößer (Ralph Stoesser) 200 | Raminder Singh 201 | renouve 202 | Reuven Peleg (R-Peleg) 203 | Richard Lloyd (Richard-Lloyd) 204 | Robert Nürnberg (robertnurnberg) 205 | Rodrigo Exterckötter Tjäder 206 | Rodrigo Roim (roim) 207 | Ronald de Man (syzygy1, syzygy) 208 | Ron Britvich (Britvich) 209 | rqs 210 | Rui Coelho (ruicoelhopedro) 211 | Ryan Schmitt 212 | Ryan Takker 213 | Sami Kiminki (skiminki) 214 | Sebastian Buchwald (UniQP) 215 | Sergei Antonov (saproj) 216 | Sergei Ivanov (svivanov72) 217 | Sergio Vieri (sergiovieri) 218 | sf-x 219 | Shahin M. Shahin (peregrine) 220 | Shane Booth (shane31) 221 | Shawn Varghese (xXH4CKST3RXx) 222 | Shawn Xu (xu-shawn) 223 | Siad Daboul (Topologist) 224 | Stefan Geschwentner (locutus2) 225 | Stefano Cardanobile (Stefano80) 226 | Stefano Di Martino (StefanoD) 227 | Steinar Gunderson (sesse) 228 | Stéphane Nicolet (snicolet) 229 | Stephen Touset (stouset) 230 | Syine Mineta (MinetaS) 231 | Taras Vuk (TarasVuk) 232 | Thanar2 233 | thaspel 234 | theo77186 235 | TierynnB 236 | Ting-Hsuan Huang (fffelix-huang) 237 | Tobias Steinmann 238 | Tomasz Sobczyk (Sopel97) 239 | Tom Truscott 240 | Tom Vijlbrief (tomtor) 241 | Torsten Franz (torfranz, tfranzer) 242 | Torsten Hellwig (Torom) 243 | Tracey Emery (basepr1me) 244 | tttak 245 | Unai Corzo (unaiic) 246 | Uri Blass (uriblass) 247 | Vince Negri (cuddlestmonkey) 248 | Viren 249 | Wencey Wang 250 | windfishballad 251 | xefoci7612 252 | Xiang Wang (KatyushaScarlet) 253 | zz4032 254 | 255 | # Additionally, we acknowledge the authors and maintainers of fishtest, 256 | # an amazing and essential framework for Stockfish development! 257 | # 258 | # https://github.com/official-stockfish/fishtest/blob/master/AUTHORS 259 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Stockfish 2 | 3 | Welcome to the Stockfish project! We are excited that you are interested in 4 | contributing. This document outlines the guidelines and steps to follow when 5 | making contributions to Stockfish. 6 | 7 | ## Table of Contents 8 | 9 | - [Building Stockfish](#building-stockfish) 10 | - [Making Contributions](#making-contributions) 11 | - [Reporting Issues](#reporting-issues) 12 | - [Submitting Pull Requests](#submitting-pull-requests) 13 | - [Code Style](#code-style) 14 | - [Community and Communication](#community-and-communication) 15 | - [License](#license) 16 | 17 | ## Building Stockfish 18 | 19 | In case you do not have a C++ compiler installed, you can follow the 20 | instructions from our wiki. 21 | 22 | - [Ubuntu][ubuntu-compiling-link] 23 | - [Windows][windows-compiling-link] 24 | - [macOS][macos-compiling-link] 25 | 26 | ## Making Contributions 27 | 28 | ### Reporting Issues 29 | 30 | If you find a bug, please open an issue on the 31 | [issue tracker][issue-tracker-link]. Be sure to include relevant information 32 | like your operating system, build environment, and a detailed description of the 33 | problem. 34 | 35 | _Please note that Stockfish's development is not focused on adding new features. 36 | Thus any issue regarding missing features will potentially be closed without 37 | further discussion._ 38 | 39 | ### Submitting Pull Requests 40 | 41 | - Functional changes need to be tested on fishtest. See 42 | [Creating my First Test][creating-my-first-test] for more details. 43 | The accompanying pull request should include a link to the test results and 44 | the new bench. 45 | 46 | - Non-functional changes (e.g. refactoring, code style, documentation) do not 47 | need to be tested on fishtest, unless they might impact performance. 48 | 49 | - Provide a clear and concise description of the changes in the pull request 50 | description. 51 | 52 | _First time contributors should add their name to [AUTHORS](./AUTHORS)._ 53 | 54 | _Stockfish's development is not focused on adding new features. Thus any pull 55 | request introducing new features will potentially be closed without further 56 | discussion._ 57 | 58 | ## Code Style 59 | 60 | Changes to Stockfish C++ code should respect our coding style defined by 61 | [.clang-format](.clang-format). You can format your changes by running 62 | `make format`. This requires clang-format version 18 to be installed on your system. 63 | 64 | ## Navigate 65 | 66 | For experienced Git users who frequently use git blame, it is recommended to 67 | configure the blame.ignoreRevsFile setting. 68 | This setting is useful for excluding noisy formatting commits. 69 | 70 | ```bash 71 | git config blame.ignoreRevsFile .git-blame-ignore-revs 72 | ``` 73 | 74 | ## Community and Communication 75 | 76 | - Join the [Stockfish discord][discord-link] to discuss ideas, issues, and 77 | development. 78 | - Participate in the [Stockfish GitHub discussions][discussions-link] for 79 | broader conversations. 80 | 81 | ## License 82 | 83 | By contributing to Stockfish, you agree that your contributions will be licensed 84 | under the GNU General Public License v3.0. See [Copying.txt][copying-link] for 85 | more details. 86 | 87 | Thank you for contributing to Stockfish and helping us make it even better! 88 | 89 | [copying-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt 90 | [discord-link]: https://discord.gg/GWDRS3kU6R 91 | [discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new 92 | [creating-my-first-test]: https://github.com/official-stockfish/fishtest/wiki/Creating-my-first-test#create-your-test 93 | [issue-tracker-link]: https://github.com/official-stockfish/Stockfish/issues 94 | [ubuntu-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Developers#user-content-installing-a-compiler-1 95 | [windows-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Developers#user-content-installing-a-compiler 96 | [macos-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Developers#user-content-installing-a-compiler-2 97 | -------------------------------------------------------------------------------- /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][build-badge]][build-link] 21 | [![License][license-badge]][license-link] 22 |
23 | [![Release][release-badge]][release-link] 24 | [![Commits][commits-badge]][commits-link] 25 |
26 | [![Website][website-badge]][website-link] 27 | [![Fishtest][fishtest-badge]][fishtest-link] 28 | [![Discord][discord-badge]][discord-link] 29 | 30 |
31 | 32 | ## Overview 33 | 34 | [Stockfish][website-link] is a **free and strong UCI chess engine** derived from 35 | Glaurung 2.1 that analyzes chess positions and computes the optimal moves. 36 | 37 | Stockfish **does not include a graphical user interface** (GUI) that is required 38 | to display a chessboard and to make it easy to input moves. These GUIs are 39 | developed independently from Stockfish and are available online. **Read the 40 | documentation for your GUI** of choice for information about how to use 41 | Stockfish with it. 42 | 43 | See also the Stockfish [documentation][wiki-usage-link] for further usage help. 44 | 45 | ## Files 46 | 47 | This distribution of Stockfish consists of the following files: 48 | 49 | * [README.md][readme-link], the file you are currently reading. 50 | 51 | * [Copying.txt][license-link], a text file containing the GNU General Public 52 | License version 3. 53 | 54 | * [AUTHORS][authors-link], a text file with the list of authors for the project. 55 | 56 | * [src][src-link], a subdirectory containing the full source code, including a 57 | Makefile that can be used to compile Stockfish on Unix-like systems. 58 | 59 | * a file with the .nnue extension, storing the neural network for the NNUE 60 | evaluation. Binary distributions will have this file embedded. 61 | 62 | ## Contributing 63 | 64 | __See [Contributing Guide](CONTRIBUTING.md).__ 65 | 66 | ### Donating hardware 67 | 68 | Improving Stockfish requires a massive amount of testing. You can donate your 69 | hardware resources by installing the [Fishtest Worker][worker-link] and viewing 70 | the current tests on [Fishtest][fishtest-link]. 71 | 72 | ### Improving the code 73 | 74 | In the [chessprogramming wiki][programming-link], many techniques used in 75 | Stockfish are explained with a lot of background information. 76 | The [section on Stockfish][programmingsf-link] describes many features 77 | and techniques used by Stockfish. However, it is generic rather than 78 | focused on Stockfish's precise implementation. 79 | 80 | The engine testing is done on [Fishtest][fishtest-link]. 81 | If you want to help improve Stockfish, please read this [guideline][guideline-link] 82 | first, where the basics of Stockfish development are explained. 83 | 84 | Discussions about Stockfish take place these days mainly in the Stockfish 85 | [Discord server][discord-link]. This is also the best place to ask questions 86 | about the codebase and how to improve it. 87 | 88 | ## Compiling Stockfish 89 | 90 | Stockfish has support for 32 or 64-bit CPUs, certain hardware instructions, 91 | big-endian machines such as Power PC, and other platforms. 92 | 93 | On Unix-like systems, it should be easy to compile Stockfish directly from the 94 | source code with the included Makefile in the folder `src`. In general, it is 95 | recommended to run `make help` to see a list of make targets with corresponding 96 | descriptions. An example suitable for most Intel and AMD chips: 97 | 98 | ``` 99 | cd src 100 | make -j profile-build 101 | ``` 102 | 103 | Detailed compilation instructions for all platforms can be found in our 104 | [documentation][wiki-compile-link]. Our wiki also has information about 105 | the [UCI commands][wiki-uci-link] supported by Stockfish. 106 | 107 | ## Terms of use 108 | 109 | Stockfish is free and distributed under the 110 | [**GNU General Public License version 3**][license-link] (GPL v3). Essentially, 111 | this means you are free to do almost exactly what you want with the program, 112 | including distributing it among your friends, making it available for download 113 | from your website, selling it (either by itself or as part of some bigger 114 | software package), or using it as the starting point for a software project of 115 | your own. 116 | 117 | The only real limitation is that whenever you distribute Stockfish in some way, 118 | you MUST always include the license and the full source code (or a pointer to 119 | where the source code can be found) to generate the exact binary you are 120 | distributing. If you make any changes to the source code, these changes must 121 | also be made available under GPL v3. 122 | 123 | ## Acknowledgements 124 | 125 | Stockfish uses neural networks trained on [data provided by the Leela Chess Zero 126 | project][lc0-data-link], which is made available under the [Open Database License][odbl-link] (ODbL). 127 | 128 | 129 | [authors-link]: https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS 130 | [build-link]: https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml 131 | [commits-link]: https://github.com/official-stockfish/Stockfish/commits/master 132 | [discord-link]: https://discord.gg/GWDRS3kU6R 133 | [issue-link]: https://github.com/official-stockfish/Stockfish/issues/new?assignees=&labels=&template=BUG-REPORT.yml 134 | [discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new 135 | [fishtest-link]: https://tests.stockfishchess.org/tests 136 | [guideline-link]: https://github.com/official-stockfish/fishtest/wiki/Creating-my-first-test 137 | [license-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt 138 | [programming-link]: https://www.chessprogramming.org/Main_Page 139 | [programmingsf-link]: https://www.chessprogramming.org/Stockfish 140 | [readme-link]: https://github.com/official-stockfish/Stockfish/blob/master/README.md 141 | [release-link]: https://github.com/official-stockfish/Stockfish/releases/latest 142 | [src-link]: https://github.com/official-stockfish/Stockfish/tree/master/src 143 | [stockfish128-logo]: https://stockfishchess.org/images/logo/icon_128x128.png 144 | [uci-link]: https://backscattering.de/chess/uci/ 145 | [website-link]: https://stockfishchess.org 146 | [website-blog-link]: https://stockfishchess.org/blog/ 147 | [wiki-link]: https://github.com/official-stockfish/Stockfish/wiki 148 | [wiki-compile-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source 149 | [wiki-uci-link]: https://github.com/official-stockfish/Stockfish/wiki/UCI-&-Commands 150 | [wiki-usage-link]: https://github.com/official-stockfish/Stockfish/wiki/Download-and-usage 151 | [worker-link]: https://github.com/official-stockfish/fishtest/wiki/Running-the-worker 152 | [lc0-data-link]: https://storage.lczero.org/files/training_data 153 | [odbl-link]: https://opendatacommons.org/licenses/odbl/odbl-10.txt 154 | 155 | [build-badge]: https://img.shields.io/github/actions/workflow/status/official-stockfish/Stockfish/stockfish.yml?branch=master&style=for-the-badge&label=stockfish&logo=github 156 | [commits-badge]: https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge 157 | [discord-badge]: https://img.shields.io/discord/435943710472011776?style=for-the-badge&label=discord&logo=Discord 158 | [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 159 | [license-badge]: https://img.shields.io/github/license/official-stockfish/Stockfish?style=for-the-badge&label=license&color=success 160 | [release-badge]: https://img.shields.io/github/v/release/official-stockfish/Stockfish?style=for-the-badge&label=official%20release 161 | [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 162 | -------------------------------------------------------------------------------- /scripts/.gitattributes: -------------------------------------------------------------------------------- 1 | *.sh text eol=lf 2 | -------------------------------------------------------------------------------- /scripts/get_native_properties.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Returns properties of the native system. 5 | # best architecture as supported by the CPU 6 | # filename of the best binary uploaded as an artifact during CI 7 | # 8 | 9 | # Check if all the given flags are present in the CPU flags list 10 | check_flags() { 11 | for flag; do 12 | printf '%s\n' "$flags" | grep -q -w "$flag" || return 1 13 | done 14 | } 15 | 16 | # Set the CPU flags list 17 | # remove underscores and points from flags, e.g. gcc uses avx512vnni, while some cpuinfo can have avx512_vnni, some systems use sse4_1 others sse4.1 18 | get_flags() { 19 | flags=$(awk '/^flags[ \t]*:|^Features[ \t]*:/{gsub(/^flags[ \t]*:[ \t]*|^Features[ \t]*:[ \t]*|[_.]/, ""); line=$0} END{print line}' /proc/cpuinfo) 20 | } 21 | 22 | # Check for gcc march "znver1" or "znver2" https://en.wikichip.org/wiki/amd/cpuid 23 | check_znver_1_2() { 24 | vendor_id=$(awk '/^vendor_id/{print $3; exit}' /proc/cpuinfo) 25 | cpu_family=$(awk '/^cpu family/{print $4; exit}' /proc/cpuinfo) 26 | [ "$vendor_id" = "AuthenticAMD" ] && [ "$cpu_family" = "23" ] && znver_1_2=true 27 | } 28 | 29 | # Set the file CPU loongarch64 architecture 30 | set_arch_loongarch64() { 31 | if check_flags 'lasx'; then 32 | true_arch='loongarch64-lasx' 33 | elif check_flags 'lsx'; then 34 | true_arch='lonngarch64-lsx' 35 | else 36 | true_arch='loongarch64' 37 | fi 38 | } 39 | 40 | # Set the file CPU x86_64 architecture 41 | set_arch_x86_64() { 42 | if check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then 43 | true_arch='x86-64-vnni256' 44 | elif check_flags 'avx512f' 'avx512bw'; then 45 | true_arch='x86-64-avx512' 46 | elif [ -z "${znver_1_2+1}" ] && check_flags 'bmi2'; then 47 | true_arch='x86-64-bmi2' 48 | elif check_flags 'avx2'; then 49 | true_arch='x86-64-avx2' 50 | elif check_flags 'sse41' && check_flags 'popcnt'; then 51 | true_arch='x86-64-sse41-popcnt' 52 | else 53 | true_arch='x86-64' 54 | fi 55 | } 56 | 57 | set_arch_ppc_64() { 58 | if $(grep -q -w "altivec" /proc/cpuinfo); then 59 | power=$(grep -oP -m 1 'cpu\t+: POWER\K\d+' /proc/cpuinfo) 60 | if [ "0$power" -gt 7 ]; then 61 | # VSX started with POWER8 62 | true_arch='ppc-64-vsx' 63 | else 64 | true_arch='ppc-64-altivec' 65 | fi 66 | else 67 | true_arch='ppc-64' 68 | fi 69 | } 70 | 71 | # Check the system type 72 | uname_s=$(uname -s) 73 | uname_m=$(uname -m) 74 | case $uname_s in 75 | 'Darwin') # Mac OSX system 76 | case $uname_m in 77 | 'arm64') 78 | true_arch='apple-silicon' 79 | file_arch='m1-apple-silicon' 80 | ;; 81 | 'x86_64') 82 | flags=$(sysctl -n machdep.cpu.features machdep.cpu.leaf7_features | tr '\n' ' ' | tr '[:upper:]' '[:lower:]' | tr -d '_.') 83 | set_arch_x86_64 84 | if [ "$true_arch" = 'x86-64-vnni256' ] || [ "$true_arch" = 'x86-64-avx512' ]; then 85 | file_arch='x86-64-bmi2' 86 | fi 87 | ;; 88 | esac 89 | file_os='macos' 90 | file_ext='tar' 91 | ;; 92 | 'Linux') # Linux system 93 | get_flags 94 | case $uname_m in 95 | 'x86_64') 96 | file_os='ubuntu' 97 | check_znver_1_2 98 | set_arch_x86_64 99 | ;; 100 | 'i686') 101 | file_os='ubuntu' 102 | true_arch='x86-32' 103 | ;; 104 | 'ppc64'*) 105 | file_os='ubuntu' 106 | set_arch_ppc_64 107 | ;; 108 | 'aarch64') 109 | file_os='android' 110 | true_arch='armv8' 111 | if check_flags 'asimddp'; then 112 | true_arch="$true_arch-dotprod" 113 | fi 114 | ;; 115 | 'armv7'*) 116 | file_os='android' 117 | true_arch='armv7' 118 | if check_flags 'neon'; then 119 | true_arch="$true_arch-neon" 120 | fi 121 | ;; 122 | 'loongarch64'*) 123 | file_os='linux' 124 | set_arch_loongarch64 125 | ;; 126 | *) # Unsupported machine type, exit with error 127 | printf 'Unsupported machine type: %s\n' "$uname_m" 128 | exit 1 129 | ;; 130 | esac 131 | file_ext='tar' 132 | ;; 133 | 'CYGWIN'*|'MINGW'*|'MSYS'*) # Windows system with POSIX compatibility layer 134 | get_flags 135 | check_znver_1_2 136 | set_arch_x86_64 137 | file_os='windows' 138 | file_ext='zip' 139 | ;; 140 | *) 141 | # Unknown system type, exit with error 142 | printf 'Unsupported system type: %s\n' "$uname_s" 143 | exit 1 144 | ;; 145 | esac 146 | 147 | if [ -z "$file_arch" ]; then 148 | file_arch=$true_arch 149 | fi 150 | 151 | file_name="stockfish-$file_os-$file_arch.$file_ext" 152 | 153 | printf '%s %s\n' "$true_arch" "$file_name" 154 | -------------------------------------------------------------------------------- /scripts/net.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | wget_or_curl=$( (command -v wget > /dev/null 2>&1 && echo "wget -qO-") || \ 4 | (command -v curl > /dev/null 2>&1 && echo "curl -skL")) 5 | 6 | 7 | sha256sum=$( (command -v shasum > /dev/null 2>&1 && echo "shasum -a 256") || \ 8 | (command -v sha256sum > /dev/null 2>&1 && echo "sha256sum")) 9 | 10 | if [ -z "$sha256sum" ]; then 11 | >&2 echo "sha256sum not found, NNUE files will be assumed valid." 12 | fi 13 | 14 | get_nnue_filename() { 15 | grep "$1" evaluate.h | grep "#define" | sed "s/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/" 16 | } 17 | 18 | validate_network() { 19 | # If no sha256sum command is available, assume the file is always valid. 20 | if [ -n "$sha256sum" ] && [ -f "$1" ]; then 21 | if [ "$1" != "nn-$($sha256sum "$1" | cut -c 1-12).nnue" ]; then 22 | rm -f "$1" 23 | return 1 24 | fi 25 | fi 26 | } 27 | 28 | fetch_network() { 29 | _filename="$(get_nnue_filename "$1")" 30 | 31 | if [ -z "$_filename" ]; then 32 | >&2 echo "NNUE file name not found for: $1" 33 | return 1 34 | fi 35 | 36 | if [ -f "$_filename" ]; then 37 | if validate_network "$_filename"; then 38 | echo "Existing $_filename validated, skipping download" 39 | return 40 | else 41 | echo "Removing invalid NNUE file: $_filename" 42 | fi 43 | fi 44 | 45 | if [ -z "$wget_or_curl" ]; then 46 | >&2 printf "%s\n" "Neither wget or curl is installed." \ 47 | "Install one of these tools to download NNUE files automatically." 48 | exit 1 49 | fi 50 | 51 | for url in \ 52 | "https://tests.stockfishchess.org/api/nn/$_filename" \ 53 | "https://github.com/official-stockfish/networks/raw/master/$_filename"; do 54 | echo "Downloading from $url ..." 55 | if $wget_or_curl "$url" > "$_filename"; then 56 | if validate_network "$_filename"; then 57 | echo "Successfully validated $_filename" 58 | else 59 | echo "Downloaded $_filename is invalid" 60 | continue 61 | fi 62 | else 63 | echo "Failed to download from $url" 64 | fi 65 | if [ -f "$_filename" ]; then 66 | return 67 | fi 68 | done 69 | 70 | # Download was not successful in the loop, return false. 71 | >&2 echo "Failed to download $_filename" 72 | return 1 73 | } 74 | 75 | fetch_network EvalFileDefaultNameBig && \ 76 | fetch_network EvalFileDefaultNameSmall 77 | -------------------------------------------------------------------------------- /src/benchmark.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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::Benchmark { 27 | 28 | std::vector setup_bench(const std::string&, std::istream&); 29 | 30 | struct BenchmarkSetup { 31 | int ttSize; 32 | int threads; 33 | std::vector commands; 34 | std::string originalInvocation; 35 | std::string filledInvocation; 36 | }; 37 | 38 | BenchmarkSetup setup_benchmark(std::istream&); 39 | 40 | } // namespace Stockfish 41 | 42 | #endif // #ifndef BENCHMARK_H_INCLUDED 43 | -------------------------------------------------------------------------------- /src/engine.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 ENGINE_H_INCLUDED 20 | #define ENGINE_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "nnue/network.h" 32 | #include "numa.h" 33 | #include "position.h" 34 | #include "search.h" 35 | #include "syzygy/tbprobe.h" // for Stockfish::Depth 36 | #include "thread.h" 37 | #include "tt.h" 38 | #include "ucioption.h" 39 | 40 | namespace Stockfish { 41 | 42 | class Engine { 43 | public: 44 | using InfoShort = Search::InfoShort; 45 | using InfoFull = Search::InfoFull; 46 | using InfoIter = Search::InfoIteration; 47 | 48 | Engine(std::optional path = std::nullopt); 49 | 50 | // Cannot be movable due to components holding backreferences to fields 51 | Engine(const Engine&) = delete; 52 | Engine(Engine&&) = delete; 53 | Engine& operator=(const Engine&) = delete; 54 | Engine& operator=(Engine&&) = delete; 55 | 56 | ~Engine() { wait_for_search_finished(); } 57 | 58 | std::uint64_t perft(const std::string& fen, Depth depth, bool isChess960); 59 | 60 | // non blocking call to start searching 61 | void go(Search::LimitsType&); 62 | // non blocking call to stop searching 63 | void stop(); 64 | 65 | // blocking call to wait for search to finish 66 | void wait_for_search_finished(); 67 | // set a new position, moves are in UCI format 68 | void set_position(const std::string& fen, const std::vector& moves); 69 | 70 | // modifiers 71 | 72 | void set_numa_config_from_option(const std::string& o); 73 | void resize_threads(); 74 | void set_tt_size(size_t mb); 75 | void set_ponderhit(bool); 76 | void search_clear(); 77 | 78 | void set_on_update_no_moves(std::function&&); 79 | void set_on_update_full(std::function&&); 80 | void set_on_iter(std::function&&); 81 | void set_on_bestmove(std::function&&); 82 | void set_on_verify_networks(std::function&&); 83 | 84 | // network related 85 | 86 | void verify_networks() const; 87 | void load_networks(); 88 | void load_big_network(const std::string& file); 89 | void load_small_network(const std::string& file); 90 | void save_network(const std::pair, std::string> files[2]); 91 | 92 | // utility functions 93 | 94 | void trace_eval() const; 95 | 96 | const OptionsMap& get_options() const; 97 | OptionsMap& get_options(); 98 | 99 | int get_hashfull(int maxAge = 0) const; 100 | 101 | std::string fen() const; 102 | void flip(); 103 | std::string visualize() const; 104 | std::vector> get_bound_thread_count_by_numa_node() const; 105 | std::string get_numa_config_as_string() const; 106 | std::string numa_config_information_as_string() const; 107 | std::string thread_allocation_information_as_string() const; 108 | std::string thread_binding_information_as_string() const; 109 | 110 | private: 111 | const std::string binaryDirectory; 112 | 113 | NumaReplicationContext numaContext; 114 | 115 | Position pos; 116 | StateListPtr states; 117 | 118 | OptionsMap options; 119 | ThreadPool threads; 120 | TranspositionTable tt; 121 | LazyNumaReplicated networks; 122 | 123 | Search::SearchManager::UpdateContext updateContext; 124 | std::function onVerifyNetworks; 125 | }; 126 | 127 | } // namespace Stockfish 128 | 129 | 130 | #endif // #ifndef ENGINE_H_INCLUDED 131 | -------------------------------------------------------------------------------- /src/evaluate.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 "evaluate.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "nnue/network.h" 32 | #include "nnue/nnue_misc.h" 33 | #include "position.h" 34 | #include "types.h" 35 | #include "uci.h" 36 | #include "nnue/nnue_accumulator.h" 37 | 38 | namespace Stockfish { 39 | 40 | // Returns a static, purely materialistic evaluation of the position from 41 | // the point of view of the side to move. It can be divided by PawnValue to get 42 | // an approximation of the material advantage on the board in terms of pawns. 43 | int Eval::simple_eval(const Position& pos) { 44 | Color c = pos.side_to_move(); 45 | return PawnValue * (pos.count(c) - pos.count(~c)) 46 | + (pos.non_pawn_material(c) - pos.non_pawn_material(~c)); 47 | } 48 | 49 | bool Eval::use_smallnet(const Position& pos) { return std::abs(simple_eval(pos)) > 962; } 50 | 51 | // Evaluate is the evaluator for the outer world. It returns a static evaluation 52 | // of the position from the point of view of the side to move. 53 | Value Eval::evaluate(const Eval::NNUE::Networks& networks, 54 | const Position& pos, 55 | Eval::NNUE::AccumulatorStack& accumulators, 56 | Eval::NNUE::AccumulatorCaches& caches, 57 | int optimism) { 58 | 59 | assert(!pos.checkers()); 60 | 61 | bool smallNet = use_smallnet(pos); 62 | auto [psqt, positional] = smallNet ? networks.small.evaluate(pos, accumulators, &caches.small) 63 | : networks.big.evaluate(pos, accumulators, &caches.big); 64 | 65 | Value nnue = (125 * psqt + 131 * positional) / 128; 66 | 67 | // Re-evaluate the position when higher eval accuracy is worth the time spent 68 | if (smallNet && (std::abs(nnue) < 236)) 69 | { 70 | std::tie(psqt, positional) = networks.big.evaluate(pos, accumulators, &caches.big); 71 | nnue = (125 * psqt + 131 * positional) / 128; 72 | smallNet = false; 73 | } 74 | 75 | // Blend optimism and eval with nnue complexity 76 | int nnueComplexity = std::abs(psqt - positional); 77 | optimism += optimism * nnueComplexity / 468; 78 | nnue -= nnue * nnueComplexity / 18000; 79 | 80 | int material = 535 * pos.count() + pos.non_pawn_material(); 81 | int v = (nnue * (77777 + material) + optimism * (7777 + material)) / 77777; 82 | 83 | // Damp down the evaluation linearly when shuffling 84 | v -= v * pos.rule50_count() / 212; 85 | 86 | // Guarantee evaluation does not hit the tablebase range 87 | v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); 88 | 89 | return v; 90 | } 91 | 92 | // Like evaluate(), but instead of returning a value, it returns 93 | // a string (suitable for outputting to stdout) that contains the detailed 94 | // descriptions and values of each evaluation term. Useful for debugging. 95 | // Trace scores are from white's point of view 96 | std::string Eval::trace(Position& pos, const Eval::NNUE::Networks& networks) { 97 | 98 | if (pos.checkers()) 99 | return "Final evaluation: none (in check)"; 100 | 101 | Eval::NNUE::AccumulatorStack accumulators; 102 | auto caches = std::make_unique(networks); 103 | 104 | std::stringstream ss; 105 | ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); 106 | ss << '\n' << NNUE::trace(pos, networks, *caches) << '\n'; 107 | 108 | ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15); 109 | 110 | auto [psqt, positional] = networks.big.evaluate(pos, accumulators, &caches->big); 111 | Value v = psqt + positional; 112 | v = pos.side_to_move() == WHITE ? v : -v; 113 | ss << "NNUE evaluation " << 0.01 * UCIEngine::to_cp(v, pos) << " (white side)\n"; 114 | 115 | v = evaluate(networks, pos, accumulators, *caches, VALUE_ZERO); 116 | v = pos.side_to_move() == WHITE ? v : -v; 117 | ss << "Final evaluation " << 0.01 * UCIEngine::to_cp(v, pos) << " (white side)"; 118 | ss << " [with scaled NNUE, ...]"; 119 | ss << "\n"; 120 | 121 | return ss.str(); 122 | } 123 | 124 | } // namespace Stockfish 125 | -------------------------------------------------------------------------------- /src/evaluate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 | 24 | #include "types.h" 25 | 26 | namespace Stockfish { 27 | 28 | class Position; 29 | 30 | namespace Eval { 31 | 32 | // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue 33 | // for the build process (profile-build and fishtest) to work. Do not change the 34 | // name of the macro or the location where this macro is defined, as it is used 35 | // in the Makefile/Fishtest. 36 | #define EvalFileDefaultNameBig "nn-1c0000000000.nnue" 37 | #define EvalFileDefaultNameSmall "nn-37f18f62d772.nnue" 38 | 39 | namespace NNUE { 40 | struct Networks; 41 | struct AccumulatorCaches; 42 | class AccumulatorStack; 43 | } 44 | 45 | std::string trace(Position& pos, const Eval::NNUE::Networks& networks); 46 | 47 | int simple_eval(const Position& pos); 48 | bool use_smallnet(const Position& pos); 49 | Value evaluate(const NNUE::Networks& networks, 50 | const Position& pos, 51 | Eval::NNUE::AccumulatorStack& accumulators, 52 | Eval::NNUE::AccumulatorCaches& caches, 53 | int optimism); 54 | } // namespace Eval 55 | 56 | } // namespace Stockfish 57 | 58 | #endif // #ifndef EVALUATE_H_INCLUDED 59 | -------------------------------------------------------------------------------- /src/history.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 HISTORY_H_INCLUDED 20 | #define HISTORY_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include // IWYU pragma: keep 30 | 31 | #include "misc.h" 32 | #include "position.h" 33 | 34 | namespace Stockfish { 35 | 36 | constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 37 | constexpr int CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 38 | constexpr int CORRECTION_HISTORY_LIMIT = 1024; 39 | constexpr int LOW_PLY_HISTORY_SIZE = 4; 40 | 41 | static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0, 42 | "PAWN_HISTORY_SIZE has to be a power of 2"); 43 | 44 | static_assert((CORRECTION_HISTORY_SIZE & (CORRECTION_HISTORY_SIZE - 1)) == 0, 45 | "CORRECTION_HISTORY_SIZE has to be a power of 2"); 46 | 47 | enum PawnHistoryType { 48 | Normal, 49 | Correction 50 | }; 51 | 52 | template 53 | inline int pawn_structure_index(const Position& pos) { 54 | return pos.pawn_key() & ((T == Normal ? PAWN_HISTORY_SIZE : CORRECTION_HISTORY_SIZE) - 1); 55 | } 56 | 57 | inline int minor_piece_index(const Position& pos) { 58 | return pos.minor_piece_key() & (CORRECTION_HISTORY_SIZE - 1); 59 | } 60 | 61 | template 62 | inline int non_pawn_index(const Position& pos) { 63 | return pos.non_pawn_key(c) & (CORRECTION_HISTORY_SIZE - 1); 64 | } 65 | 66 | // StatsEntry is the container of various numerical statistics. We use a class 67 | // instead of a naked value to directly call history update operator<<() on 68 | // the entry. The first template parameter T is the base type of the array, 69 | // and the second template parameter D limits the range of updates in [-D, D] 70 | // when we update values with the << operator 71 | template 72 | class StatsEntry { 73 | 74 | static_assert(std::is_arithmetic_v, "Not an arithmetic type"); 75 | static_assert(D <= std::numeric_limits::max(), "D overflows T"); 76 | 77 | T entry; 78 | 79 | public: 80 | StatsEntry& operator=(const T& v) { 81 | entry = v; 82 | return *this; 83 | } 84 | operator const T&() const { return entry; } 85 | 86 | void operator<<(int bonus) { 87 | // Make sure that bonus is in range [-D, D] 88 | int clampedBonus = std::clamp(bonus, -D, D); 89 | entry += clampedBonus - entry * std::abs(clampedBonus) / D; 90 | 91 | assert(std::abs(entry) <= D); 92 | } 93 | }; 94 | 95 | enum StatsType { 96 | NoCaptures, 97 | Captures 98 | }; 99 | 100 | template 101 | using Stats = MultiArray, Sizes...>; 102 | 103 | // ButterflyHistory records how often quiet moves have been successful or unsuccessful 104 | // during the current search, and is used for reduction and move ordering decisions. 105 | // It uses 2 tables (one for each color) indexed by the move's from and to squares, 106 | // see https://www.chessprogramming.org/Butterfly_Boards (~11 elo) 107 | using ButterflyHistory = Stats; 108 | 109 | // LowPlyHistory is adressed by play and move's from and to squares, used 110 | // to improve move ordering near the root 111 | using LowPlyHistory = 112 | Stats; 113 | 114 | // CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] 115 | using CapturePieceToHistory = Stats; 116 | 117 | // PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] 118 | using PieceToHistory = Stats; 119 | 120 | // ContinuationHistory is the combined history of a given pair of moves, usually 121 | // the current one given a previous one. The nested history table is based on 122 | // PieceToHistory instead of ButterflyBoards. 123 | // (~63 elo) 124 | using ContinuationHistory = MultiArray; 125 | 126 | // PawnHistory is addressed by the pawn structure and a move's [piece][to] 127 | using PawnHistory = Stats; 128 | 129 | // Correction histories record differences between the static evaluation of 130 | // positions and their search score. It is used to improve the static evaluation 131 | // used by some search heuristics. 132 | // see https://www.chessprogramming.org/Static_Evaluation_Correction_History 133 | enum CorrHistType { 134 | Pawn, // By color and pawn structure 135 | Minor, // By color and positions of minor pieces (Knight, Bishop) 136 | NonPawn, // By non-pawn material positions and color 137 | PieceTo, // By [piece][to] move 138 | Continuation, // Combined history of move pairs 139 | }; 140 | 141 | namespace Detail { 142 | 143 | template 144 | struct CorrHistTypedef { 145 | using type = Stats; 146 | }; 147 | 148 | template<> 149 | struct CorrHistTypedef { 150 | using type = Stats; 151 | }; 152 | 153 | template<> 154 | struct CorrHistTypedef { 155 | using type = MultiArray::type, PIECE_NB, SQUARE_NB>; 156 | }; 157 | 158 | template<> 159 | struct CorrHistTypedef { 160 | using type = 161 | Stats; 162 | }; 163 | 164 | } 165 | 166 | template 167 | using CorrectionHistory = typename Detail::CorrHistTypedef::type; 168 | 169 | using TTMoveHistory = StatsEntry; 170 | 171 | } // namespace Stockfish 172 | 173 | #endif // #ifndef HISTORY_H_INCLUDED 174 | -------------------------------------------------------------------------------- /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-2025 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 "misc.h" 23 | #include "position.h" 24 | #include "types.h" 25 | #include "uci.h" 26 | #include "tune.h" 27 | 28 | using namespace Stockfish; 29 | 30 | int main(int argc, char* argv[]) { 31 | 32 | std::cout << engine_info() << std::endl; 33 | 34 | Bitboards::init(); 35 | Position::init(); 36 | 37 | UCIEngine uci(argc, argv); 38 | 39 | Tune::init(uci.engine_options()); 40 | 41 | uci.loop(); 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /src/memory.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 MEMORY_H_INCLUDED 20 | #define MEMORY_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "types.h" 31 | 32 | namespace Stockfish { 33 | 34 | void* std_aligned_alloc(size_t alignment, size_t size); 35 | void std_aligned_free(void* ptr); 36 | 37 | // Memory aligned by page size, min alignment: 4096 bytes 38 | void* aligned_large_pages_alloc(size_t size); 39 | void aligned_large_pages_free(void* mem); 40 | 41 | bool has_large_pages(); 42 | 43 | // Frees memory which was placed there with placement new. 44 | // Works for both single objects and arrays of unknown bound. 45 | template 46 | void memory_deleter(T* ptr, FREE_FUNC free_func) { 47 | if (!ptr) 48 | return; 49 | 50 | // Explicitly needed to call the destructor 51 | if constexpr (!std::is_trivially_destructible_v) 52 | ptr->~T(); 53 | 54 | free_func(ptr); 55 | } 56 | 57 | // Frees memory which was placed there with placement new. 58 | // Works for both single objects and arrays of unknown bound. 59 | template 60 | void memory_deleter_array(T* ptr, FREE_FUNC free_func) { 61 | if (!ptr) 62 | return; 63 | 64 | 65 | // Move back on the pointer to where the size is allocated 66 | const size_t array_offset = std::max(sizeof(size_t), alignof(T)); 67 | char* raw_memory = reinterpret_cast(ptr) - array_offset; 68 | 69 | if constexpr (!std::is_trivially_destructible_v) 70 | { 71 | const size_t size = *reinterpret_cast(raw_memory); 72 | 73 | // Explicitly call the destructor for each element in reverse order 74 | for (size_t i = size; i-- > 0;) 75 | ptr[i].~T(); 76 | } 77 | 78 | free_func(raw_memory); 79 | } 80 | 81 | // Allocates memory for a single object and places it there with placement new 82 | template 83 | inline std::enable_if_t, T*> memory_allocator(ALLOC_FUNC alloc_func, 84 | Args&&... args) { 85 | void* raw_memory = alloc_func(sizeof(T)); 86 | ASSERT_ALIGNED(raw_memory, alignof(T)); 87 | return new (raw_memory) T(std::forward(args)...); 88 | } 89 | 90 | // Allocates memory for an array of unknown bound and places it there with placement new 91 | template 92 | inline std::enable_if_t, std::remove_extent_t*> 93 | memory_allocator(ALLOC_FUNC alloc_func, size_t num) { 94 | using ElementType = std::remove_extent_t; 95 | 96 | const size_t array_offset = std::max(sizeof(size_t), alignof(ElementType)); 97 | 98 | // Save the array size in the memory location 99 | char* raw_memory = 100 | reinterpret_cast(alloc_func(array_offset + num * sizeof(ElementType))); 101 | ASSERT_ALIGNED(raw_memory, alignof(T)); 102 | 103 | new (raw_memory) size_t(num); 104 | 105 | for (size_t i = 0; i < num; ++i) 106 | new (raw_memory + array_offset + i * sizeof(ElementType)) ElementType(); 107 | 108 | // Need to return the pointer at the start of the array so that 109 | // the indexing in unique_ptr works. 110 | return reinterpret_cast(raw_memory + array_offset); 111 | } 112 | 113 | // 114 | // 115 | // aligned large page unique ptr 116 | // 117 | // 118 | 119 | template 120 | struct LargePageDeleter { 121 | void operator()(T* ptr) const { return memory_deleter(ptr, aligned_large_pages_free); } 122 | }; 123 | 124 | template 125 | struct LargePageArrayDeleter { 126 | void operator()(T* ptr) const { return memory_deleter_array(ptr, aligned_large_pages_free); } 127 | }; 128 | 129 | template 130 | using LargePagePtr = 131 | std::conditional_t, 132 | std::unique_ptr>>, 133 | std::unique_ptr>>; 134 | 135 | // make_unique_large_page for single objects 136 | template 137 | std::enable_if_t, LargePagePtr> make_unique_large_page(Args&&... args) { 138 | static_assert(alignof(T) <= 4096, 139 | "aligned_large_pages_alloc() may fail for such a big alignment requirement of T"); 140 | 141 | T* obj = memory_allocator(aligned_large_pages_alloc, std::forward(args)...); 142 | 143 | return LargePagePtr(obj); 144 | } 145 | 146 | // make_unique_large_page for arrays of unknown bound 147 | template 148 | std::enable_if_t, LargePagePtr> make_unique_large_page(size_t num) { 149 | using ElementType = std::remove_extent_t; 150 | 151 | static_assert(alignof(ElementType) <= 4096, 152 | "aligned_large_pages_alloc() may fail for such a big alignment requirement of T"); 153 | 154 | ElementType* memory = memory_allocator(aligned_large_pages_alloc, num); 155 | 156 | return LargePagePtr(memory); 157 | } 158 | 159 | // 160 | // 161 | // aligned unique ptr 162 | // 163 | // 164 | 165 | template 166 | struct AlignedDeleter { 167 | void operator()(T* ptr) const { return memory_deleter(ptr, std_aligned_free); } 168 | }; 169 | 170 | template 171 | struct AlignedArrayDeleter { 172 | void operator()(T* ptr) const { return memory_deleter_array(ptr, std_aligned_free); } 173 | }; 174 | 175 | template 176 | using AlignedPtr = 177 | std::conditional_t, 178 | std::unique_ptr>>, 179 | std::unique_ptr>>; 180 | 181 | // make_unique_aligned for single objects 182 | template 183 | std::enable_if_t, AlignedPtr> make_unique_aligned(Args&&... args) { 184 | const auto func = [](size_t size) { return std_aligned_alloc(alignof(T), size); }; 185 | T* obj = memory_allocator(func, std::forward(args)...); 186 | 187 | return AlignedPtr(obj); 188 | } 189 | 190 | // make_unique_aligned for arrays of unknown bound 191 | template 192 | std::enable_if_t, AlignedPtr> make_unique_aligned(size_t num) { 193 | using ElementType = std::remove_extent_t; 194 | 195 | const auto func = [](size_t size) { return std_aligned_alloc(alignof(ElementType), size); }; 196 | ElementType* memory = memory_allocator(func, num); 197 | 198 | return AlignedPtr(memory); 199 | } 200 | 201 | 202 | // Get the first aligned element of an array. 203 | // ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes, 204 | // where N is the number of elements in the array. 205 | template 206 | T* align_ptr_up(T* ptr) { 207 | static_assert(alignof(T) < Alignment); 208 | 209 | const uintptr_t ptrint = reinterpret_cast(reinterpret_cast(ptr)); 210 | return reinterpret_cast( 211 | reinterpret_cast((ptrint + (Alignment - 1)) / Alignment * Alignment)); 212 | } 213 | 214 | 215 | } // namespace Stockfish 216 | 217 | #endif // #ifndef MEMORY_H_INCLUDED 218 | -------------------------------------------------------------------------------- /src/movegen.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 // IWYU pragma: keep 23 | #include 24 | 25 | #include "types.h" 26 | 27 | namespace Stockfish { 28 | 29 | class Position; 30 | 31 | enum GenType { 32 | CAPTURES, 33 | QUIETS, 34 | EVASIONS, 35 | NON_EVASIONS, 36 | LEGAL 37 | }; 38 | 39 | struct ExtMove: public Move { 40 | int value; 41 | 42 | void operator=(Move m) { data = m.raw(); } 43 | 44 | // Inhibit unwanted implicit conversions to Move 45 | // with an ambiguity that yields to a compile error. 46 | operator float() const = delete; 47 | }; 48 | 49 | inline bool operator<(const ExtMove& f, const ExtMove& s) { return f.value < s.value; } 50 | 51 | template 52 | ExtMove* generate(const Position& pos, ExtMove* moveList); 53 | 54 | // The MoveList struct wraps the generate() function and returns a convenient 55 | // list of moves. Using MoveList is sometimes preferable to directly calling 56 | // the lower level generate() function. 57 | template 58 | struct MoveList { 59 | 60 | explicit MoveList(const Position& pos) : 61 | last(generate(pos, moveList)) {} 62 | const ExtMove* begin() const { return moveList; } 63 | const ExtMove* end() const { return last; } 64 | size_t size() const { return last - moveList; } 65 | bool contains(Move move) const { return std::find(begin(), end(), move) != end(); } 66 | 67 | private: 68 | ExtMove moveList[MAX_MOVES], *last; 69 | }; 70 | 71 | } // namespace Stockfish 72 | 73 | #endif // #ifndef MOVEGEN_H_INCLUDED 74 | -------------------------------------------------------------------------------- /src/movepick.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 | 24 | #include "history.h" 25 | #include "movegen.h" 26 | #include "types.h" 27 | 28 | namespace Stockfish { 29 | 30 | class Position; 31 | 32 | template 33 | class ValueList; 34 | 35 | // The MovePicker class is used to pick one pseudo-legal move at a time from the 36 | // current position. The most important method is next_move(), which emits one 37 | // new pseudo-legal move on every call, until there are no moves left, when 38 | // Move::none() is returned. In order to improve the efficiency of the alpha-beta 39 | // algorithm, MovePicker attempts to return the moves which are most likely to get 40 | // a cut-off first. 41 | class MovePicker { 42 | 43 | public: 44 | MovePicker(const MovePicker&) = delete; 45 | MovePicker& operator=(const MovePicker&) = delete; 46 | MovePicker(const Position&, 47 | Move, 48 | Depth, 49 | const ButterflyHistory*, 50 | const LowPlyHistory*, 51 | const CapturePieceToHistory*, 52 | const PieceToHistory**, 53 | const PawnHistory*, 54 | int); 55 | MovePicker(const Position&, Move, int, const CapturePieceToHistory*); 56 | Move next_move(); 57 | void skip_quiet_moves(); 58 | bool otherPieceTypesMobile(PieceType pt, ValueList& capturesSearched); 59 | 60 | private: 61 | template 62 | Move select(Pred); 63 | template 64 | void score(); 65 | ExtMove* begin() { return cur; } 66 | ExtMove* end() { return endMoves; } 67 | 68 | const Position& pos; 69 | const ButterflyHistory* mainHistory; 70 | const LowPlyHistory* lowPlyHistory; 71 | const CapturePieceToHistory* captureHistory; 72 | const PieceToHistory** continuationHistory; 73 | const PawnHistory* pawnHistory; 74 | Move ttMove; 75 | ExtMove * cur, *endMoves, *endBadCaptures, *beginBadQuiets, *endBadQuiets; 76 | int stage; 77 | int threshold; 78 | Depth depth; 79 | int ply; 80 | bool skipQuiets = false; 81 | ExtMove moves[MAX_MOVES]; 82 | }; 83 | 84 | } // namespace Stockfish 85 | 86 | #endif // #ifndef MOVEPICK_H_INCLUDED 87 | -------------------------------------------------------------------------------- /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-2025 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 "../../bitboard.h" 24 | #include "../../position.h" 25 | #include "../../types.h" 26 | #include "../nnue_accumulator.h" 27 | 28 | namespace Stockfish::Eval::NNUE::Features { 29 | 30 | // Index of a feature for a given king position and another piece on some square 31 | template 32 | inline IndexType HalfKAv2_hm::make_index(Square s, Piece pc, Square ksq) { 33 | return IndexType((int(s) ^ OrientTBL[Perspective][ksq]) + PieceSquareIndex[Perspective][pc] 34 | + KingBuckets[Perspective][ksq]); 35 | } 36 | 37 | // Get a list of indices for active features 38 | template 39 | void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active) { 40 | Square ksq = pos.square(Perspective); 41 | Bitboard bb = pos.pieces(); 42 | while (bb) 43 | { 44 | Square s = pop_lsb(bb); 45 | active.push_back(make_index(s, pos.piece_on(s), ksq)); 46 | } 47 | } 48 | 49 | // Explicit template instantiations 50 | template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); 51 | template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); 52 | template IndexType HalfKAv2_hm::make_index(Square s, Piece pc, Square ksq); 53 | template IndexType HalfKAv2_hm::make_index(Square s, Piece pc, Square ksq); 54 | 55 | // Get a list of indices for recently changed features 56 | template 57 | void HalfKAv2_hm::append_changed_indices(Square ksq, 58 | const DirtyPiece& dp, 59 | IndexList& removed, 60 | IndexList& added) { 61 | for (int i = 0; i < dp.dirty_num; ++i) 62 | { 63 | if (dp.from[i] != SQ_NONE) 64 | removed.push_back(make_index(dp.from[i], dp.piece[i], ksq)); 65 | if (dp.to[i] != SQ_NONE) 66 | added.push_back(make_index(dp.to[i], dp.piece[i], ksq)); 67 | } 68 | } 69 | 70 | // Explicit template instantiations 71 | template void HalfKAv2_hm::append_changed_indices(Square ksq, 72 | const DirtyPiece& dp, 73 | IndexList& removed, 74 | IndexList& added); 75 | template void HalfKAv2_hm::append_changed_indices(Square ksq, 76 | const DirtyPiece& dp, 77 | IndexList& removed, 78 | IndexList& added); 79 | 80 | bool HalfKAv2_hm::requires_refresh(const DirtyPiece& dirtyPiece, Color perspective) { 81 | return 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-2025 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 25 | 26 | #include "../../misc.h" 27 | #include "../../types.h" 28 | #include "../nnue_common.h" 29 | 30 | namespace Stockfish { 31 | class Position; 32 | } 33 | 34 | namespace Stockfish::Eval::NNUE::Features { 35 | 36 | // Feature HalfKAv2_hm: Combination of the position of own king and the 37 | // position of pieces. Position mirrored such that king is always on e..h files. 38 | class HalfKAv2_hm { 39 | 40 | // Unique number for each piece type on each square 41 | enum { 42 | PS_NONE = 0, 43 | PS_W_PAWN = 0, 44 | PS_B_PAWN = 1 * SQUARE_NB, 45 | PS_W_KNIGHT = 2 * SQUARE_NB, 46 | PS_B_KNIGHT = 3 * SQUARE_NB, 47 | PS_W_BISHOP = 4 * SQUARE_NB, 48 | PS_B_BISHOP = 5 * SQUARE_NB, 49 | PS_W_ROOK = 6 * SQUARE_NB, 50 | PS_B_ROOK = 7 * SQUARE_NB, 51 | PS_W_QUEEN = 8 * SQUARE_NB, 52 | PS_B_QUEEN = 9 * SQUARE_NB, 53 | PS_KING = 10 * SQUARE_NB, 54 | PS_NB = 11 * SQUARE_NB 55 | }; 56 | 57 | static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = { 58 | // Convention: W - us, B - them 59 | // Viewed from other side, W and B are reversed 60 | {PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_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_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE, 63 | PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE}}; 64 | 65 | public: 66 | // Feature name 67 | static constexpr const char* Name = "HalfKAv2_hm(Friend)"; 68 | 69 | // Hash value embedded in the evaluation file 70 | static constexpr std::uint32_t HashValue = 0x7f234cb8u; 71 | 72 | // Number of feature dimensions 73 | static constexpr IndexType Dimensions = 74 | static_cast(SQUARE_NB) * static_cast(PS_NB) / 2; 75 | 76 | #define B(v) (v * PS_NB) 77 | // clang-format off 78 | static constexpr int KingBuckets[COLOR_NB][SQUARE_NB] = { 79 | { B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28), 80 | B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24), 81 | B(20), B(21), B(22), B(23), B(23), B(22), B(21), B(20), 82 | B(16), B(17), B(18), B(19), B(19), B(18), B(17), B(16), 83 | B(12), B(13), B(14), B(15), B(15), B(14), B(13), B(12), 84 | B( 8), B( 9), B(10), B(11), B(11), B(10), B( 9), B( 8), 85 | B( 4), B( 5), B( 6), B( 7), B( 7), B( 6), B( 5), B( 4), 86 | B( 0), B( 1), B( 2), B( 3), B( 3), B( 2), B( 1), B( 0) }, 87 | { B( 0), B( 1), B( 2), B( 3), B( 3), B( 2), B( 1), B( 0), 88 | B( 4), B( 5), B( 6), B( 7), B( 7), B( 6), B( 5), B( 4), 89 | B( 8), B( 9), B(10), B(11), B(11), B(10), B( 9), B( 8), 90 | B(12), B(13), B(14), B(15), B(15), B(14), B(13), B(12), 91 | B(16), B(17), B(18), B(19), B(19), B(18), B(17), B(16), 92 | B(20), B(21), B(22), B(23), B(23), B(22), B(21), B(20), 93 | B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24), 94 | B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28) } 95 | }; 96 | // clang-format on 97 | #undef B 98 | // clang-format off 99 | // Orient a square according to perspective (rotates by 180 for black) 100 | static constexpr int OrientTBL[COLOR_NB][SQUARE_NB] = { 101 | { SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, 102 | SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, 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_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, 110 | SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, 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 | }; 118 | // clang-format on 119 | 120 | // Maximum number of simultaneously active features. 121 | static constexpr IndexType MaxActiveDimensions = 32; 122 | using IndexList = ValueList; 123 | 124 | // Index of a feature for a given king position and another piece on some square 125 | template 126 | static IndexType make_index(Square s, Piece pc, Square ksq); 127 | 128 | // Get a list of indices for active features 129 | template 130 | static void append_active_indices(const Position& pos, IndexList& active); 131 | 132 | // Get a list of indices for recently changed features 133 | template 134 | static void 135 | append_changed_indices(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added); 136 | 137 | // Returns whether the change stored in this DirtyPiece means 138 | // that a full accumulator refresh is required. 139 | static bool requires_refresh(const DirtyPiece& dirtyPiece, Color perspective); 140 | }; 141 | 142 | } // namespace Stockfish::Eval::NNUE::Features 143 | 144 | #endif // #ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED 145 | -------------------------------------------------------------------------------- /src/nnue/layers/clipped_relu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 25 | #include 26 | #include 27 | 28 | #include "../nnue_common.h" 29 | 30 | namespace Stockfish::Eval::NNUE::Layers { 31 | 32 | // Clipped ReLU 33 | template 34 | class ClippedReLU { 35 | public: 36 | // Input/output type 37 | using InputType = std::int32_t; 38 | using OutputType = std::uint8_t; 39 | 40 | // Number of input/output dimensions 41 | static constexpr IndexType InputDimensions = InDims; 42 | static constexpr IndexType OutputDimensions = InputDimensions; 43 | static constexpr IndexType PaddedOutputDimensions = 44 | ceil_to_multiple(OutputDimensions, 32); 45 | 46 | using OutputBuffer = OutputType[PaddedOutputDimensions]; 47 | 48 | // Hash value embedded in the evaluation file 49 | static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { 50 | std::uint32_t hashValue = 0x538D24C7u; 51 | hashValue += prevHash; 52 | return hashValue; 53 | } 54 | 55 | // Read network parameters 56 | bool read_parameters(std::istream&) { return true; } 57 | 58 | // Write network parameters 59 | bool write_parameters(std::ostream&) const { return true; } 60 | 61 | // Forward propagation 62 | void propagate(const InputType* input, OutputType* output) const { 63 | 64 | #if defined(USE_AVX2) 65 | if constexpr (InputDimensions % SimdWidth == 0) 66 | { 67 | constexpr IndexType NumChunks = InputDimensions / SimdWidth; 68 | const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); 69 | const auto in = reinterpret_cast(input); 70 | const auto out = reinterpret_cast<__m256i*>(output); 71 | for (IndexType i = 0; i < NumChunks; ++i) 72 | { 73 | const __m256i words0 = 74 | _mm256_srli_epi16(_mm256_packus_epi32(_mm256_load_si256(&in[i * 4 + 0]), 75 | _mm256_load_si256(&in[i * 4 + 1])), 76 | WeightScaleBits); 77 | const __m256i words1 = 78 | _mm256_srli_epi16(_mm256_packus_epi32(_mm256_load_si256(&in[i * 4 + 2]), 79 | _mm256_load_si256(&in[i * 4 + 3])), 80 | WeightScaleBits); 81 | _mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32( 82 | _mm256_packs_epi16(words0, words1), Offsets)); 83 | } 84 | } 85 | else 86 | { 87 | constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); 88 | const auto in = reinterpret_cast(input); 89 | const auto out = reinterpret_cast<__m128i*>(output); 90 | for (IndexType i = 0; i < NumChunks; ++i) 91 | { 92 | const __m128i words0 = _mm_srli_epi16( 93 | _mm_packus_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1])), 94 | WeightScaleBits); 95 | const __m128i words1 = _mm_srli_epi16( 96 | _mm_packus_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])), 97 | WeightScaleBits); 98 | _mm_store_si128(&out[i], _mm_packs_epi16(words0, words1)); 99 | } 100 | } 101 | constexpr IndexType Start = InputDimensions % SimdWidth == 0 102 | ? InputDimensions / SimdWidth * SimdWidth 103 | : InputDimensions / (SimdWidth / 2) * (SimdWidth / 2); 104 | 105 | #elif defined(USE_SSE2) 106 | constexpr IndexType NumChunks = InputDimensions / SimdWidth; 107 | 108 | #ifndef USE_SSE41 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 | { 116 | #if defined(USE_SSE41) 117 | const __m128i words0 = _mm_srli_epi16( 118 | _mm_packus_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1])), 119 | WeightScaleBits); 120 | const __m128i words1 = _mm_srli_epi16( 121 | _mm_packus_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])), 122 | WeightScaleBits); 123 | _mm_store_si128(&out[i], _mm_packs_epi16(words0, words1)); 124 | #else 125 | const __m128i words0 = _mm_srai_epi16( 126 | _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1])), 127 | WeightScaleBits); 128 | const __m128i words1 = _mm_srai_epi16( 129 | _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])), 130 | WeightScaleBits); 131 | const __m128i packedbytes = _mm_packs_epi16(words0, words1); 132 | _mm_store_si128(&out[i], _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)); 133 | #endif 134 | } 135 | constexpr IndexType Start = NumChunks * SimdWidth; 136 | 137 | #elif defined(USE_NEON) 138 | constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); 139 | const int8x8_t Zero = {0}; 140 | const auto in = reinterpret_cast(input); 141 | const auto out = reinterpret_cast(output); 142 | for (IndexType i = 0; i < NumChunks; ++i) 143 | { 144 | int16x8_t shifted; 145 | const auto pack = reinterpret_cast(&shifted); 146 | pack[0] = vqshrn_n_s32(in[i * 2 + 0], WeightScaleBits); 147 | pack[1] = vqshrn_n_s32(in[i * 2 + 1], WeightScaleBits); 148 | out[i] = vmax_s8(vqmovn_s16(shifted), Zero); 149 | } 150 | constexpr IndexType Start = NumChunks * (SimdWidth / 2); 151 | #else 152 | constexpr IndexType Start = 0; 153 | #endif 154 | 155 | for (IndexType i = Start; i < InputDimensions; ++i) 156 | { 157 | output[i] = static_cast(std::clamp(input[i] >> WeightScaleBits, 0, 127)); 158 | } 159 | } 160 | }; 161 | 162 | } // namespace Stockfish::Eval::NNUE::Layers 163 | 164 | #endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED 165 | -------------------------------------------------------------------------------- /src/nnue/layers/simd.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 STOCKFISH_SIMD_H_INCLUDED 20 | #define STOCKFISH_SIMD_H_INCLUDED 21 | 22 | #if defined(USE_AVX2) 23 | #include 24 | 25 | #elif defined(USE_SSE41) 26 | #include 27 | 28 | #elif defined(USE_SSSE3) 29 | #include 30 | 31 | #elif defined(USE_SSE2) 32 | #include 33 | 34 | #elif defined(USE_NEON) 35 | #include 36 | #endif 37 | 38 | namespace Stockfish::Simd { 39 | 40 | #if defined(USE_AVX512) 41 | 42 | [[maybe_unused]] static int m512_hadd(__m512i sum, int bias) { 43 | return _mm512_reduce_add_epi32(sum) + bias; 44 | } 45 | 46 | [[maybe_unused]] static void m512_add_dpbusd_epi32(__m512i& acc, __m512i a, __m512i b) { 47 | 48 | #if defined(USE_VNNI) 49 | acc = _mm512_dpbusd_epi32(acc, a, b); 50 | #else 51 | __m512i product0 = _mm512_maddubs_epi16(a, b); 52 | product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); 53 | acc = _mm512_add_epi32(acc, product0); 54 | #endif 55 | } 56 | 57 | #endif 58 | 59 | #if defined(USE_AVX2) 60 | 61 | [[maybe_unused]] static int m256_hadd(__m256i sum, int bias) { 62 | __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); 63 | sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC)); 64 | sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB)); 65 | return _mm_cvtsi128_si32(sum128) + bias; 66 | } 67 | 68 | [[maybe_unused]] static void m256_add_dpbusd_epi32(__m256i& acc, __m256i a, __m256i b) { 69 | 70 | #if defined(USE_VNNI) 71 | acc = _mm256_dpbusd_epi32(acc, a, b); 72 | #else 73 | __m256i product0 = _mm256_maddubs_epi16(a, b); 74 | product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); 75 | acc = _mm256_add_epi32(acc, product0); 76 | #endif 77 | } 78 | 79 | #endif 80 | 81 | #if defined(USE_SSSE3) 82 | 83 | [[maybe_unused]] static int m128_hadd(__m128i sum, int bias) { 84 | sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC 85 | sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB 86 | return _mm_cvtsi128_si32(sum) + bias; 87 | } 88 | 89 | [[maybe_unused]] static void m128_add_dpbusd_epi32(__m128i& acc, __m128i a, __m128i b) { 90 | 91 | __m128i product0 = _mm_maddubs_epi16(a, b); 92 | product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); 93 | acc = _mm_add_epi32(acc, product0); 94 | } 95 | 96 | #endif 97 | 98 | #if defined(USE_NEON_DOTPROD) 99 | 100 | [[maybe_unused]] static void 101 | dotprod_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) { 102 | 103 | acc = vdotq_s32(acc, a, b); 104 | } 105 | #endif 106 | 107 | #if defined(USE_NEON) 108 | 109 | [[maybe_unused]] static int neon_m128_reduce_add_epi32(int32x4_t s) { 110 | #if USE_NEON >= 8 111 | return vaddvq_s32(s); 112 | #else 113 | return s[0] + s[1] + s[2] + s[3]; 114 | #endif 115 | } 116 | 117 | [[maybe_unused]] static int neon_m128_hadd(int32x4_t sum, int bias) { 118 | return neon_m128_reduce_add_epi32(sum) + bias; 119 | } 120 | 121 | #endif 122 | 123 | #if USE_NEON >= 8 124 | [[maybe_unused]] static void neon_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) { 125 | 126 | int16x8_t product0 = vmull_s8(vget_low_s8(a), vget_low_s8(b)); 127 | int16x8_t product1 = vmull_high_s8(a, b); 128 | int16x8_t sum = vpaddq_s16(product0, product1); 129 | acc = vpadalq_s16(acc, sum); 130 | } 131 | #endif 132 | } 133 | 134 | #endif // STOCKFISH_SIMD_H_INCLUDED 135 | -------------------------------------------------------------------------------- /src/nnue/layers/sqr_clipped_relu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 25 | #include 26 | #include 27 | 28 | #include "../nnue_common.h" 29 | 30 | namespace Stockfish::Eval::NNUE::Layers { 31 | 32 | // Clipped ReLU 33 | template 34 | class SqrClippedReLU { 35 | public: 36 | // Input/output type 37 | using InputType = std::int32_t; 38 | using OutputType = std::uint8_t; 39 | 40 | // Number of input/output dimensions 41 | static constexpr IndexType InputDimensions = InDims; 42 | static constexpr IndexType OutputDimensions = InputDimensions; 43 | static constexpr IndexType PaddedOutputDimensions = 44 | ceil_to_multiple(OutputDimensions, 32); 45 | 46 | using OutputBuffer = OutputType[PaddedOutputDimensions]; 47 | 48 | // Hash value embedded in the evaluation file 49 | static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { 50 | std::uint32_t hashValue = 0x538D24C7u; 51 | hashValue += prevHash; 52 | return hashValue; 53 | } 54 | 55 | // Read network parameters 56 | bool read_parameters(std::istream&) { return true; } 57 | 58 | // Write network parameters 59 | bool write_parameters(std::ostream&) const { return true; } 60 | 61 | // Forward propagation 62 | void propagate(const InputType* input, OutputType* output) const { 63 | 64 | #if defined(USE_SSE2) 65 | constexpr IndexType NumChunks = InputDimensions / 16; 66 | 67 | static_assert(WeightScaleBits == 6); 68 | const auto in = reinterpret_cast(input); 69 | const auto out = reinterpret_cast<__m128i*>(output); 70 | for (IndexType i = 0; i < NumChunks; ++i) 71 | { 72 | __m128i words0 = 73 | _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1])); 74 | __m128i words1 = 75 | _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])); 76 | 77 | // We shift by WeightScaleBits * 2 = 12 and divide by 128 78 | // which is an additional shift-right of 7, meaning 19 in total. 79 | // MulHi strips the lower 16 bits so we need to shift out 3 more to match. 80 | words0 = _mm_srli_epi16(_mm_mulhi_epi16(words0, words0), 3); 81 | words1 = _mm_srli_epi16(_mm_mulhi_epi16(words1, words1), 3); 82 | 83 | _mm_store_si128(&out[i], _mm_packs_epi16(words0, words1)); 84 | } 85 | constexpr IndexType Start = NumChunks * 16; 86 | 87 | #else 88 | constexpr IndexType Start = 0; 89 | #endif 90 | 91 | for (IndexType i = Start; i < InputDimensions; ++i) 92 | { 93 | output[i] = static_cast( 94 | // Really should be /127 but we need to make it fast so we right-shift 95 | // by an extra 7 bits instead. Needs to be accounted for in the trainer. 96 | std::min(127ll, ((long long) (input[i]) * input[i]) >> (2 * WeightScaleBits + 7))); 97 | } 98 | } 99 | }; 100 | 101 | } // namespace Stockfish::Eval::NNUE::Layers 102 | 103 | #endif // NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED 104 | -------------------------------------------------------------------------------- /src/nnue/network.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 NETWORK_H_INCLUDED 20 | #define NETWORK_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "../memory.h" 32 | #include "../types.h" 33 | #include "nnue_accumulator.h" 34 | #include "nnue_architecture.h" 35 | #include "nnue_feature_transformer.h" 36 | #include "nnue_misc.h" 37 | 38 | namespace Stockfish { 39 | class Position; 40 | } 41 | 42 | namespace Stockfish::Eval::NNUE { 43 | 44 | enum class EmbeddedNNUEType { 45 | BIG, 46 | SMALL, 47 | }; 48 | 49 | using NetworkOutput = std::tuple; 50 | 51 | template 52 | class Network { 53 | static constexpr IndexType FTDimensions = Arch::TransformedFeatureDimensions; 54 | 55 | public: 56 | Network(EvalFile file, EmbeddedNNUEType type) : 57 | evalFile(file), 58 | embeddedType(type) {} 59 | 60 | Network(const Network& other); 61 | Network(Network&& other) = default; 62 | 63 | Network& operator=(const Network& other); 64 | Network& operator=(Network&& other) = default; 65 | 66 | void load(const std::string& rootDirectory, std::string evalfilePath); 67 | bool save(const std::optional& filename) const; 68 | 69 | NetworkOutput evaluate(const Position& pos, 70 | AccumulatorStack& accumulatorStack, 71 | AccumulatorCaches::Cache* cache) const; 72 | 73 | 74 | void verify(std::string evalfilePath, const std::function&) const; 75 | NnueEvalTrace trace_evaluate(const Position& pos, 76 | AccumulatorStack& accumulatorStack, 77 | AccumulatorCaches::Cache* cache) const; 78 | 79 | private: 80 | void load_user_net(const std::string&, const std::string&); 81 | void load_internal(); 82 | 83 | void initialize(); 84 | 85 | bool save(std::ostream&, const std::string&, const std::string&) const; 86 | std::optional load(std::istream&); 87 | 88 | bool read_header(std::istream&, std::uint32_t*, std::string*) const; 89 | bool write_header(std::ostream&, std::uint32_t, const std::string&) const; 90 | 91 | bool read_parameters(std::istream&, std::string&) const; 92 | bool write_parameters(std::ostream&, const std::string&) const; 93 | 94 | // Input feature converter 95 | LargePagePtr featureTransformer; 96 | 97 | // Evaluation function 98 | AlignedPtr network; 99 | 100 | EvalFile evalFile; 101 | EmbeddedNNUEType embeddedType; 102 | 103 | // Hash value of evaluation function structure 104 | static constexpr std::uint32_t hash = Transformer::get_hash_value() ^ Arch::get_hash_value(); 105 | 106 | template 107 | friend struct AccumulatorCaches::Cache; 108 | 109 | friend class AccumulatorStack; 110 | }; 111 | 112 | // Definitions of the network types 113 | using SmallFeatureTransformer = FeatureTransformer; 114 | using SmallNetworkArchitecture = 115 | NetworkArchitecture; 116 | 117 | using BigFeatureTransformer = FeatureTransformer; 118 | using BigNetworkArchitecture = NetworkArchitecture; 119 | 120 | using NetworkBig = Network; 121 | using NetworkSmall = Network; 122 | 123 | 124 | struct Networks { 125 | Networks(NetworkBig&& nB, NetworkSmall&& nS) : 126 | big(std::move(nB)), 127 | small(std::move(nS)) {} 128 | 129 | NetworkBig big; 130 | NetworkSmall small; 131 | }; 132 | 133 | 134 | } // namespace Stockfish 135 | 136 | #endif 137 | -------------------------------------------------------------------------------- /src/nnue/nnue_accumulator.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "../types.h" 31 | #include "nnue_architecture.h" 32 | #include "nnue_common.h" 33 | 34 | namespace Stockfish { 35 | class Position; 36 | } 37 | 38 | namespace Stockfish::Eval::NNUE { 39 | 40 | using BiasType = std::int16_t; 41 | using PSQTWeightType = std::int32_t; 42 | using IndexType = std::uint32_t; 43 | 44 | template 45 | struct alignas(CacheLineSize) Accumulator; 46 | 47 | template 48 | class FeatureTransformer; 49 | 50 | // Class that holds the result of affine transformation of input features 51 | template 52 | struct alignas(CacheLineSize) Accumulator { 53 | std::int16_t accumulation[COLOR_NB][Size]; 54 | std::int32_t psqtAccumulation[COLOR_NB][PSQTBuckets]; 55 | std::array computed; 56 | }; 57 | 58 | 59 | // AccumulatorCaches struct provides per-thread accumulator caches, where each 60 | // cache contains multiple entries for each of the possible king squares. 61 | // When the accumulator needs to be refreshed, the cached entry is used to more 62 | // efficiently update the accumulator, instead of rebuilding it from scratch. 63 | // This idea, was first described by Luecx (author of Koivisto) and 64 | // is commonly referred to as "Finny Tables". 65 | struct AccumulatorCaches { 66 | 67 | template 68 | AccumulatorCaches(const Networks& networks) { 69 | clear(networks); 70 | } 71 | 72 | template 73 | struct alignas(CacheLineSize) Cache { 74 | 75 | struct alignas(CacheLineSize) Entry { 76 | BiasType accumulation[Size]; 77 | PSQTWeightType psqtAccumulation[PSQTBuckets]; 78 | Bitboard byColorBB[COLOR_NB]; 79 | Bitboard byTypeBB[PIECE_TYPE_NB]; 80 | 81 | // To initialize a refresh entry, we set all its bitboards empty, 82 | // so we put the biases in the accumulation, without any weights on top 83 | void clear(const BiasType* biases) { 84 | 85 | std::memcpy(accumulation, biases, sizeof(accumulation)); 86 | std::memset((uint8_t*) this + offsetof(Entry, psqtAccumulation), 0, 87 | sizeof(Entry) - offsetof(Entry, psqtAccumulation)); 88 | } 89 | }; 90 | 91 | template 92 | void clear(const Network& network) { 93 | for (auto& entries1D : entries) 94 | for (auto& entry : entries1D) 95 | entry.clear(network.featureTransformer->biases); 96 | } 97 | 98 | std::array& operator[](Square sq) { return entries[sq]; } 99 | 100 | std::array, SQUARE_NB> entries; 101 | }; 102 | 103 | template 104 | void clear(const Networks& networks) { 105 | big.clear(networks.big); 106 | small.clear(networks.small); 107 | } 108 | 109 | Cache big; 110 | Cache small; 111 | }; 112 | 113 | 114 | struct AccumulatorState { 115 | Accumulator accumulatorBig; 116 | Accumulator accumulatorSmall; 117 | DirtyPiece dirtyPiece; 118 | 119 | template 120 | auto& acc() noexcept { 121 | static_assert(Size == TransformedFeatureDimensionsBig 122 | || Size == TransformedFeatureDimensionsSmall, 123 | "Invalid size for accumulator"); 124 | 125 | if constexpr (Size == TransformedFeatureDimensionsBig) 126 | return accumulatorBig; 127 | else if constexpr (Size == TransformedFeatureDimensionsSmall) 128 | return accumulatorSmall; 129 | } 130 | 131 | template 132 | const auto& acc() const noexcept { 133 | static_assert(Size == TransformedFeatureDimensionsBig 134 | || Size == TransformedFeatureDimensionsSmall, 135 | "Invalid size for accumulator"); 136 | 137 | if constexpr (Size == TransformedFeatureDimensionsBig) 138 | return accumulatorBig; 139 | else if constexpr (Size == TransformedFeatureDimensionsSmall) 140 | return accumulatorSmall; 141 | } 142 | 143 | void reset(const DirtyPiece& dp) noexcept; 144 | }; 145 | 146 | 147 | class AccumulatorStack { 148 | public: 149 | AccumulatorStack() : 150 | accumulators(MAX_PLY + 1), 151 | size{1} {} 152 | 153 | [[nodiscard]] const AccumulatorState& latest() const noexcept; 154 | 155 | void reset() noexcept; 156 | void push(const DirtyPiece& dirtyPiece) noexcept; 157 | void pop() noexcept; 158 | 159 | template 160 | void evaluate(const Position& pos, 161 | const FeatureTransformer& featureTransformer, 162 | AccumulatorCaches::Cache& cache) noexcept; 163 | 164 | private: 165 | [[nodiscard]] AccumulatorState& mut_latest() noexcept; 166 | 167 | template 168 | void evaluate_side(const Position& pos, 169 | const FeatureTransformer& featureTransformer, 170 | AccumulatorCaches::Cache& cache) noexcept; 171 | 172 | template 173 | [[nodiscard]] std::size_t find_last_usable_accumulator() const noexcept; 174 | 175 | template 176 | void forward_update_incremental(const Position& pos, 177 | const FeatureTransformer& featureTransformer, 178 | const std::size_t begin) noexcept; 179 | 180 | template 181 | void backward_update_incremental(const Position& pos, 182 | const FeatureTransformer& featureTransformer, 183 | const std::size_t end) noexcept; 184 | 185 | std::vector accumulators; 186 | std::size_t size; 187 | }; 188 | 189 | } // namespace Stockfish::Eval::NNUE 190 | 191 | #endif // NNUE_ACCUMULATOR_H_INCLUDED 192 | -------------------------------------------------------------------------------- /src/nnue/nnue_architecture.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 | #include 26 | #include 27 | 28 | #include "features/half_ka_v2_hm.h" 29 | #include "layers/affine_transform.h" 30 | #include "layers/affine_transform_sparse_input.h" 31 | #include "layers/clipped_relu.h" 32 | #include "layers/sqr_clipped_relu.h" 33 | #include "nnue_common.h" 34 | 35 | namespace Stockfish::Eval::NNUE { 36 | 37 | // Input features used in evaluation function 38 | using FeatureSet = Features::HalfKAv2_hm; 39 | 40 | // Number of input feature dimensions after conversion 41 | constexpr IndexType TransformedFeatureDimensionsBig = 3072; 42 | constexpr int L2Big = 15; 43 | constexpr int L3Big = 32; 44 | 45 | constexpr IndexType TransformedFeatureDimensionsSmall = 128; 46 | constexpr int L2Small = 15; 47 | constexpr int L3Small = 32; 48 | 49 | constexpr IndexType PSQTBuckets = 8; 50 | constexpr IndexType LayerStacks = 8; 51 | 52 | template 53 | struct NetworkArchitecture { 54 | static constexpr IndexType TransformedFeatureDimensions = L1; 55 | static constexpr int FC_0_OUTPUTS = L2; 56 | static constexpr int FC_1_OUTPUTS = L3; 57 | 58 | Layers::AffineTransformSparseInput fc_0; 59 | Layers::SqrClippedReLU ac_sqr_0; 60 | Layers::ClippedReLU ac_0; 61 | Layers::AffineTransform fc_1; 62 | Layers::ClippedReLU ac_1; 63 | Layers::AffineTransform fc_2; 64 | 65 | // Hash value embedded in the evaluation file 66 | static constexpr std::uint32_t get_hash_value() { 67 | // input slice hash 68 | std::uint32_t hashValue = 0xEC42E90Du; 69 | hashValue ^= TransformedFeatureDimensions * 2; 70 | 71 | hashValue = decltype(fc_0)::get_hash_value(hashValue); 72 | hashValue = decltype(ac_0)::get_hash_value(hashValue); 73 | hashValue = decltype(fc_1)::get_hash_value(hashValue); 74 | hashValue = decltype(ac_1)::get_hash_value(hashValue); 75 | hashValue = decltype(fc_2)::get_hash_value(hashValue); 76 | 77 | return hashValue; 78 | } 79 | 80 | // Read network parameters 81 | bool read_parameters(std::istream& stream) { 82 | return fc_0.read_parameters(stream) && ac_0.read_parameters(stream) 83 | && fc_1.read_parameters(stream) && ac_1.read_parameters(stream) 84 | && fc_2.read_parameters(stream); 85 | } 86 | 87 | // Write network parameters 88 | bool write_parameters(std::ostream& stream) const { 89 | return fc_0.write_parameters(stream) && ac_0.write_parameters(stream) 90 | && fc_1.write_parameters(stream) && ac_1.write_parameters(stream) 91 | && fc_2.write_parameters(stream); 92 | } 93 | 94 | std::int32_t propagate(const TransformedFeatureType* transformedFeatures) { 95 | struct alignas(CacheLineSize) Buffer { 96 | alignas(CacheLineSize) typename decltype(fc_0)::OutputBuffer fc_0_out; 97 | alignas(CacheLineSize) typename decltype(ac_sqr_0)::OutputType 98 | ac_sqr_0_out[ceil_to_multiple(FC_0_OUTPUTS * 2, 32)]; 99 | alignas(CacheLineSize) typename decltype(ac_0)::OutputBuffer ac_0_out; 100 | alignas(CacheLineSize) typename decltype(fc_1)::OutputBuffer fc_1_out; 101 | alignas(CacheLineSize) typename decltype(ac_1)::OutputBuffer ac_1_out; 102 | alignas(CacheLineSize) typename decltype(fc_2)::OutputBuffer fc_2_out; 103 | 104 | Buffer() { std::memset(this, 0, sizeof(*this)); } 105 | }; 106 | 107 | #if defined(__clang__) && (__APPLE__) 108 | // workaround for a bug reported with xcode 12 109 | static thread_local auto tlsBuffer = std::make_unique(); 110 | // Access TLS only once, cache result. 111 | Buffer& buffer = *tlsBuffer; 112 | #else 113 | alignas(CacheLineSize) static thread_local Buffer buffer; 114 | #endif 115 | 116 | fc_0.propagate(transformedFeatures, buffer.fc_0_out); 117 | ac_sqr_0.propagate(buffer.fc_0_out, buffer.ac_sqr_0_out); 118 | ac_0.propagate(buffer.fc_0_out, buffer.ac_0_out); 119 | std::memcpy(buffer.ac_sqr_0_out + FC_0_OUTPUTS, buffer.ac_0_out, 120 | FC_0_OUTPUTS * sizeof(typename decltype(ac_0)::OutputType)); 121 | fc_1.propagate(buffer.ac_sqr_0_out, buffer.fc_1_out); 122 | ac_1.propagate(buffer.fc_1_out, buffer.ac_1_out); 123 | fc_2.propagate(buffer.ac_1_out, buffer.fc_2_out); 124 | 125 | // buffer.fc_0_out[FC_0_OUTPUTS] is such that 1.0 is equal to 127*(1<. 17 | */ 18 | 19 | // Code for calculating NNUE evaluation function 20 | 21 | #include "nnue_misc.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "../position.h" 34 | #include "../types.h" 35 | #include "../uci.h" 36 | #include "network.h" 37 | #include "nnue_accumulator.h" 38 | 39 | namespace Stockfish::Eval::NNUE { 40 | 41 | 42 | constexpr std::string_view PieceToChar(" PNBRQK pnbrqk"); 43 | 44 | 45 | namespace { 46 | // Converts a Value into (centi)pawns and writes it in a buffer. 47 | // The buffer must have capacity for at least 5 chars. 48 | void format_cp_compact(Value v, char* buffer, const Position& pos) { 49 | 50 | buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); 51 | 52 | int cp = std::abs(UCIEngine::to_cp(v, pos)); 53 | if (cp >= 10000) 54 | { 55 | buffer[1] = '0' + cp / 10000; 56 | cp %= 10000; 57 | buffer[2] = '0' + cp / 1000; 58 | cp %= 1000; 59 | buffer[3] = '0' + cp / 100; 60 | buffer[4] = ' '; 61 | } 62 | else if (cp >= 1000) 63 | { 64 | buffer[1] = '0' + cp / 1000; 65 | cp %= 1000; 66 | buffer[2] = '0' + cp / 100; 67 | cp %= 100; 68 | buffer[3] = '.'; 69 | buffer[4] = '0' + cp / 10; 70 | } 71 | else 72 | { 73 | buffer[1] = '0' + cp / 100; 74 | cp %= 100; 75 | buffer[2] = '.'; 76 | buffer[3] = '0' + cp / 10; 77 | cp %= 10; 78 | buffer[4] = '0' + cp / 1; 79 | } 80 | } 81 | 82 | 83 | // Converts a Value into pawns, always keeping two decimals 84 | void format_cp_aligned_dot(Value v, std::stringstream& stream, const Position& pos) { 85 | 86 | const double pawns = std::abs(0.01 * UCIEngine::to_cp(v, pos)); 87 | 88 | stream << (v < 0 ? '-' 89 | : v > 0 ? '+' 90 | : ' ') 91 | << std::setiosflags(std::ios::fixed) << std::setw(6) << std::setprecision(2) << pawns; 92 | } 93 | } 94 | 95 | 96 | // Returns a string with the value of each piece on a board, 97 | // and a table for (PSQT, Layers) values bucket by bucket. 98 | std::string 99 | trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::AccumulatorCaches& caches) { 100 | 101 | std::stringstream ss; 102 | 103 | char board[3 * 8 + 1][8 * 8 + 2]; 104 | std::memset(board, ' ', sizeof(board)); 105 | for (int row = 0; row < 3 * 8 + 1; ++row) 106 | board[row][8 * 8 + 1] = '\0'; 107 | 108 | // A lambda to output one box of the board 109 | auto writeSquare = [&board, &pos](File file, Rank rank, Piece pc, Value value) { 110 | const int x = int(file) * 8; 111 | const int y = (7 - int(rank)) * 3; 112 | for (int i = 1; i < 8; ++i) 113 | board[y][x + i] = board[y + 3][x + i] = '-'; 114 | for (int i = 1; i < 3; ++i) 115 | board[y + i][x] = board[y + i][x + 8] = '|'; 116 | board[y][x] = board[y][x + 8] = board[y + 3][x + 8] = board[y + 3][x] = '+'; 117 | if (pc != NO_PIECE) 118 | board[y + 1][x + 4] = PieceToChar[pc]; 119 | if (is_valid(value)) 120 | format_cp_compact(value, &board[y + 2][x + 2], pos); 121 | }; 122 | 123 | AccumulatorStack accumulators; 124 | 125 | // We estimate the value of each piece by doing a differential evaluation from 126 | // the current base eval, simulating the removal of the piece from its square. 127 | auto [psqt, positional] = networks.big.evaluate(pos, accumulators, &caches.big); 128 | Value base = psqt + positional; 129 | base = pos.side_to_move() == WHITE ? base : -base; 130 | 131 | for (File f = FILE_A; f <= FILE_H; ++f) 132 | for (Rank r = RANK_1; r <= RANK_8; ++r) 133 | { 134 | Square sq = make_square(f, r); 135 | Piece pc = pos.piece_on(sq); 136 | Value v = VALUE_NONE; 137 | 138 | if (pc != NO_PIECE && type_of(pc) != KING) 139 | { 140 | pos.remove_piece(sq); 141 | 142 | accumulators.reset(); 143 | std::tie(psqt, positional) = networks.big.evaluate(pos, accumulators, &caches.big); 144 | Value eval = psqt + positional; 145 | eval = pos.side_to_move() == WHITE ? eval : -eval; 146 | v = base - eval; 147 | 148 | pos.put_piece(pc, sq); 149 | } 150 | 151 | writeSquare(f, r, pc, v); 152 | } 153 | 154 | ss << " NNUE derived piece values:\n"; 155 | for (int row = 0; row < 3 * 8 + 1; ++row) 156 | ss << board[row] << '\n'; 157 | ss << '\n'; 158 | 159 | accumulators.reset(); 160 | auto t = networks.big.trace_evaluate(pos, accumulators, &caches.big); 161 | 162 | ss << " NNUE network contributions " 163 | << (pos.side_to_move() == WHITE ? "(White to move)" : "(Black to move)") << std::endl 164 | << "+------------+------------+------------+------------+\n" 165 | << "| Bucket | Material | Positional | Total |\n" 166 | << "| | (PSQT) | (Layers) | |\n" 167 | << "+------------+------------+------------+------------+\n"; 168 | 169 | for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket) 170 | { 171 | ss << "| " << bucket << " " // 172 | << " | "; 173 | format_cp_aligned_dot(t.psqt[bucket], ss, pos); 174 | ss << " " // 175 | << " | "; 176 | format_cp_aligned_dot(t.positional[bucket], ss, pos); 177 | ss << " " // 178 | << " | "; 179 | format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss, pos); 180 | ss << " " // 181 | << " |"; 182 | if (bucket == t.correctBucket) 183 | ss << " <-- this bucket is used"; 184 | ss << '\n'; 185 | } 186 | 187 | ss << "+------------+------------+------------+------------+\n"; 188 | 189 | return ss.str(); 190 | } 191 | 192 | 193 | } // namespace Stockfish::Eval::NNUE 194 | -------------------------------------------------------------------------------- /src/nnue/nnue_misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 NNUE_MISC_H_INCLUDED 20 | #define NNUE_MISC_H_INCLUDED 21 | 22 | #include 23 | #include 24 | 25 | #include "../types.h" 26 | #include "nnue_architecture.h" 27 | 28 | namespace Stockfish { 29 | 30 | class Position; 31 | 32 | namespace Eval::NNUE { 33 | 34 | struct EvalFile { 35 | // Default net name, will use one of the EvalFileDefaultName* macros defined 36 | // in evaluate.h 37 | std::string defaultName; 38 | // Selected net name, either via uci option or default 39 | std::string current; 40 | // Net description extracted from the net file 41 | std::string netDescription; 42 | }; 43 | 44 | 45 | struct NnueEvalTrace { 46 | static_assert(LayerStacks == PSQTBuckets); 47 | 48 | Value psqt[LayerStacks]; 49 | Value positional[LayerStacks]; 50 | std::size_t correctBucket; 51 | }; 52 | 53 | struct Networks; 54 | struct AccumulatorCaches; 55 | 56 | std::string trace(Position& pos, const Networks& networks, AccumulatorCaches& caches); 57 | 58 | } // namespace Stockfish::Eval::NNUE 59 | } // namespace Stockfish 60 | 61 | #endif // #ifndef NNUE_MISC_H_INCLUDED 62 | -------------------------------------------------------------------------------- /src/perft.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 PERFT_H_INCLUDED 20 | #define PERFT_H_INCLUDED 21 | 22 | #include 23 | 24 | #include "movegen.h" 25 | #include "position.h" 26 | #include "types.h" 27 | #include "uci.h" 28 | 29 | namespace Stockfish::Benchmark { 30 | 31 | // Utility to verify move generation. All the leaf nodes up 32 | // to the given depth are generated and counted, and the sum is returned. 33 | template 34 | uint64_t perft(Position& pos, Depth depth) { 35 | 36 | StateInfo st; 37 | 38 | uint64_t cnt, nodes = 0; 39 | const bool leaf = (depth == 2); 40 | 41 | for (const auto& m : MoveList(pos)) 42 | { 43 | if (Root && depth <= 1) 44 | cnt = 1, nodes++; 45 | else 46 | { 47 | pos.do_move(m, st); 48 | cnt = leaf ? MoveList(pos).size() : perft(pos, depth - 1); 49 | nodes += cnt; 50 | pos.undo_move(m); 51 | } 52 | if (Root) 53 | sync_cout << UCIEngine::move(m, pos.is_chess960()) << ": " << cnt << sync_endl; 54 | } 55 | return nodes; 56 | } 57 | 58 | inline uint64_t perft(const std::string& fen, Depth depth, bool isChess960) { 59 | StateListPtr states(new std::deque(1)); 60 | Position p; 61 | p.set(fen, isChess960, &states->back()); 62 | 63 | return perft(p, depth); 64 | } 65 | } 66 | 67 | #endif // PERFT_H_INCLUDED 68 | -------------------------------------------------------------------------------- /src/score.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 "score.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "uci.h" 26 | 27 | namespace Stockfish { 28 | 29 | Score::Score(Value v, const Position& pos) { 30 | assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); 31 | 32 | if (!is_decisive(v)) 33 | { 34 | score = InternalUnits{UCIEngine::to_cp(v, pos)}; 35 | } 36 | else if (std::abs(v) <= VALUE_TB) 37 | { 38 | auto distance = VALUE_TB - std::abs(v); 39 | score = (v > 0) ? Tablebase{distance, true} : Tablebase{-distance, false}; 40 | } 41 | else 42 | { 43 | auto distance = VALUE_MATE - std::abs(v); 44 | score = (v > 0) ? Mate{distance} : Mate{-distance}; 45 | } 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /src/score.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 SCORE_H_INCLUDED 20 | #define SCORE_H_INCLUDED 21 | 22 | #include 23 | #include 24 | 25 | #include "types.h" 26 | 27 | namespace Stockfish { 28 | 29 | class Position; 30 | 31 | class Score { 32 | public: 33 | struct Mate { 34 | int plies; 35 | }; 36 | 37 | struct Tablebase { 38 | int plies; 39 | bool win; 40 | }; 41 | 42 | struct InternalUnits { 43 | int value; 44 | }; 45 | 46 | Score() = default; 47 | Score(Value v, const Position& pos); 48 | 49 | template 50 | bool is() const { 51 | return std::holds_alternative(score); 52 | } 53 | 54 | template 55 | T get() const { 56 | return std::get(score); 57 | } 58 | 59 | template 60 | decltype(auto) visit(F&& f) const { 61 | return std::visit(std::forward(f), score); 62 | } 63 | 64 | private: 65 | std::variant score; 66 | }; 67 | 68 | } 69 | 70 | #endif // #ifndef SCORE_H_INCLUDED 71 | -------------------------------------------------------------------------------- /src/syzygy/tbprobe.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 | #include 24 | 25 | 26 | namespace Stockfish { 27 | class Position; 28 | class OptionsMap; 29 | 30 | using Depth = int; 31 | 32 | namespace Search { 33 | struct RootMove; 34 | using RootMoves = std::vector; 35 | } 36 | } 37 | 38 | namespace Stockfish::Tablebases { 39 | 40 | struct Config { 41 | int cardinality = 0; 42 | bool rootInTB = false; 43 | bool useRule50 = false; 44 | Depth probeDepth = 0; 45 | }; 46 | 47 | enum WDLScore { 48 | WDLLoss = -2, // Loss 49 | WDLBlessedLoss = -1, // Loss, but draw under 50-move rule 50 | WDLDraw = 0, // Draw 51 | WDLCursedWin = 1, // Win, but draw under 50-move rule 52 | WDLWin = 2, // Win 53 | }; 54 | 55 | // Possible states after a probing operation 56 | enum ProbeState { 57 | FAIL = 0, // Probe failed (missing file table) 58 | OK = 1, // Probe successful 59 | CHANGE_STM = -1, // DTZ should check the other side 60 | ZEROING_BEST_MOVE = 2 // Best move zeroes DTZ (capture or pawn move) 61 | }; 62 | 63 | extern int MaxCardinality; 64 | 65 | 66 | void init(const std::string& paths); 67 | WDLScore probe_wdl(Position& pos, ProbeState* result); 68 | int probe_dtz(Position& pos, ProbeState* result); 69 | bool root_probe(Position& pos, Search::RootMoves& rootMoves, bool rule50, bool rankDTZ); 70 | bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, bool rule50); 71 | Config rank_root_moves(const OptionsMap& options, 72 | Position& pos, 73 | Search::RootMoves& rootMoves, 74 | bool rankDTZ = false); 75 | 76 | } // namespace Stockfish::Tablebases 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 | #include 28 | #include 29 | #include 30 | 31 | #include "numa.h" 32 | #include "position.h" 33 | #include "search.h" 34 | #include "thread_win32_osx.h" 35 | 36 | namespace Stockfish { 37 | 38 | 39 | class OptionsMap; 40 | using Value = int; 41 | 42 | // Sometimes we don't want to actually bind the threads, but the recipient still 43 | // needs to think it runs on *some* NUMA node, such that it can access structures 44 | // that rely on NUMA node knowledge. This class encapsulates this optional process 45 | // such that the recipient does not need to know whether the binding happened or not. 46 | class OptionalThreadToNumaNodeBinder { 47 | public: 48 | OptionalThreadToNumaNodeBinder(NumaIndex n) : 49 | numaConfig(nullptr), 50 | numaId(n) {} 51 | 52 | OptionalThreadToNumaNodeBinder(const NumaConfig& cfg, NumaIndex n) : 53 | numaConfig(&cfg), 54 | numaId(n) {} 55 | 56 | NumaReplicatedAccessToken operator()() const { 57 | if (numaConfig != nullptr) 58 | return numaConfig->bind_current_thread_to_numa_node(numaId); 59 | else 60 | return NumaReplicatedAccessToken(numaId); 61 | } 62 | 63 | private: 64 | const NumaConfig* numaConfig; 65 | NumaIndex numaId; 66 | }; 67 | 68 | // Abstraction of a thread. It contains a pointer to the worker and a native thread. 69 | // After construction, the native thread is started with idle_loop() 70 | // waiting for a signal to start searching. 71 | // When the signal is received, the thread starts searching and when 72 | // the search is finished, it goes back to idle_loop() waiting for a new signal. 73 | class Thread { 74 | public: 75 | Thread(Search::SharedState&, 76 | std::unique_ptr, 77 | size_t, 78 | OptionalThreadToNumaNodeBinder); 79 | virtual ~Thread(); 80 | 81 | void idle_loop(); 82 | void start_searching(); 83 | void clear_worker(); 84 | void run_custom_job(std::function f); 85 | 86 | void ensure_network_replicated(); 87 | 88 | // Thread has been slightly altered to allow running custom jobs, so 89 | // this name is no longer correct. However, this class (and ThreadPool) 90 | // require further work to make them properly generic while maintaining 91 | // appropriate specificity regarding search, from the point of view of an 92 | // outside user, so renaming of this function is left for whenever that happens. 93 | void wait_for_search_finished(); 94 | size_t id() const { return idx; } 95 | 96 | std::unique_ptr worker; 97 | std::function jobFunc; 98 | 99 | private: 100 | std::mutex mutex; 101 | std::condition_variable cv; 102 | size_t idx, nthreads; 103 | bool exit = false, searching = true; // Set before starting std::thread 104 | NativeThread stdThread; 105 | NumaReplicatedAccessToken numaAccessToken; 106 | }; 107 | 108 | 109 | // ThreadPool struct handles all the threads-related stuff like init, starting, 110 | // parking and, most importantly, launching a thread. All the access to threads 111 | // is done through this class. 112 | class ThreadPool { 113 | public: 114 | ThreadPool() {} 115 | 116 | ~ThreadPool() { 117 | // destroy any existing thread(s) 118 | if (threads.size() > 0) 119 | { 120 | main_thread()->wait_for_search_finished(); 121 | 122 | threads.clear(); 123 | } 124 | } 125 | 126 | ThreadPool(const ThreadPool&) = delete; 127 | ThreadPool(ThreadPool&&) = delete; 128 | 129 | ThreadPool& operator=(const ThreadPool&) = delete; 130 | ThreadPool& operator=(ThreadPool&&) = delete; 131 | 132 | void start_thinking(const OptionsMap&, Position&, StateListPtr&, Search::LimitsType); 133 | void run_on_thread(size_t threadId, std::function f); 134 | void wait_on_thread(size_t threadId); 135 | size_t num_threads() const; 136 | void clear(); 137 | void set(const NumaConfig& numaConfig, 138 | Search::SharedState, 139 | const Search::SearchManager::UpdateContext&); 140 | 141 | Search::SearchManager* main_manager(); 142 | Thread* main_thread() const { return threads.front().get(); } 143 | uint64_t nodes_searched() const; 144 | uint64_t tb_hits() const; 145 | Thread* get_best_thread() const; 146 | void start_searching(); 147 | void wait_for_search_finished() const; 148 | 149 | std::vector get_bound_thread_count_by_numa_node() const; 150 | 151 | void ensure_network_replicated(); 152 | 153 | std::atomic_bool stop, abortedSearch, increaseDepth; 154 | 155 | auto cbegin() const noexcept { return threads.cbegin(); } 156 | auto begin() noexcept { return threads.begin(); } 157 | auto end() noexcept { return threads.end(); } 158 | auto cend() const noexcept { return threads.cend(); } 159 | auto size() const noexcept { return threads.size(); } 160 | auto empty() const noexcept { return threads.empty(); } 161 | 162 | private: 163 | StateListPtr setupStates; 164 | std::vector> threads; 165 | std::vector boundThreadToNumaNode; 166 | 167 | uint64_t accumulate(std::atomic Search::Worker::*member) const { 168 | 169 | uint64_t sum = 0; 170 | for (auto&& th : threads) 171 | sum += (th->worker.get()->*member).load(std::memory_order_relaxed); 172 | return sum; 173 | } 174 | }; 175 | 176 | } // namespace Stockfish 177 | 178 | #endif // #ifndef THREAD_H_INCLUDED 179 | -------------------------------------------------------------------------------- /src/thread_win32_osx.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 | #include 34 | 35 | namespace Stockfish { 36 | 37 | class NativeThread { 38 | pthread_t thread; 39 | 40 | static constexpr size_t TH_STACK_SIZE = 8 * 1024 * 1024; 41 | 42 | public: 43 | template 44 | explicit NativeThread(Function&& fun, Args&&... args) { 45 | auto func = new std::function( 46 | std::bind(std::forward(fun), std::forward(args)...)); 47 | 48 | pthread_attr_t attr_storage, *attr = &attr_storage; 49 | pthread_attr_init(attr); 50 | pthread_attr_setstacksize(attr, TH_STACK_SIZE); 51 | 52 | auto start_routine = [](void* ptr) -> void* { 53 | auto f = reinterpret_cast*>(ptr); 54 | // Call the function 55 | (*f)(); 56 | delete f; 57 | return nullptr; 58 | }; 59 | 60 | pthread_create(&thread, attr, start_routine, func); 61 | } 62 | 63 | void join() { pthread_join(thread, nullptr); } 64 | }; 65 | 66 | } // namespace Stockfish 67 | 68 | #else // Default case: use STL classes 69 | 70 | namespace Stockfish { 71 | 72 | using NativeThread = std::thread; 73 | 74 | } // namespace Stockfish 75 | 76 | #endif 77 | 78 | #endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED 79 | -------------------------------------------------------------------------------- /src/timeman.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 "timeman.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "search.h" 27 | #include "ucioption.h" 28 | 29 | namespace Stockfish { 30 | 31 | TimePoint TimeManagement::optimum() const { return optimumTime; } 32 | TimePoint TimeManagement::maximum() const { return maximumTime; } 33 | 34 | void TimeManagement::clear() { 35 | availableNodes = -1; // When in 'nodes as time' mode 36 | } 37 | 38 | void TimeManagement::advance_nodes_time(std::int64_t nodes) { 39 | assert(useNodesTime); 40 | availableNodes = std::max(int64_t(0), availableNodes - nodes); 41 | } 42 | 43 | // Called at the beginning of the search and calculates 44 | // the bounds of time allowed for the current game ply. We currently support: 45 | // 1) x basetime (+ z increment) 46 | // 2) x moves in y seconds (+ z increment) 47 | void TimeManagement::init(Search::LimitsType& limits, 48 | Color us, 49 | int ply, 50 | const OptionsMap& options, 51 | double& originalTimeAdjust) { 52 | TimePoint npmsec = TimePoint(options["nodestime"]); 53 | 54 | // If we have no time, we don't need to fully initialize TM. 55 | // startTime is used by movetime and useNodesTime is used in elapsed calls. 56 | startTime = limits.startTime; 57 | useNodesTime = npmsec != 0; 58 | 59 | if (limits.time[us] == 0) 60 | return; 61 | 62 | TimePoint moveOverhead = TimePoint(options["Move Overhead"]); 63 | 64 | // optScale is a percentage of available time to use for the current move. 65 | // maxScale is a multiplier applied to optimumTime. 66 | double optScale, maxScale; 67 | 68 | // If we have to play in 'nodes as time' mode, then convert from time 69 | // to nodes, and use resulting values in time management formulas. 70 | // WARNING: to avoid time losses, the given npmsec (nodes per millisecond) 71 | // must be much lower than the real engine speed. 72 | if (useNodesTime) 73 | { 74 | if (availableNodes == -1) // Only once at game start 75 | availableNodes = npmsec * limits.time[us]; // Time is in msec 76 | 77 | // Convert from milliseconds to nodes 78 | limits.time[us] = TimePoint(availableNodes); 79 | limits.inc[us] *= npmsec; 80 | limits.npmsec = npmsec; 81 | moveOverhead *= npmsec; 82 | } 83 | 84 | // These numbers are used where multiplications, divisions or comparisons 85 | // with constants are involved. 86 | const int64_t scaleFactor = useNodesTime ? npmsec : 1; 87 | const TimePoint scaledTime = limits.time[us] / scaleFactor; 88 | const TimePoint scaledInc = limits.inc[us] / scaleFactor; 89 | 90 | // Maximum move horizon 91 | int centiMTG = limits.movestogo ? std::min(limits.movestogo * 100, 5000) : 5051; 92 | 93 | // If less than one second, gradually reduce mtg 94 | if (scaledTime < 1000 && double(centiMTG) / scaledInc > 5.051) 95 | { 96 | centiMTG = scaledTime * 5.051; 97 | } 98 | 99 | // Make sure timeLeft is > 0 since we may use it as a divisor 100 | TimePoint timeLeft = 101 | std::max(TimePoint(1), 102 | limits.time[us] 103 | + (limits.inc[us] * (centiMTG - 100) - moveOverhead * (200 + centiMTG)) / 100); 104 | 105 | // x basetime (+ z increment) 106 | // If there is a healthy increment, timeLeft can exceed the actual available 107 | // game time for the current move, so also cap to a percentage of available game time. 108 | if (limits.movestogo == 0) 109 | { 110 | // Extra time according to timeLeft 111 | if (originalTimeAdjust < 0) 112 | originalTimeAdjust = 0.3128 * std::log10(timeLeft) - 0.4354; 113 | 114 | // Calculate time constants based on current time left. 115 | double logTimeInSec = std::log10(scaledTime / 1000.0); 116 | double optConstant = std::min(0.0032116 + 0.000321123 * logTimeInSec, 0.00508017); 117 | double maxConstant = std::max(3.3977 + 3.03950 * logTimeInSec, 2.94761); 118 | 119 | optScale = std::min(0.0121431 + std::pow(ply + 2.94693, 0.461073) * optConstant, 120 | 0.213035 * limits.time[us] / timeLeft) 121 | * originalTimeAdjust; 122 | 123 | maxScale = std::min(6.67704, maxConstant + ply / 11.9847); 124 | } 125 | 126 | // x moves in y seconds (+ z increment) 127 | else 128 | { 129 | optScale = 130 | std::min((0.88 + ply / 116.4) / (centiMTG / 100.0), 0.88 * limits.time[us] / timeLeft); 131 | maxScale = 1.3 + 0.11 * (centiMTG / 100.0); 132 | } 133 | 134 | // Limit the maximum possible time for this move 135 | optimumTime = TimePoint(optScale * timeLeft); 136 | maximumTime = 137 | TimePoint(std::min(0.825179 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10; 138 | 139 | if (options["Ponder"]) 140 | optimumTime += optimumTime / 4; 141 | } 142 | 143 | } // namespace Stockfish 144 | -------------------------------------------------------------------------------- /src/timeman.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 23 | 24 | #include "misc.h" 25 | #include "types.h" 26 | 27 | namespace Stockfish { 28 | 29 | class OptionsMap; 30 | 31 | namespace Search { 32 | struct LimitsType; 33 | } 34 | 35 | // The TimeManagement class computes the optimal time to think depending on 36 | // the maximum available time, the game move number, and other parameters. 37 | class TimeManagement { 38 | public: 39 | void init(Search::LimitsType& limits, 40 | Color us, 41 | int ply, 42 | const OptionsMap& options, 43 | double& originalTimeAdjust); 44 | 45 | TimePoint optimum() const; 46 | TimePoint maximum() const; 47 | template 48 | TimePoint elapsed(FUNC nodes) const { 49 | return useNodesTime ? TimePoint(nodes()) : elapsed_time(); 50 | } 51 | TimePoint elapsed_time() const { return now() - startTime; }; 52 | 53 | void clear(); 54 | void advance_nodes_time(std::int64_t nodes); 55 | 56 | private: 57 | TimePoint startTime; 58 | TimePoint optimumTime; 59 | TimePoint maximumTime; 60 | 61 | std::int64_t availableNodes = -1; // When in 'nodes as time' mode 62 | bool useNodesTime = false; // True if we are in 'nodes as time' mode 63 | }; 64 | 65 | } // namespace Stockfish 66 | 67 | #endif // #ifndef TIMEMAN_H_INCLUDED 68 | -------------------------------------------------------------------------------- /src/tt.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 23 | #include 24 | #include 25 | 26 | #include "memory.h" 27 | #include "types.h" 28 | 29 | namespace Stockfish { 30 | 31 | class ThreadPool; 32 | struct TTEntry; 33 | struct Cluster; 34 | 35 | // There is only one global hash table for the engine and all its threads. For chess in particular, we even allow racy 36 | // updates between threads to and from the TT, as taking the time to synchronize access would cost thinking time and 37 | // thus elo. As a hash table, collisions are possible and may cause chess playing issues (bizarre blunders, faulty mate 38 | // reports, etc). Fixing these also loses elo; however such risk decreases quickly with larger TT size. 39 | // 40 | // `probe` is the primary method: given a board position, we lookup its entry in the table, and return a tuple of: 41 | // 1) whether the entry already has this position 42 | // 2) a copy of the prior data (if any) (may be inconsistent due to read races) 43 | // 3) a writer object to this entry 44 | // The copied data and the writer are separated to maintain clear boundaries between local vs global objects. 45 | 46 | 47 | // A copy of the data already in the entry (possibly collided). `probe` may be racy, resulting in inconsistent data. 48 | struct TTData { 49 | Move move; 50 | Value value, eval; 51 | Depth depth; 52 | Bound bound; 53 | bool is_pv; 54 | 55 | TTData() = delete; 56 | 57 | // clang-format off 58 | TTData(Move m, Value v, Value ev, Depth d, Bound b, bool pv) : 59 | move(m), 60 | value(v), 61 | eval(ev), 62 | depth(d), 63 | bound(b), 64 | is_pv(pv) {}; 65 | // clang-format on 66 | }; 67 | 68 | 69 | // This is used to make racy writes to the global TT. 70 | struct TTWriter { 71 | public: 72 | void write(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev, uint8_t generation8); 73 | 74 | private: 75 | friend class TranspositionTable; 76 | TTEntry* entry; 77 | TTWriter(TTEntry* tte); 78 | }; 79 | 80 | 81 | class TranspositionTable { 82 | 83 | public: 84 | ~TranspositionTable() { aligned_large_pages_free(table); } 85 | 86 | void resize(size_t mbSize, ThreadPool& threads); // Set TT size 87 | void clear(ThreadPool& threads); // Re-initialize memory, multithreaded 88 | int hashfull(int maxAge = 0) 89 | const; // Approximate what fraction of entries (permille) have been written to during this root search 90 | 91 | void 92 | new_search(); // This must be called at the beginning of each root search to track entry aging 93 | uint8_t generation() const; // The current age, used when writing new data to the TT 94 | std::tuple 95 | probe(const Key key) const; // The main method, whose retvals separate local vs global objects 96 | TTEntry* first_entry(const Key key) 97 | const; // This is the hash function; its only external use is memory prefetching. 98 | 99 | private: 100 | friend struct TTEntry; 101 | 102 | size_t clusterCount; 103 | Cluster* table = nullptr; 104 | 105 | uint8_t generation8 = 0; // Size must be not bigger than TTEntry::genBound8 106 | }; 107 | 108 | } // namespace Stockfish 109 | 110 | #endif // #ifndef TT_H_INCLUDED 111 | -------------------------------------------------------------------------------- /src/tune.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 "tune.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "ucioption.h" 29 | 30 | using std::string; 31 | 32 | namespace Stockfish { 33 | 34 | bool Tune::update_on_last; 35 | const Option* LastOption = nullptr; 36 | OptionsMap* Tune::options; 37 | namespace { 38 | std::map TuneResults; 39 | 40 | std::optional on_tune(const Option& o) { 41 | 42 | if (!Tune::update_on_last || LastOption == &o) 43 | Tune::read_options(); 44 | 45 | return std::nullopt; 46 | } 47 | } 48 | 49 | void Tune::make_option(OptionsMap* opts, const string& n, int v, const SetRange& r) { 50 | 51 | // Do not generate option when there is nothing to tune (ie. min = max) 52 | if (r(v).first == r(v).second) 53 | return; 54 | 55 | if (TuneResults.count(n)) 56 | v = TuneResults[n]; 57 | 58 | opts->add(n, Option(v, r(v).first, r(v).second, on_tune)); 59 | LastOption = &((*opts)[n]); 60 | 61 | // Print formatted parameters, ready to be copy-pasted in Fishtest 62 | std::cout << n << "," // 63 | << v << "," // 64 | << r(v).first << "," // 65 | << r(v).second << "," // 66 | << (r(v).second - r(v).first) / 20.0 << "," // 67 | << "0.0020" << std::endl; 68 | } 69 | 70 | string Tune::next(string& names, bool pop) { 71 | 72 | string name; 73 | 74 | do 75 | { 76 | string token = names.substr(0, names.find(',')); 77 | 78 | if (pop) 79 | names.erase(0, token.size() + 1); 80 | 81 | std::stringstream ws(token); 82 | name += (ws >> token, token); // Remove trailing whitespace 83 | 84 | } while (std::count(name.begin(), name.end(), '(') - std::count(name.begin(), name.end(), ')')); 85 | 86 | return name; 87 | } 88 | 89 | 90 | template<> 91 | void Tune::Entry::init_option() { 92 | make_option(options, name, value, range); 93 | } 94 | 95 | template<> 96 | void Tune::Entry::read_option() { 97 | if (options->count(name)) 98 | value = int((*options)[name]); 99 | } 100 | 101 | // Instead of a variable here we have a PostUpdate function: just call it 102 | template<> 103 | void Tune::Entry::init_option() {} 104 | template<> 105 | void Tune::Entry::read_option() { 106 | value(); 107 | } 108 | 109 | } // namespace Stockfish 110 | 111 | 112 | // Init options with tuning session results instead of default values. Useful to 113 | // get correct bench signature after a tuning session or to test tuned values. 114 | // Just copy fishtest tuning results in a result.txt file and extract the 115 | // values with: 116 | // 117 | // cat results.txt | sed 's/^param: \([^,]*\), best: \([^,]*\).*/ TuneResults["\1"] = int(round(\2));/' 118 | // 119 | // Then paste the output below, as the function body 120 | 121 | 122 | namespace Stockfish { 123 | 124 | void Tune::read_results() { /* ...insert your values here... */ } 125 | 126 | } // namespace Stockfish 127 | -------------------------------------------------------------------------------- /src/tune.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 // IWYU pragma: keep 26 | #include 27 | #include 28 | 29 | namespace Stockfish { 30 | 31 | class OptionsMap; 32 | 33 | using Range = std::pair; // Option's min-max values 34 | using RangeFun = Range(int); 35 | 36 | // Default Range function, to calculate Option's min-max values 37 | inline Range default_range(int v) { return v > 0 ? Range(0, 2 * v) : Range(2 * v, 0); } 38 | 39 | struct SetRange { 40 | explicit SetRange(RangeFun f) : 41 | fun(f) {} 42 | SetRange(int min, int max) : 43 | fun(nullptr), 44 | range(min, max) {} 45 | Range operator()(int v) const { return fun ? fun(v) : range; } 46 | 47 | RangeFun* fun; 48 | Range range; 49 | }; 50 | 51 | #define SetDefaultRange SetRange(default_range) 52 | 53 | 54 | // Tune class implements the 'magic' code that makes the setup of a fishtest tuning 55 | // session as easy as it can be. Mainly you have just to remove const qualifiers 56 | // from the variables you want to tune and flag them for tuning, so if you have: 57 | // 58 | // const Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } }; 59 | // 60 | // If you have a my_post_update() function to run after values have been updated, 61 | // and a my_range() function to set custom Option's min-max values, then you just 62 | // remove the 'const' qualifiers and write somewhere below in the file: 63 | // 64 | // TUNE(SetRange(my_range), myValue, my_post_update); 65 | // 66 | // You can also set the range directly, and restore the default at the end 67 | // 68 | // TUNE(SetRange(-100, 100), myValue, SetDefaultRange); 69 | // 70 | // In case update function is slow and you have many parameters, you can add: 71 | // 72 | // UPDATE_ON_LAST(); 73 | // 74 | // And the values update, including post update function call, will be done only 75 | // once, after the engine receives the last UCI option, that is the one defined 76 | // and created as the last one, so the GUI should send the options in the same 77 | // order in which have been defined. 78 | 79 | class Tune { 80 | 81 | using PostUpdate = void(); // Post-update function 82 | 83 | Tune() { read_results(); } 84 | Tune(const Tune&) = delete; 85 | void operator=(const Tune&) = delete; 86 | void read_results(); 87 | 88 | static Tune& instance() { 89 | static Tune t; 90 | return t; 91 | } // Singleton 92 | 93 | // Use polymorphism to accommodate Entry of different types in the same vector 94 | struct EntryBase { 95 | virtual ~EntryBase() = default; 96 | virtual void init_option() = 0; 97 | virtual void read_option() = 0; 98 | }; 99 | 100 | template 101 | struct Entry: public EntryBase { 102 | 103 | static_assert(!std::is_const_v, "Parameter cannot be const!"); 104 | 105 | static_assert(std::is_same_v || std::is_same_v, 106 | "Parameter type not supported!"); 107 | 108 | Entry(const std::string& n, T& v, const SetRange& r) : 109 | name(n), 110 | value(v), 111 | range(r) {} 112 | void operator=(const Entry&) = delete; // Because 'value' is a reference 113 | void init_option() override; 114 | void read_option() override; 115 | 116 | std::string name; 117 | T& value; 118 | SetRange range; 119 | }; 120 | 121 | // Our facility to fill the container, each Entry corresponds to a parameter 122 | // to tune. We use variadic templates to deal with an unspecified number of 123 | // entries, each one of a possible different type. 124 | static std::string next(std::string& names, bool pop = true); 125 | 126 | int add(const SetRange&, std::string&&) { return 0; } 127 | 128 | template 129 | int add(const SetRange& range, std::string&& names, T& value, Args&&... args) { 130 | list.push_back(std::unique_ptr(new Entry(next(names), value, range))); 131 | return add(range, std::move(names), args...); 132 | } 133 | 134 | // Template specialization for arrays: recursively handle multi-dimensional arrays 135 | template 136 | int add(const SetRange& range, std::string&& names, T (&value)[N], Args&&... args) { 137 | for (size_t i = 0; i < N; i++) 138 | add(range, next(names, i == N - 1) + "[" + std::to_string(i) + "]", value[i]); 139 | return add(range, std::move(names), args...); 140 | } 141 | 142 | // Template specialization for SetRange 143 | template 144 | int add(const SetRange&, std::string&& names, SetRange& value, Args&&... args) { 145 | return add(value, (next(names), std::move(names)), args...); 146 | } 147 | 148 | static void make_option(OptionsMap* options, const std::string& n, int v, const SetRange& r); 149 | 150 | std::vector> list; 151 | 152 | public: 153 | template 154 | static int add(const std::string& names, Args&&... args) { 155 | return instance().add(SetDefaultRange, names.substr(1, names.size() - 2), 156 | args...); // Remove trailing parenthesis 157 | } 158 | static void init(OptionsMap& o) { 159 | options = &o; 160 | for (auto& e : instance().list) 161 | e->init_option(); 162 | read_options(); 163 | } // Deferred, due to UCIEngine::Options access 164 | static void read_options() { 165 | for (auto& e : instance().list) 166 | e->read_option(); 167 | } 168 | 169 | static bool update_on_last; 170 | static OptionsMap* options; 171 | }; 172 | 173 | // Some macro magic :-) we define a dummy int variable that the compiler initializes calling Tune::add() 174 | #define STRINGIFY(x) #x 175 | #define UNIQUE2(x, y) x##y 176 | #define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__ 177 | #define TUNE(...) int UNIQUE(p, __LINE__) = Tune::add(STRINGIFY((__VA_ARGS__)), __VA_ARGS__) 178 | 179 | #define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true 180 | 181 | } // namespace Stockfish 182 | 183 | #endif // #ifndef TUNE_H_INCLUDED 184 | -------------------------------------------------------------------------------- /src/uci.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 | #include 26 | 27 | #include "engine.h" 28 | #include "misc.h" 29 | #include "search.h" 30 | 31 | namespace Stockfish { 32 | 33 | class Position; 34 | class Move; 35 | class Score; 36 | enum Square : int; 37 | using Value = int; 38 | 39 | class UCIEngine { 40 | public: 41 | UCIEngine(int argc, char** argv); 42 | 43 | void loop(); 44 | 45 | static int to_cp(Value v, const Position& pos); 46 | static std::string format_score(const Score& s); 47 | static std::string square(Square s); 48 | static std::string move(Move m, bool chess960); 49 | static std::string wdl(Value v, const Position& pos); 50 | static std::string to_lower(std::string str); 51 | static Move to_move(const Position& pos, std::string str); 52 | 53 | static Search::LimitsType parse_limits(std::istream& is); 54 | 55 | auto& engine_options() { return engine.get_options(); } 56 | 57 | private: 58 | Engine engine; 59 | CommandLine cli; 60 | 61 | static void print_info_string(std::string_view str); 62 | 63 | void go(std::istringstream& is); 64 | void bench(std::istream& args); 65 | void benchmark(std::istream& args); 66 | void position(std::istringstream& is); 67 | void setoption(std::istringstream& is); 68 | std::uint64_t perft(const Search::LimitsType&); 69 | 70 | static void on_update_no_moves(const Engine::InfoShort& info); 71 | static void on_update_full(const Engine::InfoFull& info, bool showWDL); 72 | static void on_iter(const Engine::InfoIter& info); 73 | static void on_bestmove(std::string_view bestmove, std::string_view ponder); 74 | 75 | void init_search_update_listeners(); 76 | }; 77 | 78 | } // namespace Stockfish 79 | 80 | #endif // #ifndef UCI_H_INCLUDED 81 | -------------------------------------------------------------------------------- /src/ucioption.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 "ucioption.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "misc.h" 30 | 31 | namespace Stockfish { 32 | 33 | bool CaseInsensitiveLess::operator()(const std::string& s1, const std::string& s2) const { 34 | 35 | return std::lexicographical_compare( 36 | s1.begin(), s1.end(), s2.begin(), s2.end(), 37 | [](char c1, char c2) { return std::tolower(c1) < std::tolower(c2); }); 38 | } 39 | 40 | void OptionsMap::add_info_listener(InfoListener&& message_func) { info = std::move(message_func); } 41 | 42 | void OptionsMap::setoption(std::istringstream& is) { 43 | std::string token, name, value; 44 | 45 | is >> token; // Consume the "name" token 46 | 47 | // Read the option name (can contain spaces) 48 | while (is >> token && token != "value") 49 | name += (name.empty() ? "" : " ") + token; 50 | 51 | // Read the option value (can contain spaces) 52 | while (is >> token) 53 | value += (value.empty() ? "" : " ") + token; 54 | 55 | if (options_map.count(name)) 56 | options_map[name] = value; 57 | else 58 | sync_cout << "No such option: " << name << sync_endl; 59 | } 60 | 61 | const Option& OptionsMap::operator[](const std::string& name) const { 62 | auto it = options_map.find(name); 63 | assert(it != options_map.end()); 64 | return it->second; 65 | } 66 | 67 | // Inits options and assigns idx in the correct printing order 68 | void OptionsMap::add(const std::string& name, const Option& option) { 69 | if (!options_map.count(name)) 70 | { 71 | static size_t insert_order = 0; 72 | 73 | options_map[name] = option; 74 | 75 | options_map[name].parent = this; 76 | options_map[name].idx = insert_order++; 77 | } 78 | else 79 | { 80 | std::cerr << "Option \"" << name << "\" was already added!" << std::endl; 81 | std::exit(EXIT_FAILURE); 82 | } 83 | } 84 | 85 | 86 | std::size_t OptionsMap::count(const std::string& name) const { return options_map.count(name); } 87 | 88 | Option::Option(const OptionsMap* map) : 89 | parent(map) {} 90 | 91 | Option::Option(const char* v, OnChange f) : 92 | type("string"), 93 | min(0), 94 | max(0), 95 | on_change(std::move(f)) { 96 | defaultValue = currentValue = v; 97 | } 98 | 99 | Option::Option(bool v, OnChange f) : 100 | type("check"), 101 | min(0), 102 | max(0), 103 | on_change(std::move(f)) { 104 | defaultValue = currentValue = (v ? "true" : "false"); 105 | } 106 | 107 | Option::Option(OnChange f) : 108 | type("button"), 109 | min(0), 110 | max(0), 111 | on_change(std::move(f)) {} 112 | 113 | Option::Option(double v, int minv, int maxv, OnChange f) : 114 | type("spin"), 115 | min(minv), 116 | max(maxv), 117 | on_change(std::move(f)) { 118 | defaultValue = currentValue = std::to_string(v); 119 | } 120 | 121 | Option::Option(const char* v, const char* cur, OnChange f) : 122 | type("combo"), 123 | min(0), 124 | max(0), 125 | on_change(std::move(f)) { 126 | defaultValue = v; 127 | currentValue = cur; 128 | } 129 | 130 | Option::operator int() const { 131 | assert(type == "check" || type == "spin"); 132 | return (type == "spin" ? std::stoi(currentValue) : currentValue == "true"); 133 | } 134 | 135 | Option::operator std::string() const { 136 | assert(type == "string"); 137 | return currentValue; 138 | } 139 | 140 | bool Option::operator==(const char* s) const { 141 | assert(type == "combo"); 142 | return !CaseInsensitiveLess()(currentValue, s) && !CaseInsensitiveLess()(s, currentValue); 143 | } 144 | 145 | bool Option::operator!=(const char* s) const { return !(*this == s); } 146 | 147 | 148 | // Updates currentValue and triggers on_change() action. It's up to 149 | // the GUI to check for option's limits, but we could receive the new value 150 | // from the user by console window, so let's check the bounds anyway. 151 | Option& Option::operator=(const std::string& v) { 152 | 153 | assert(!type.empty()); 154 | 155 | if ((type != "button" && type != "string" && v.empty()) 156 | || (type == "check" && v != "true" && v != "false") 157 | || (type == "spin" && (std::stof(v) < min || std::stof(v) > max))) 158 | return *this; 159 | 160 | if (type == "combo") 161 | { 162 | OptionsMap comboMap; // To have case insensitive compare 163 | std::string token; 164 | std::istringstream ss(defaultValue); 165 | while (ss >> token) 166 | comboMap.add(token, Option()); 167 | if (!comboMap.count(v) || v == "var") 168 | return *this; 169 | } 170 | 171 | if (type == "string") 172 | currentValue = v == "" ? "" : v; 173 | else if (type != "button") 174 | currentValue = v; 175 | 176 | if (on_change) 177 | { 178 | const auto ret = on_change(*this); 179 | 180 | if (ret && parent != nullptr && parent->info != nullptr) 181 | parent->info(ret); 182 | } 183 | 184 | return *this; 185 | } 186 | 187 | std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { 188 | for (size_t idx = 0; idx < om.options_map.size(); ++idx) 189 | for (const auto& it : om.options_map) 190 | if (it.second.idx == idx) 191 | { 192 | const Option& o = it.second; 193 | os << "\noption name " << it.first << " type " << o.type; 194 | 195 | if (o.type == "check" || o.type == "combo") 196 | os << " default " << o.defaultValue; 197 | 198 | else if (o.type == "string") 199 | { 200 | std::string defaultValue = o.defaultValue.empty() ? "" : o.defaultValue; 201 | os << " default " << defaultValue; 202 | } 203 | 204 | else if (o.type == "spin") 205 | os << " default " << int(stof(o.defaultValue)) << " min " << o.min << " max " 206 | << o.max; 207 | 208 | break; 209 | } 210 | 211 | return os; 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/ucioption.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2025 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 UCIOPTION_H_INCLUDED 20 | #define UCIOPTION_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | namespace Stockfish { 30 | // Define a custom comparator, because the UCI options should be case-insensitive 31 | struct CaseInsensitiveLess { 32 | bool operator()(const std::string&, const std::string&) const; 33 | }; 34 | 35 | class OptionsMap; 36 | 37 | // The Option class implements each option as specified by the UCI protocol 38 | class Option { 39 | public: 40 | using OnChange = std::function(const Option&)>; 41 | 42 | Option(const OptionsMap*); 43 | Option(OnChange = nullptr); 44 | Option(bool v, OnChange = nullptr); 45 | Option(const char* v, OnChange = nullptr); 46 | Option(double v, int minv, int maxv, OnChange = nullptr); 47 | Option(const char* v, const char* cur, OnChange = nullptr); 48 | 49 | Option& operator=(const std::string&); 50 | operator int() const; 51 | operator std::string() const; 52 | bool operator==(const char*) const; 53 | bool operator!=(const char*) const; 54 | 55 | friend std::ostream& operator<<(std::ostream&, const OptionsMap&); 56 | 57 | int operator<<(const Option&) = delete; 58 | 59 | private: 60 | friend class OptionsMap; 61 | friend class Engine; 62 | friend class Tune; 63 | 64 | 65 | std::string defaultValue, currentValue, type; 66 | int min, max; 67 | size_t idx; 68 | OnChange on_change; 69 | const OptionsMap* parent = nullptr; 70 | }; 71 | 72 | class OptionsMap { 73 | public: 74 | using InfoListener = std::function)>; 75 | 76 | OptionsMap() = default; 77 | OptionsMap(const OptionsMap&) = delete; 78 | OptionsMap(OptionsMap&&) = delete; 79 | OptionsMap& operator=(const OptionsMap&) = delete; 80 | OptionsMap& operator=(OptionsMap&&) = delete; 81 | 82 | void add_info_listener(InfoListener&&); 83 | 84 | void setoption(std::istringstream&); 85 | 86 | const Option& operator[](const std::string&) const; 87 | 88 | void add(const std::string&, const Option& option); 89 | 90 | std::size_t count(const std::string&) const; 91 | 92 | private: 93 | friend class Engine; 94 | friend class Option; 95 | 96 | friend std::ostream& operator<<(std::ostream&, const OptionsMap&); 97 | 98 | // The options container is defined as a std::map 99 | using OptionsStore = std::map; 100 | 101 | OptionsStore options_map; 102 | InfoListener info; 103 | }; 104 | 105 | } 106 | #endif // #ifndef UCIOPTION_H_INCLUDED 107 | -------------------------------------------------------------------------------- /tests/.gitattributes: -------------------------------------------------------------------------------- 1 | *.sh text eol=lf 2 | -------------------------------------------------------------------------------- /tests/perft.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # verify perft numbers (positions from https://www.chessprogramming.org/Perft_Results) 3 | 4 | TESTS_FAILED=0 5 | 6 | error() 7 | { 8 | echo "perft testing failed on line $1" 9 | exit 1 10 | } 11 | trap 'error ${LINENO}' ERR 12 | 13 | echo "perft testing started" 14 | 15 | EXPECT_SCRIPT=$(mktemp) 16 | 17 | cat << 'EOF' > $EXPECT_SCRIPT 18 | #!/usr/bin/expect -f 19 | set timeout 30 20 | lassign [lrange $argv 0 4] pos depth result chess960 logfile 21 | log_file -noappend $logfile 22 | spawn ./stockfish 23 | if {$chess960 == "true"} { 24 | send "setoption name UCI_Chess960 value true\n" 25 | } 26 | send "position $pos\ngo perft $depth\n" 27 | expect { 28 | "Nodes searched: $result" {} 29 | timeout {puts "TIMEOUT: Expected $result nodes"; exit 1} 30 | eof {puts "EOF: Stockfish crashed"; exit 2} 31 | } 32 | send "quit\n" 33 | expect eof 34 | EOF 35 | 36 | chmod +x $EXPECT_SCRIPT 37 | 38 | run_test() { 39 | local pos="$1" 40 | local depth="$2" 41 | local expected="$3" 42 | local chess960="$4" 43 | local tmp_file=$(mktemp) 44 | 45 | echo -n "Testing depth $depth: ${pos:0:40}... " 46 | 47 | if $EXPECT_SCRIPT "$pos" "$depth" "$expected" "$chess960" "$tmp_file" > /dev/null 2>&1; then 48 | echo "OK" 49 | rm -f "$tmp_file" 50 | else 51 | local exit_code=$? 52 | echo "FAILED (exit code: $exit_code)" 53 | echo "===== Output for failed test =====" 54 | cat "$tmp_file" 55 | echo "==================================" 56 | rm -f "$tmp_file" 57 | TESTS_FAILED=1 58 | fi 59 | } 60 | 61 | # standard positions 62 | 63 | run_test "startpos" 7 3195901860 "false" 64 | run_test "fen r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -" 5 193690690 "false" 65 | run_test "fen 8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -" 7 178633661 "false" 66 | run_test "fen r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1" 6 706045033 "false" 67 | run_test "fen rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8" 5 89941194 "false" 68 | run_test "fen r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10" 5 164075551 "false" 69 | run_test "fen r7/4p3/5p1q/3P4/4pQ2/4pP2/6pp/R3K1kr w Q - 1 3" 5 11609488 "false" 70 | 71 | # chess960 positions 72 | 73 | run_test "fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w AHah - 0 1" 6 119060324 "true" 74 | run_test "fen 1rqbkrbn/1ppppp1p/1n6/p1N3p1/8/2P4P/PP1PPPP1/1RQBKRBN w FBfb - 0 9" 6 191762235 "true" 75 | run_test "fen rbbqn1kr/pp2p1pp/6n1/2pp1p2/2P4P/P7/BP1PPPP1/R1BQNNKR w HAha - 0 9" 6 924181432 "true" 76 | run_test "fen rqbbknr1/1ppp2pp/p5n1/4pp2/P7/1PP5/1Q1PPPPP/R1BBKNRN w GAga - 0 9" 6 308553169 "true" 77 | run_test "fen 4rrb1/1kp3b1/1p1p4/pP1Pn2p/5p2/1PR2P2/2P1NB1P/2KR1B2 w D - 0 21" 6 872323796 "true" 78 | run_test "fen 1rkr3b/1ppn3p/3pB1n1/6q1/R2P4/4N1P1/1P5P/2KRQ1B1 b Dbd - 0 14" 6 2678022813 "true" 79 | run_test "fen qbbnrkr1/p1pppppp/1p4n1/8/2P5/6N1/PPNPPPPP/1BRKBRQ1 b FCge - 1 3" 6 521301336 "true" 80 | run_test "fen rr6/2kpp3/1ppn2p1/p2b1q1p/P4P1P/1PNN2P1/2PP4/1K2R2R b E - 1 20" 2 1438 "true" 81 | run_test "fen rr6/2kpp3/1ppn2p1/p2b1q1p/P4P1P/1PNN2P1/2PP4/1K2RR2 w E - 0 20" 3 37340 "true" 82 | run_test "fen rr6/2kpp3/1ppnb1p1/p2Q1q1p/P4P1P/1PNN2P1/2PP4/1K2RR2 b E - 2 19" 4 2237725 "true" 83 | run_test "fen rr6/2kpp3/1ppnb1p1/p4q1p/P4P1P/1PNN2P1/2PP2Q1/1K2RR2 w E - 1 19" 4 2098209 "true" 84 | run_test "fen rr6/2kpp3/1ppnb1p1/p4q1p/P4P1P/1PNN2P1/2PP2Q1/1K2RR2 w E - 1 19" 5 79014522 "true" 85 | run_test "fen rr6/2kpp3/1ppnb1p1/p4q1p/P4P1P/1PNN2P1/2PP2Q1/1K2RR2 w E - 1 19" 6 2998685421 "true" 86 | 87 | rm -f $EXPECT_SCRIPT 88 | echo "perft testing completed" 89 | 90 | if [ $TESTS_FAILED -ne 0 ]; then 91 | echo "Some tests failed" 92 | exit 1 93 | fi 94 | -------------------------------------------------------------------------------- /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=`eval "$WINE_PATH ./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 | --------------------------------------------------------------------------------