├── .gitignore ├── .git-blame-ignore-revs ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ └── BUG-REPORT.yml ├── ci │ ├── libcxx17.imp │ ├── arm_matrix.json │ └── matrix.json └── workflows │ ├── iwyu.yml │ ├── clang-format.yml │ ├── codeql.yml │ ├── sanitizers.yml │ ├── compilation.yml │ ├── arm_compilation.yml │ ├── stockfish.yml │ └── upload_binaries.yml ├── CITATION.cff ├── tests ├── signature.sh ├── perft.sh └── reprosearch.sh ├── src ├── benchmark.h ├── main.cpp ├── incbin │ └── UNLICENCE ├── score.cpp ├── score.h ├── evaluate.h ├── timeman.h ├── nnue │ ├── nnue_misc.h │ ├── features │ │ ├── half_ka_v2_hm.cpp │ │ └── half_ka_v2_hm.h │ ├── nnue_accumulator.h │ ├── layers │ │ ├── sqr_clipped_relu.h │ │ ├── simd.h │ │ └── clipped_relu.h │ ├── network.h │ ├── nnue_architecture.h │ └── nnue_misc.cpp ├── perft.h ├── movegen.h ├── syzygy │ └── tbprobe.h ├── uci.h ├── ucioption.h ├── thread_win32_osx.h ├── engine.h ├── tune.cpp ├── tt.h ├── thread.h ├── evaluate.cpp ├── timeman.cpp ├── ucioption.cpp ├── engine.cpp ├── tt.cpp ├── tune.h ├── benchmark.cpp ├── misc.h ├── movepick.h └── bitboard.cpp ├── .clang-format ├── scripts └── get_native_properties.sh ├── CONTRIBUTING.md ├── AUTHORS └── README.md /.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 | -------------------------------------------------------------------------------- /.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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/benchmark.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | } // namespace Stockfish 31 | 32 | #endif // #ifndef BENCHMARK_H_INCLUDED 33 | -------------------------------------------------------------------------------- /tests/perft.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # verify perft numbers (positions from www.chessprogramming.org/Perft_Results) 3 | 4 | error() 5 | { 6 | echo "perft testing failed on line $1" 7 | exit 1 8 | } 9 | trap 'error ${LINENO}' ERR 10 | 11 | echo "perft testing started" 12 | 13 | cat << EOF > perft.exp 14 | set timeout 10 15 | lassign \$argv pos depth result 16 | spawn ./stockfish 17 | send "position \$pos\\ngo perft \$depth\\n" 18 | expect "Nodes searched? \$result" {} timeout {exit 1} 19 | send "quit\\n" 20 | expect eof 21 | EOF 22 | 23 | expect perft.exp startpos 5 4865609 > /dev/null 24 | expect perft.exp "fen r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -" 5 193690690 > /dev/null 25 | expect perft.exp "fen 8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -" 6 11030083 > /dev/null 26 | expect perft.exp "fen r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1" 5 15833292 > /dev/null 27 | expect perft.exp "fen rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8" 5 89941194 > /dev/null 28 | expect perft.exp "fen r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10" 5 164075551 > /dev/null 29 | 30 | rm perft.exp 31 | 32 | echo "perft testing OK" 33 | -------------------------------------------------------------------------------- /.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 | 11 | # Mappings for includes between public headers 12 | { include: [ "", public, "", public ] }, 13 | { include: [ "", public, "", public ] }, 14 | { include: [ "", public, "", public ] }, 15 | { include: [ "", public, "", public ] }, 16 | { include: [ "", public, "", public ] }, 17 | 18 | # Missing mappings in include-what-you-use's libcxx.imp 19 | { include: ["@<__condition_variable/.*>", private, "", public ] }, 20 | { include: ["@<__mutex/.*>", private, "", public ] }, 21 | ] 22 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/score.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 (std::abs(v) < VALUE_TB_WIN_IN_MAX_PLY) 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 | } -------------------------------------------------------------------------------- /.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 | 18 | - name: Checkout include-what-you-use 19 | uses: actions/checkout@v4 20 | with: 21 | repository: include-what-you-use/include-what-you-use 22 | ref: f25caa280dc3277c4086ec345ad279a2463fea0f 23 | path: include-what-you-use 24 | 25 | - name: Download required linux packages 26 | run: | 27 | sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main' 28 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - 29 | sudo apt update 30 | sudo apt install -y libclang-17-dev clang-17 libc++-17-dev 31 | 32 | - name: Set up include-what-you-use 33 | run: | 34 | mkdir build && cd build 35 | cmake -G "Unix Makefiles" -DCMAKE_PREFIX_PATH="/usr/lib/llvm-17" .. 36 | sudo make install 37 | working-directory: include-what-you-use 38 | 39 | - name: Check include-what-you-use 40 | run: include-what-you-use --version 41 | 42 | - name: Check includes 43 | run: > 44 | make analyze 45 | COMP=clang 46 | CXX=include-what-you-use 47 | CXXFLAGS="-stdlib=libc++ -Xiwyu --comment_style=long -Xiwyu --mapping='${{ github.workspace }}/Stockfish/.github/ci/libcxx17.imp' -Xiwyu --error" 48 | -------------------------------------------------------------------------------- /src/score.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/evaluate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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-c721dfca8cd3.nnue" 37 | #define EvalFileDefaultNameSmall "nn-baff1ede1f90.nnue" 38 | 39 | namespace NNUE { 40 | struct Networks; 41 | struct AccumulatorCaches; 42 | } 43 | 44 | std::string trace(Position& pos, const Eval::NNUE::Networks& networks); 45 | 46 | int simple_eval(const Position& pos, Color c); 47 | bool use_smallnet(const Position& pos); 48 | Value evaluate(const NNUE::Networks& networks, 49 | const Position& pos, 50 | Eval::NNUE::AccumulatorCaches& caches, 51 | int optimism); 52 | } // namespace Eval 53 | 54 | } // namespace Stockfish 55 | 56 | #endif // #ifndef EVALUATE_H_INCLUDED 57 | -------------------------------------------------------------------------------- /.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 | jobs: 15 | Clang-Format: 16 | name: Clang-Format 17 | runs-on: ubuntu-20.04 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | ref: ${{ github.event.pull_request.head.sha }} 22 | 23 | - name: Run clang-format style check 24 | uses: jidicula/clang-format-action@f62da5e3d3a2d88ff364771d9d938773a618ab5e # @v4.11.0 25 | id: clang-format 26 | continue-on-error: true 27 | with: 28 | clang-format-version: "17" 29 | exclude-regex: "incbin" 30 | 31 | - name: Comment on PR 32 | if: steps.clang-format.outcome == 'failure' 33 | uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # @v2.5.0 34 | with: 35 | message: | 36 | clang-format 17 needs to be run on this PR. 37 | If you do not have clang-format installed, the maintainer will run it when merging. 38 | For the exact version please see https://packages.ubuntu.com/mantic/clang-format-17. 39 | 40 | _(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_ 41 | comment_tag: execution 42 | 43 | - name: Comment on PR 44 | if: steps.clang-format.outcome != 'failure' 45 | uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # @v2.5.0 46 | with: 47 | message: | 48 | _(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_ 49 | create_if_not_exists: false 50 | comment_tag: execution 51 | mode: delete 52 | -------------------------------------------------------------------------------- /.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 | 34 | # Initializes the CodeQL tools for scanning. 35 | - name: Initialize CodeQL 36 | uses: github/codeql-action/init@v3 37 | with: 38 | languages: ${{ matrix.language }} 39 | # If you wish to specify custom queries, you can do so here or in a config file. 40 | # By default, queries listed here will override any specified in a config file. 41 | # Prefix the list here with "+" to use these queries and those in the config file. 42 | 43 | # 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 44 | # queries: security-extended,security-and-quality 45 | 46 | - name: Build 47 | working-directory: src 48 | run: make -j build ARCH=x86-64-modern 49 | 50 | - name: Perform CodeQL Analysis 51 | uses: github/codeql-action/analyze@v3 52 | with: 53 | category: "/language:${{matrix.language}}" 54 | -------------------------------------------------------------------------------- /src/timeman.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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( 40 | Search::LimitsType& limits, Color us, int ply, const OptionsMap& options, int& originalPly); 41 | 42 | TimePoint optimum() const; 43 | TimePoint maximum() const; 44 | template 45 | TimePoint elapsed(FUNC nodes) const { 46 | return useNodesTime ? TimePoint(nodes()) : elapsed_time(); 47 | } 48 | TimePoint elapsed_time() const { return now() - startTime; }; 49 | 50 | void clear(); 51 | void advance_nodes_time(std::int64_t nodes); 52 | 53 | private: 54 | TimePoint startTime; 55 | TimePoint optimumTime; 56 | TimePoint maximumTime; 57 | 58 | std::int64_t availableNodes = -1; // When in 'nodes as time' mode 59 | bool useNodesTime = false; // True if we are in 'nodes as time' mode 60 | }; 61 | 62 | } // namespace Stockfish 63 | 64 | #endif // #ifndef TIMEMAN_H_INCLUDED 65 | -------------------------------------------------------------------------------- /src/nnue/nnue_misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | void hint_common_parent_position(const Position& pos, 58 | const Networks& networks, 59 | AccumulatorCaches& caches); 60 | 61 | } // namespace Stockfish::Eval::NNUE 62 | } // namespace Stockfish 63 | 64 | #endif // #ifndef NNUE_MISC_H_INCLUDED 65 | -------------------------------------------------------------------------------- /src/perft.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); 38 | 39 | uint64_t cnt, nodes = 0; 40 | const bool leaf = (depth == 2); 41 | 42 | for (const auto& m : MoveList(pos)) 43 | { 44 | if (Root && depth <= 1) 45 | cnt = 1, nodes++; 46 | else 47 | { 48 | pos.do_move(m, st); 49 | cnt = leaf ? MoveList(pos).size() : perft(pos, depth - 1); 50 | nodes += cnt; 51 | pos.undo_move(m); 52 | } 53 | if (Root) 54 | sync_cout << UCIEngine::move(m, pos.is_chess960()) << ": " << cnt << sync_endl; 55 | } 56 | return nodes; 57 | } 58 | 59 | inline uint64_t perft(const std::string& fen, Depth depth, bool isChess960) { 60 | StateListPtr states(new std::deque(1)); 61 | Position p; 62 | p.set(fen, isChess960, &states->back()); 63 | 64 | return perft(p, depth); 65 | } 66 | } 67 | 68 | #endif // PERFT_H_INCLUDED 69 | -------------------------------------------------------------------------------- /src/movegen.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | QUIET_CHECKS, 35 | EVASIONS, 36 | NON_EVASIONS, 37 | LEGAL 38 | }; 39 | 40 | struct ExtMove: public Move { 41 | int value; 42 | 43 | void operator=(Move m) { data = m.raw(); } 44 | 45 | // Inhibit unwanted implicit conversions to Move 46 | // with an ambiguity that yields to a compile error. 47 | operator float() const = delete; 48 | }; 49 | 50 | inline bool operator<(const ExtMove& f, const ExtMove& s) { return f.value < s.value; } 51 | 52 | template 53 | ExtMove* generate(const Position& pos, ExtMove* moveList); 54 | 55 | // The MoveList struct wraps the generate() function and returns a convenient 56 | // list of moves. Using MoveList is sometimes preferable to directly calling 57 | // the lower level generate() function. 58 | template 59 | struct MoveList { 60 | 61 | explicit MoveList(const Position& pos) : 62 | last(generate(pos, moveList)) {} 63 | const ExtMove* begin() const { return moveList; } 64 | const ExtMove* end() const { return last; } 65 | size_t size() const { return last - moveList; } 66 | bool contains(Move move) const { return std::find(begin(), end(), move) != end(); } 67 | 68 | private: 69 | ExtMove moveList[MAX_MOVES], *last; 70 | }; 71 | 72 | } // namespace Stockfish 73 | 74 | #endif // #ifndef MOVEGEN_H_INCLUDED 75 | -------------------------------------------------------------------------------- /src/syzygy/tbprobe.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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); 70 | bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, bool rule50); 71 | Config rank_root_moves(const OptionsMap& options, Position& pos, Search::RootMoves& rootMoves); 72 | 73 | } // namespace Stockfish::Tablebases 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /src/uci.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | void go(std::istringstream& is); 62 | void bench(std::istream& args); 63 | void position(std::istringstream& is); 64 | void setoption(std::istringstream& is); 65 | std::uint64_t perft(const Search::LimitsType&); 66 | 67 | static void on_update_no_moves(const Engine::InfoShort& info); 68 | static void on_update_full(const Engine::InfoFull& info, bool showWDL); 69 | static void on_iter(const Engine::InfoIter& info); 70 | static void on_bestmove(std::string_view bestmove, std::string_view ponder); 71 | }; 72 | 73 | } // namespace Stockfish 74 | 75 | #endif // #ifndef UCI_H_INCLUDED 76 | -------------------------------------------------------------------------------- /.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 | instrumented_option: sanitizer-thread 25 | - name: Run with UB sanitizer 26 | make_option: sanitize=undefined 27 | instrumented_option: sanitizer-undefined 28 | - name: Run under valgrind 29 | make_option: "" 30 | instrumented_option: valgrind 31 | - name: Run under valgrind-thread 32 | make_option: "" 33 | instrumented_option: valgrind-thread 34 | - name: Run non-instrumented 35 | make_option: "" 36 | instrumented_option: none 37 | defaults: 38 | run: 39 | working-directory: src 40 | shell: ${{ matrix.config.shell }} 41 | steps: 42 | - uses: actions/checkout@v4 43 | 44 | - name: Download required linux packages 45 | run: | 46 | sudo apt update 47 | sudo apt install expect valgrind g++-multilib 48 | 49 | - name: Download the used network from the fishtest framework 50 | run: make net 51 | 52 | - name: Check compiler 53 | run: $COMPCXX -v 54 | 55 | - name: Test help target 56 | run: make help 57 | 58 | - name: Check git 59 | run: git --version 60 | 61 | # Since Linux Kernel 6.5 we are getting false positives from the ci, 62 | # lower the ALSR entropy to disable ALSR, which works as a temporary workaround. 63 | # https://github.com/google/sanitizers/issues/1716 64 | # https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2056762 65 | 66 | - name: Lower ALSR entropy 67 | run: sudo sysctl -w vm.mmap_rnd_bits=28 68 | 69 | # Sanitizers 70 | 71 | - name: ${{ matrix.sanitizers.name }} 72 | run: | 73 | export CXXFLAGS="-O1 -fno-inline" 74 | make clean 75 | make -j4 ARCH=x86-64-sse41-popcnt ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null 76 | ../tests/instrumented.sh --${{ matrix.sanitizers.instrumented_option }} 77 | -------------------------------------------------------------------------------- /src/ucioption.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | 28 | namespace Stockfish { 29 | // Define a custom comparator, because the UCI options should be case-insensitive 30 | struct CaseInsensitiveLess { 31 | bool operator()(const std::string&, const std::string&) const; 32 | }; 33 | 34 | class Option; 35 | 36 | class OptionsMap { 37 | public: 38 | void setoption(std::istringstream&); 39 | 40 | friend std::ostream& operator<<(std::ostream&, const OptionsMap&); 41 | 42 | Option operator[](const std::string&) const; 43 | Option& operator[](const std::string&); 44 | 45 | std::size_t count(const std::string&) const; 46 | 47 | private: 48 | // The options container is defined as a std::map 49 | using OptionsStore = std::map; 50 | 51 | OptionsStore options_map; 52 | }; 53 | 54 | // The Option class implements each option as specified by the UCI protocol 55 | class Option { 56 | public: 57 | using OnChange = std::function; 58 | 59 | Option(OnChange = nullptr); 60 | Option(bool v, OnChange = nullptr); 61 | Option(const char* v, OnChange = nullptr); 62 | Option(double v, int minv, int maxv, OnChange = nullptr); 63 | Option(const char* v, const char* cur, OnChange = nullptr); 64 | 65 | Option& operator=(const std::string&); 66 | void operator<<(const Option&); 67 | operator int() const; 68 | operator std::string() const; 69 | bool operator==(const char*) const; 70 | 71 | friend std::ostream& operator<<(std::ostream&, const OptionsMap&); 72 | 73 | private: 74 | std::string defaultValue, currentValue, type; 75 | int min, max; 76 | size_t idx; 77 | OnChange on_change; 78 | }; 79 | 80 | } 81 | #endif // #ifndef UCIOPTION_H_INCLUDED 82 | -------------------------------------------------------------------------------- /src/thread_win32_osx.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | -------------------------------------------------------------------------------- /.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 | 29 | - name: Install fixed GCC on Linux 30 | if: runner.os == 'Linux' 31 | uses: egor-tensin/setup-gcc@eaa888eb19115a521fa72b65cd94fe1f25bbcaac # @v1.3 32 | with: 33 | version: 11 34 | 35 | - name: Setup msys and install required packages 36 | if: runner.os == 'Windows' 37 | uses: msys2/setup-msys2@v2 38 | with: 39 | msystem: ${{ matrix.config.msys_sys }} 40 | install: mingw-w64-${{ matrix.config.msys_env }} make git zip 41 | 42 | - name: Download SDE package 43 | if: runner.os == 'Linux' || runner.os == 'Windows' 44 | uses: petarpetrovt/setup-sde@91a1a03434384e064706634125a15f7446d2aafb # @v2.3 45 | with: 46 | environmentVariableName: SDE_DIR 47 | sdeVersion: 9.27.0 48 | 49 | - name: Download the used network from the fishtest framework 50 | run: make net 51 | 52 | - name: Check compiler 53 | run: $COMPCXX -v 54 | 55 | - name: Test help target 56 | run: make help 57 | 58 | - name: Check git 59 | run: git --version 60 | 61 | - name: Check compiler 62 | run: $COMPCXX -v 63 | 64 | - name: Show g++ cpu info 65 | if: runner.os != 'macOS' 66 | run: g++ -Q -march=native --help=target 67 | 68 | - name: Show clang++ cpu info 69 | if: runner.os == 'macOS' 70 | run: clang++ -E - -march=native -### 71 | 72 | # x86-64 with newer extensions tests 73 | 74 | - name: Compile ${{ matrix.config.binaries }} build 75 | run: | 76 | make clean 77 | make -j4 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH="$SDE" 78 | make strip ARCH=$BINARY COMP=$COMP 79 | WINE_PATH="$SDE" ../tests/signature.sh $benchref 80 | mv ./stockfish$EXT ../stockfish-$NAME-$BINARY$EXT 81 | 82 | - name: Remove non src files 83 | run: git clean -fx 84 | 85 | - name: Upload artifact for (pre)-release 86 | uses: actions/upload-artifact@v4 87 | with: 88 | name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} 89 | path: . 90 | -------------------------------------------------------------------------------- /.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 | 30 | - name: Download required linux packages 31 | if: runner.os == 'Linux' 32 | run: | 33 | sudo apt update 34 | sudo apt install qemu-user 35 | 36 | - name: Install NDK 37 | if: runner.os == 'Linux' 38 | run: | 39 | if [ $COMP == ndk ]; then 40 | NDKV="21.4.7075529" 41 | ANDROID_ROOT=/usr/local/lib/android 42 | ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk 43 | SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager 44 | echo "y" | $SDKMANAGER "ndk;$NDKV" 45 | ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$NDKV 46 | ANDROID_NDK_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin 47 | echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV 48 | fi 49 | 50 | - name: Extract the bench number from the commit history 51 | run: | 52 | for hash in $(git rev-list -100 HEAD); do 53 | 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 54 | done 55 | [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $hash" && echo "Reference bench: $benchref" || echo "No bench found" 56 | 57 | - name: Download the used network from the fishtest framework 58 | run: make net 59 | 60 | - name: Check compiler 61 | run: | 62 | if [ $COMP == ndk ]; then 63 | export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH 64 | fi 65 | $COMPCXX -v 66 | 67 | - name: Test help target 68 | run: make help 69 | 70 | - name: Check git 71 | run: git --version 72 | 73 | # Compile profile guided builds 74 | 75 | - name: Compile ${{ matrix.binaries }} build 76 | run: | 77 | if [ $COMP == ndk ]; then 78 | export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH 79 | export LDFLAGS="-static -Wno-unused-command-line-argument" 80 | fi 81 | make clean 82 | make -j4 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH=$EMU 83 | make strip ARCH=$BINARY COMP=$COMP 84 | WINE_PATH=$EMU ../tests/signature.sh $benchref 85 | mv ./stockfish$EXT ../stockfish-android-$BINARY$EXT 86 | 87 | - name: Remove non src files 88 | run: git clean -fx 89 | 90 | - name: Upload artifact for (pre)-release 91 | uses: actions/upload-artifact@v4 92 | with: 93 | name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} 94 | path: . 95 | -------------------------------------------------------------------------------- /src/engine.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 "position.h" 33 | #include "search.h" 34 | #include "syzygy/tbprobe.h" // for Stockfish::Depth 35 | #include "thread.h" 36 | #include "tt.h" 37 | #include "ucioption.h" 38 | 39 | namespace Stockfish { 40 | 41 | enum Square : int; 42 | 43 | class Engine { 44 | public: 45 | using InfoShort = Search::InfoShort; 46 | using InfoFull = Search::InfoFull; 47 | using InfoIter = Search::InfoIteration; 48 | 49 | Engine(std::string path = ""); 50 | ~Engine() { wait_for_search_finished(); } 51 | 52 | std::uint64_t perft(const std::string& fen, Depth depth, bool isChess960); 53 | 54 | // non blocking call to start searching 55 | void go(Search::LimitsType&); 56 | // non blocking call to stop searching 57 | void stop(); 58 | 59 | // blocking call to wait for search to finish 60 | void wait_for_search_finished(); 61 | // set a new position, moves are in UCI format 62 | void set_position(const std::string& fen, const std::vector& moves); 63 | 64 | // modifiers 65 | 66 | void resize_threads(); 67 | void set_tt_size(size_t mb); 68 | void set_ponderhit(bool); 69 | void search_clear(); 70 | 71 | void set_on_update_no_moves(std::function&&); 72 | void set_on_update_full(std::function&&); 73 | void set_on_iter(std::function&&); 74 | void set_on_bestmove(std::function&&); 75 | 76 | // network related 77 | 78 | void verify_networks() const; 79 | void load_networks(); 80 | void load_big_network(const std::string& file); 81 | void load_small_network(const std::string& file); 82 | void save_network(const std::pair, std::string> files[2]); 83 | 84 | // utility functions 85 | 86 | void trace_eval() const; 87 | OptionsMap& get_options(); 88 | std::string fen() const; 89 | void flip(); 90 | std::string visualize() const; 91 | 92 | private: 93 | const std::string binaryDirectory; 94 | 95 | Position pos; 96 | StateListPtr states; 97 | Square capSq; 98 | 99 | OptionsMap options; 100 | ThreadPool threads; 101 | TranspositionTable tt; 102 | Eval::NNUE::Networks networks; 103 | 104 | Search::SearchManager::UpdateContext updateContext; 105 | }; 106 | 107 | } // namespace Stockfish 108 | 109 | 110 | #endif // #ifndef ENGINE_H_INCLUDED 111 | -------------------------------------------------------------------------------- /src/tune.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | 27 | #include "ucioption.h" 28 | 29 | using std::string; 30 | 31 | namespace Stockfish { 32 | 33 | bool Tune::update_on_last; 34 | const Option* LastOption = nullptr; 35 | OptionsMap* Tune::options; 36 | 37 | 38 | namespace { 39 | std::map TuneResults; 40 | 41 | void on_tune(const Option& o) { 42 | 43 | if (!Tune::update_on_last || LastOption == &o) 44 | Tune::read_options(); 45 | } 46 | 47 | 48 | void make_option(OptionsMap* options, const string& n, int v, const SetRange& r) { 49 | 50 | // Do not generate option when there is nothing to tune (ie. min = max) 51 | if (r(v).first == r(v).second) 52 | return; 53 | 54 | if (TuneResults.count(n)) 55 | v = TuneResults[n]; 56 | 57 | (*options)[n] << Option(v, r(v).first, r(v).second, on_tune); 58 | LastOption = &((*options)[n]); 59 | 60 | // Print formatted parameters, ready to be copy-pasted in Fishtest 61 | std::cout << n << "," << v << "," << r(v).first << "," << r(v).second << "," 62 | << (r(v).second - r(v).first) / 20.0 << "," 63 | << "0.0020" << std::endl; 64 | } 65 | } 66 | 67 | string Tune::next(string& names, bool pop) { 68 | 69 | string name; 70 | 71 | do 72 | { 73 | string token = names.substr(0, names.find(',')); 74 | 75 | if (pop) 76 | names.erase(0, token.size() + 1); 77 | 78 | std::stringstream ws(token); 79 | name += (ws >> token, token); // Remove trailing whitespace 80 | 81 | } while (std::count(name.begin(), name.end(), '(') - std::count(name.begin(), name.end(), ')')); 82 | 83 | return name; 84 | } 85 | 86 | 87 | template<> 88 | void Tune::Entry::init_option() { 89 | make_option(options, name, value, range); 90 | } 91 | 92 | template<> 93 | void Tune::Entry::read_option() { 94 | if (options->count(name)) 95 | value = int((*options)[name]); 96 | } 97 | 98 | // Instead of a variable here we have a PostUpdate function: just call it 99 | template<> 100 | void Tune::Entry::init_option() {} 101 | template<> 102 | void Tune::Entry::read_option() { 103 | value(); 104 | } 105 | 106 | } // namespace Stockfish 107 | 108 | 109 | // Init options with tuning session results instead of default values. Useful to 110 | // get correct bench signature after a tuning session or to test tuned values. 111 | // Just copy fishtest tuning results in a result.txt file and extract the 112 | // values with: 113 | // 114 | // cat results.txt | sed 's/^param: \([^,]*\), best: \([^,]*\).*/ TuneResults["\1"] = int(round(\2));/' 115 | // 116 | // Then paste the output below, as the function body 117 | 118 | 119 | namespace Stockfish { 120 | 121 | void Tune::read_results() { /* ...insert your values here... */ 122 | } 123 | 124 | } // namespace Stockfish 125 | -------------------------------------------------------------------------------- /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 x86_64 architecture 30 | set_arch_x86_64() { 31 | if check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then 32 | true_arch='x86-64-vnni256' 33 | elif check_flags 'avx512f' 'avx512bw'; then 34 | true_arch='x86-64-avx512' 35 | elif [ -z "${znver_1_2+1}" ] && check_flags 'bmi2'; then 36 | true_arch='x86-64-bmi2' 37 | elif check_flags 'avx2'; then 38 | true_arch='x86-64-avx2' 39 | elif check_flags 'sse41' && check_flags 'popcnt'; then 40 | true_arch='x86-64-sse41-popcnt' 41 | else 42 | true_arch='x86-64' 43 | fi 44 | } 45 | 46 | # Check the system type 47 | uname_s=$(uname -s) 48 | uname_m=$(uname -m) 49 | case $uname_s in 50 | 'Darwin') # Mac OSX system 51 | case $uname_m in 52 | 'arm64') 53 | true_arch='apple-silicon' 54 | file_arch='x86-64-sse41-popcnt' # Supported by Rosetta 2 55 | ;; 56 | 'x86_64') 57 | flags=$(sysctl -n machdep.cpu.features machdep.cpu.leaf7_features | tr '\n' ' ' | tr '[:upper:]' '[:lower:]' | tr -d '_.') 58 | set_arch_x86_64 59 | if [ "$true_arch" = 'x86-64-vnni256' ] || [ "$true_arch" = 'x86-64-avx512' ]; then 60 | file_arch='x86-64-bmi2' 61 | fi 62 | ;; 63 | esac 64 | file_os='macos' 65 | file_ext='tar' 66 | ;; 67 | 'Linux') # Linux system 68 | get_flags 69 | case $uname_m in 70 | 'x86_64') 71 | file_os='ubuntu' 72 | check_znver_1_2 73 | set_arch_x86_64 74 | ;; 75 | 'i686') 76 | file_os='ubuntu' 77 | true_arch='x86-32' 78 | ;; 79 | 'aarch64') 80 | file_os='android' 81 | true_arch='armv8' 82 | if check_flags 'asimddp'; then 83 | true_arch="$true_arch-dotprod" 84 | fi 85 | ;; 86 | 'armv7'*) 87 | file_os='android' 88 | true_arch='armv7' 89 | if check_flags 'neon'; then 90 | true_arch="$true_arch-neon" 91 | fi 92 | ;; 93 | *) # Unsupported machine type, exit with error 94 | printf 'Unsupported machine type: %s\n' "$uname_m" 95 | exit 1 96 | ;; 97 | esac 98 | file_ext='tar' 99 | ;; 100 | 'CYGWIN'*|'MINGW'*|'MSYS'*) # Windows system with POSIX compatibility layer 101 | get_flags 102 | check_znver_1_2 103 | set_arch_x86_64 104 | file_os='windows' 105 | file_ext='zip' 106 | ;; 107 | *) 108 | # Unknown system type, exit with error 109 | printf 'Unsupported system type: %s\n' "$uname_s" 110 | exit 1 111 | ;; 112 | esac 113 | 114 | if [ -z "$file_arch" ]; then 115 | file_arch=$true_arch 116 | fi 117 | 118 | file_name="stockfish-$file_os-$file_arch.$file_ext" 119 | 120 | printf '%s %s\n' "$true_arch" "$file_name" 121 | -------------------------------------------------------------------------------- /.github/ci/matrix.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": [ 3 | { 4 | "name": "Ubuntu 20.04 GCC", 5 | "os": "ubuntu-20.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-avxvnni", 89 | "config": { 90 | "os": "macos-14" 91 | } 92 | }, 93 | { 94 | "binaries": "x86-64-avx512", 95 | "config": { 96 | "os": "macos-14" 97 | } 98 | }, 99 | { 100 | "binaries": "x86-64-vnni256", 101 | "config": { 102 | "os": "macos-14" 103 | } 104 | }, 105 | { 106 | "binaries": "x86-64-vnni512", 107 | "config": { 108 | "os": "macos-14" 109 | } 110 | }, 111 | { 112 | "binaries": "x86-64-avxvnni", 113 | "config": { 114 | "ubuntu-20.04": null 115 | } 116 | }, 117 | { 118 | "binaries": "x86-64-avxvnni", 119 | "config": { 120 | "os": "macos-13" 121 | } 122 | }, 123 | { 124 | "binaries": "x86-64-avx512", 125 | "config": { 126 | "os": "macos-13" 127 | } 128 | }, 129 | { 130 | "binaries": "x86-64-vnni256", 131 | "config": { 132 | "os": "macos-13" 133 | } 134 | }, 135 | { 136 | "binaries": "x86-64-vnni512", 137 | "config": { 138 | "os": "macos-13" 139 | } 140 | }, 141 | { 142 | "binaries": "apple-silicon", 143 | "config": { 144 | "os": "windows-2022" 145 | } 146 | }, 147 | { 148 | "binaries": "apple-silicon", 149 | "config": { 150 | "os": "macos-13" 151 | } 152 | }, 153 | { 154 | "binaries": "apple-silicon", 155 | "config": { 156 | "os": "ubuntu-20.04" 157 | } 158 | } 159 | ] 160 | } 161 | -------------------------------------------------------------------------------- /.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 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | # returns null if no pre-release exists 22 | - name: Get Commit SHA of Latest Pre-release 23 | run: | 24 | # Install required packages 25 | sudo apt-get update 26 | sudo apt-get install -y curl jq 27 | 28 | 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 29 | 30 | # delete old previous pre-release and tag 31 | - run: gh release delete ${{ env.COMMIT_SHA_TAG }} --cleanup-tag 32 | if: env.COMMIT_SHA_TAG != 'null' 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | 36 | # Make sure that an old ci that still runs on master doesn't recreate a prerelease 37 | - name: Check Pullable Commits 38 | id: check_commits 39 | run: | 40 | git fetch 41 | CHANGES=$(git rev-list HEAD..origin/master --count) 42 | echo "CHANGES=$CHANGES" >> $GITHUB_ENV 43 | 44 | - name: Get last commit SHA 45 | id: last_commit 46 | run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV 47 | 48 | - name: Get commit date 49 | id: commit_date 50 | run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV 51 | 52 | # Create a new pre-release, the other upload_binaries.yml will upload the binaries 53 | # to this pre-release. 54 | - name: Create Prerelease 55 | if: github.ref_name == 'master' && env.CHANGES == '0' 56 | uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981 57 | with: 58 | name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} 59 | tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} 60 | prerelease: true 61 | 62 | Matrix: 63 | runs-on: ubuntu-latest 64 | outputs: 65 | matrix: ${{ steps.set-matrix.outputs.matrix }} 66 | arm_matrix: ${{ steps.set-arm-matrix.outputs.arm_matrix }} 67 | steps: 68 | - uses: actions/checkout@v4 69 | - id: set-matrix 70 | run: | 71 | TASKS=$(echo $(cat .github/ci/matrix.json) ) 72 | echo "MATRIX=$TASKS" >> $GITHUB_OUTPUT 73 | - id: set-arm-matrix 74 | run: | 75 | TASKS_ARM=$(echo $(cat .github/ci/arm_matrix.json) ) 76 | echo "ARM_MATRIX=$TASKS_ARM" >> $GITHUB_OUTPUT 77 | Compilation: 78 | needs: [Matrix] 79 | uses: ./.github/workflows/compilation.yml 80 | with: 81 | matrix: ${{ needs.Matrix.outputs.matrix }} 82 | ARMCompilation: 83 | needs: [Matrix] 84 | uses: ./.github/workflows/arm_compilation.yml 85 | with: 86 | matrix: ${{ needs.Matrix.outputs.arm_matrix }} 87 | IWYU: 88 | uses: ./.github/workflows/iwyu.yml 89 | Sanitizers: 90 | uses: ./.github/workflows/sanitizers.yml 91 | Tests: 92 | uses: ./.github/workflows/tests.yml 93 | Binaries: 94 | if: github.repository == 'official-stockfish/Stockfish' 95 | needs: [Matrix, Prerelease, Compilation] 96 | uses: ./.github/workflows/upload_binaries.yml 97 | with: 98 | matrix: ${{ needs.Matrix.outputs.matrix }} 99 | ARM_Binaries: 100 | if: github.repository == 'official-stockfish/Stockfish' 101 | needs: [Matrix, Prerelease, ARMCompilation] 102 | uses: ./.github/workflows/upload_binaries.yml 103 | with: 104 | matrix: ${{ needs.Matrix.outputs.arm_matrix }} 105 | -------------------------------------------------------------------------------- /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 17 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 | 90 | [copying-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt 91 | [discord-link]: https://discord.gg/GWDRS3kU6R 92 | [discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new 93 | [creating-my-first-test]: https://github.com/official-stockfish/fishtest/wiki/Creating-my-first-test#create-your-test 94 | [issue-tracker-link]: https://github.com/official-stockfish/Stockfish/issues 95 | [ubuntu-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Developers#user-content-installing-a-compiler-1 96 | [windows-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Developers#user-content-installing-a-compiler 97 | [macos-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Developers#user-content-installing-a-compiler-2 98 | -------------------------------------------------------------------------------- /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-2024 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 | int HalfKAv2_hm::update_cost(const StateInfo* st) { return st->dirtyPiece.dirty_num; } 81 | 82 | int HalfKAv2_hm::refresh_cost(const Position& pos) { return pos.count(); } 83 | 84 | bool HalfKAv2_hm::requires_refresh(const StateInfo* st, Color perspective) { 85 | return st->dirtyPiece.piece[0] == make_piece(perspective, KING); 86 | } 87 | 88 | } // namespace Stockfish::Eval::NNUE::Features 89 | -------------------------------------------------------------------------------- /src/nnue/nnue_accumulator.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | 26 | #include "nnue_architecture.h" 27 | #include "nnue_common.h" 28 | 29 | namespace Stockfish::Eval::NNUE { 30 | 31 | using BiasType = std::int16_t; 32 | using PSQTWeightType = std::int32_t; 33 | using IndexType = std::uint32_t; 34 | 35 | // Class that holds the result of affine transformation of input features 36 | template 37 | struct alignas(CacheLineSize) Accumulator { 38 | std::int16_t accumulation[COLOR_NB][Size]; 39 | std::int32_t psqtAccumulation[COLOR_NB][PSQTBuckets]; 40 | bool computed[COLOR_NB]; 41 | }; 42 | 43 | 44 | // AccumulatorCaches struct provides per-thread accumulator caches, where each 45 | // cache contains multiple entries for each of the possible king squares. 46 | // When the accumulator needs to be refreshed, the cached entry is used to more 47 | // efficiently update the accumulator, instead of rebuilding it from scratch. 48 | // This idea, was first described by Luecx (author of Koivisto) and 49 | // is commonly referred to as "Finny Tables". 50 | struct AccumulatorCaches { 51 | 52 | template 53 | AccumulatorCaches(const Networks& networks) { 54 | clear(networks); 55 | } 56 | 57 | template 58 | struct alignas(CacheLineSize) Cache { 59 | 60 | struct alignas(CacheLineSize) Entry { 61 | BiasType accumulation[Size]; 62 | PSQTWeightType psqtAccumulation[PSQTBuckets]; 63 | Bitboard byColorBB[COLOR_NB]; 64 | Bitboard byTypeBB[PIECE_TYPE_NB]; 65 | 66 | // To initialize a refresh entry, we set all its bitboards empty, 67 | // so we put the biases in the accumulation, without any weights on top 68 | void clear(const BiasType* biases) { 69 | 70 | std::memcpy(accumulation, biases, sizeof(accumulation)); 71 | std::memset((uint8_t*) this + offsetof(Entry, psqtAccumulation), 0, 72 | sizeof(Entry) - offsetof(Entry, psqtAccumulation)); 73 | } 74 | }; 75 | 76 | template 77 | void clear(const Network& network) { 78 | for (auto& entries1D : entries) 79 | for (auto& entry : entries1D) 80 | entry.clear(network.featureTransformer->biases); 81 | } 82 | 83 | void clear(const BiasType* biases) { 84 | for (auto& entry : entries) 85 | entry.clear(biases); 86 | } 87 | 88 | std::array& operator[](Square sq) { return entries[sq]; } 89 | 90 | std::array, SQUARE_NB> entries; 91 | }; 92 | 93 | template 94 | void clear(const Networks& networks) { 95 | big.clear(networks.big); 96 | small.clear(networks.small); 97 | } 98 | 99 | Cache big; 100 | Cache small; 101 | }; 102 | 103 | } // namespace Stockfish::Eval::NNUE 104 | 105 | #endif // NNUE_ACCUMULATOR_H_INCLUDED 106 | -------------------------------------------------------------------------------- /.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 | 9 | jobs: 10 | Artifacts: 11 | name: ${{ matrix.config.name }} ${{ matrix.binaries }} 12 | runs-on: ${{ matrix.config.os }} 13 | env: 14 | COMPCXX: ${{ matrix.config.compiler }} 15 | COMP: ${{ matrix.config.comp }} 16 | EXT: ${{ matrix.config.ext }} 17 | NAME: ${{ matrix.config.simple_name }} 18 | BINARY: ${{ matrix.binaries }} 19 | SDE: ${{ matrix.config.sde }} 20 | strategy: 21 | fail-fast: false 22 | matrix: ${{ fromJson(inputs.matrix) }} 23 | defaults: 24 | run: 25 | shell: ${{ matrix.config.shell }} 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - name: Download artifact from compilation 30 | uses: actions/download-artifact@v4 31 | with: 32 | name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} 33 | path: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} 34 | 35 | - name: Setup msys and install required packages 36 | if: runner.os == 'Windows' 37 | uses: msys2/setup-msys2@v2 38 | with: 39 | msystem: ${{ matrix.config.msys_sys }} 40 | install: mingw-w64-${{ matrix.config.msys_env }} make git zip 41 | 42 | - name: Create Package 43 | run: | 44 | mkdir stockfish 45 | 46 | - name: Download wiki 47 | run: | 48 | git clone https://github.com/official-stockfish/Stockfish.wiki.git wiki 49 | rm -rf wiki/.git 50 | mv wiki stockfish/ 51 | 52 | - name: Copy files 53 | run: | 54 | mv "${{ matrix.config.simple_name }} ${{ matrix.binaries }}" stockfish-workflow 55 | cd stockfish-workflow 56 | cp -r src ../stockfish/ 57 | cp stockfish-$NAME-$BINARY$EXT ../stockfish/ 58 | cp "Top CPU Contributors.txt" ../stockfish/ 59 | cp Copying.txt ../stockfish/ 60 | cp AUTHORS ../stockfish/ 61 | cp CITATION.cff ../stockfish/ 62 | cp README.md ../stockfish/ 63 | cp CONTRIBUTING.md ../stockfish/ 64 | 65 | - name: Create tar 66 | if: runner.os != 'Windows' 67 | run: | 68 | chmod +x ./stockfish/stockfish-$NAME-$BINARY$EXT 69 | tar -cvf stockfish-$NAME-$BINARY.tar stockfish 70 | 71 | - name: Create zip 72 | if: runner.os == 'Windows' 73 | run: | 74 | zip -r stockfish-$NAME-$BINARY.zip stockfish 75 | 76 | - name: Release 77 | if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag' 78 | uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981 79 | with: 80 | files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} 81 | 82 | - name: Get last commit sha 83 | id: last_commit 84 | run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV 85 | 86 | - name: Get commit date 87 | id: commit_date 88 | run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV 89 | 90 | # Make sure that an old ci that still runs on master doesn't recreate a prerelease 91 | - name: Check Pullable Commits 92 | id: check_commits 93 | run: | 94 | git fetch 95 | CHANGES=$(git rev-list HEAD..origin/master --count) 96 | echo "CHANGES=$CHANGES" >> $GITHUB_ENV 97 | 98 | - name: Prerelease 99 | if: github.ref_name == 'master' && env.CHANGES == '0' 100 | continue-on-error: true 101 | uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981 102 | with: 103 | name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} 104 | tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} 105 | prerelease: true 106 | files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} 107 | -------------------------------------------------------------------------------- /src/nnue/layers/sqr_clipped_relu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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/tt.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | 25 | #include "misc.h" 26 | #include "types.h" 27 | 28 | namespace Stockfish { 29 | 30 | // TTEntry struct is the 10 bytes transposition table entry, defined as below: 31 | // 32 | // key 16 bit 33 | // depth 8 bit 34 | // generation 5 bit 35 | // pv node 1 bit 36 | // bound type 2 bit 37 | // move 16 bit 38 | // value 16 bit 39 | // eval value 16 bit 40 | struct TTEntry { 41 | 42 | Move move() const { return Move(move16); } 43 | Value value() const { return Value(value16); } 44 | Value eval() const { return Value(eval16); } 45 | Depth depth() const { return Depth(depth8 + DEPTH_OFFSET); } 46 | bool is_pv() const { return bool(genBound8 & 0x4); } 47 | Bound bound() const { return Bound(genBound8 & 0x3); } 48 | void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev, uint8_t generation8); 49 | // The returned age is a multiple of TranspositionTable::GENERATION_DELTA 50 | uint8_t relative_age(const uint8_t generation8) const; 51 | 52 | private: 53 | friend class TranspositionTable; 54 | 55 | uint16_t key16; 56 | uint8_t depth8; 57 | uint8_t genBound8; 58 | Move move16; 59 | int16_t value16; 60 | int16_t eval16; 61 | }; 62 | 63 | 64 | // A TranspositionTable is an array of Cluster, of size clusterCount. Each 65 | // cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry 66 | // contains information on exactly one position. The size of a Cluster should 67 | // divide the size of a cache line for best performance, as the cacheline is 68 | // prefetched when possible. 69 | class TranspositionTable { 70 | 71 | static constexpr int ClusterSize = 3; 72 | 73 | struct Cluster { 74 | TTEntry entry[ClusterSize]; 75 | char padding[2]; // Pad to 32 bytes 76 | }; 77 | 78 | static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size"); 79 | 80 | // Constants used to refresh the hash table periodically 81 | 82 | // We have 8 bits available where the lowest 3 bits are 83 | // reserved for other things. 84 | static constexpr unsigned GENERATION_BITS = 3; 85 | // increment for generation field 86 | static constexpr int GENERATION_DELTA = (1 << GENERATION_BITS); 87 | // cycle length 88 | static constexpr int GENERATION_CYCLE = 255 + GENERATION_DELTA; 89 | // mask to pull out generation number 90 | static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF; 91 | 92 | public: 93 | ~TranspositionTable() { aligned_large_pages_free(table); } 94 | 95 | void new_search() { 96 | // increment by delta to keep lower bits as is 97 | generation8 += GENERATION_DELTA; 98 | } 99 | 100 | TTEntry* probe(const Key key, bool& found) const; 101 | int hashfull() const; 102 | void resize(size_t mbSize, int threadCount); 103 | void clear(size_t threadCount); 104 | 105 | TTEntry* first_entry(const Key key) const { 106 | return &table[mul_hi64(key, clusterCount)].entry[0]; 107 | } 108 | 109 | uint8_t generation() const { return generation8; } 110 | 111 | private: 112 | friend struct TTEntry; 113 | 114 | size_t clusterCount; 115 | Cluster* table = nullptr; 116 | uint8_t generation8 = 0; // Size must be not bigger than TTEntry::genBound8 117 | }; 118 | 119 | } // namespace Stockfish 120 | 121 | #endif // #ifndef TT_H_INCLUDED 122 | -------------------------------------------------------------------------------- /src/thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | 30 | #include "position.h" 31 | #include "search.h" 32 | #include "thread_win32_osx.h" 33 | 34 | namespace Stockfish { 35 | 36 | 37 | class OptionsMap; 38 | using Value = int; 39 | 40 | // Abstraction of a thread. It contains a pointer to the worker and a native thread. 41 | // After construction, the native thread is started with idle_loop() 42 | // waiting for a signal to start searching. 43 | // When the signal is received, the thread starts searching and when 44 | // the search is finished, it goes back to idle_loop() waiting for a new signal. 45 | class Thread { 46 | public: 47 | Thread(Search::SharedState&, std::unique_ptr, size_t); 48 | virtual ~Thread(); 49 | 50 | void idle_loop(); 51 | void start_searching(); 52 | void wait_for_search_finished(); 53 | size_t id() const { return idx; } 54 | 55 | std::unique_ptr worker; 56 | 57 | private: 58 | std::mutex mutex; 59 | std::condition_variable cv; 60 | size_t idx, nthreads; 61 | bool exit = false, searching = true; // Set before starting std::thread 62 | NativeThread stdThread; 63 | }; 64 | 65 | 66 | // ThreadPool struct handles all the threads-related stuff like init, starting, 67 | // parking and, most importantly, launching a thread. All the access to threads 68 | // is done through this class. 69 | class ThreadPool { 70 | 71 | public: 72 | ~ThreadPool() { 73 | // destroy any existing thread(s) 74 | if (threads.size() > 0) 75 | { 76 | main_thread()->wait_for_search_finished(); 77 | 78 | while (threads.size() > 0) 79 | delete threads.back(), threads.pop_back(); 80 | } 81 | } 82 | 83 | void start_thinking(const OptionsMap&, Position&, StateListPtr&, Search::LimitsType); 84 | void clear(); 85 | void set(Search::SharedState, const Search::SearchManager::UpdateContext&); 86 | 87 | Search::SearchManager* main_manager(); 88 | Thread* main_thread() const { return threads.front(); } 89 | uint64_t nodes_searched() const; 90 | uint64_t tb_hits() const; 91 | Thread* get_best_thread() const; 92 | void start_searching(); 93 | void wait_for_search_finished() const; 94 | 95 | std::atomic_bool stop, abortedSearch, increaseDepth; 96 | 97 | auto cbegin() const noexcept { return threads.cbegin(); } 98 | auto begin() noexcept { return threads.begin(); } 99 | auto end() noexcept { return threads.end(); } 100 | auto cend() const noexcept { return threads.cend(); } 101 | auto size() const noexcept { return threads.size(); } 102 | auto empty() const noexcept { return threads.empty(); } 103 | 104 | private: 105 | StateListPtr setupStates; 106 | std::vector threads; 107 | 108 | uint64_t accumulate(std::atomic Search::Worker::*member) const { 109 | 110 | uint64_t sum = 0; 111 | for (Thread* th : threads) 112 | sum += (th->worker.get()->*member).load(std::memory_order_relaxed); 113 | return sum; 114 | } 115 | }; 116 | 117 | } // namespace Stockfish 118 | 119 | #endif // #ifndef THREAD_H_INCLUDED 120 | -------------------------------------------------------------------------------- /src/nnue/network.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | 28 | #include "../misc.h" 29 | #include "../position.h" 30 | #include "../types.h" 31 | #include "nnue_architecture.h" 32 | #include "nnue_feature_transformer.h" 33 | #include "nnue_misc.h" 34 | #include "nnue_accumulator.h" 35 | 36 | namespace Stockfish::Eval::NNUE { 37 | 38 | enum class EmbeddedNNUEType { 39 | BIG, 40 | SMALL, 41 | }; 42 | 43 | 44 | template 45 | class Network { 46 | static constexpr IndexType FTDimensions = Arch::TransformedFeatureDimensions; 47 | 48 | public: 49 | Network(EvalFile file, EmbeddedNNUEType type) : 50 | evalFile(file), 51 | embeddedType(type) {} 52 | 53 | void load(const std::string& rootDirectory, std::string evalfilePath); 54 | bool save(const std::optional& filename) const; 55 | 56 | Value evaluate(const Position& pos, 57 | AccumulatorCaches::Cache* cache, 58 | bool adjusted = false, 59 | int* complexity = nullptr) const; 60 | 61 | 62 | void hint_common_access(const Position& pos, 63 | AccumulatorCaches::Cache* cache) const; 64 | 65 | void verify(std::string evalfilePath) const; 66 | NnueEvalTrace trace_evaluate(const Position& pos, 67 | AccumulatorCaches::Cache* cache) const; 68 | 69 | private: 70 | void load_user_net(const std::string&, const std::string&); 71 | void load_internal(); 72 | 73 | void initialize(); 74 | 75 | bool save(std::ostream&, const std::string&, const std::string&) const; 76 | std::optional load(std::istream&); 77 | 78 | bool read_header(std::istream&, std::uint32_t*, std::string*) const; 79 | bool write_header(std::ostream&, std::uint32_t, const std::string&) const; 80 | 81 | bool read_parameters(std::istream&, std::string&) const; 82 | bool write_parameters(std::ostream&, const std::string&) const; 83 | 84 | // Input feature converter 85 | LargePagePtr featureTransformer; 86 | 87 | // Evaluation function 88 | AlignedPtr network[LayerStacks]; 89 | 90 | EvalFile evalFile; 91 | EmbeddedNNUEType embeddedType; 92 | 93 | // Hash value of evaluation function structure 94 | static constexpr std::uint32_t hash = Transformer::get_hash_value() ^ Arch::get_hash_value(); 95 | 96 | template 97 | friend struct AccumulatorCaches::Cache; 98 | }; 99 | 100 | // Definitions of the network types 101 | using SmallFeatureTransformer = 102 | FeatureTransformer; 103 | using SmallNetworkArchitecture = 104 | NetworkArchitecture; 105 | 106 | using BigFeatureTransformer = 107 | FeatureTransformer; 108 | using BigNetworkArchitecture = NetworkArchitecture; 109 | 110 | using NetworkBig = Network; 111 | using NetworkSmall = Network; 112 | 113 | 114 | struct Networks { 115 | Networks(NetworkBig&& nB, NetworkSmall&& nS) : 116 | big(std::move(nB)), 117 | small(std::move(nS)) {} 118 | 119 | NetworkBig big; 120 | NetworkSmall small; 121 | }; 122 | 123 | 124 | } // namespace Stockfish 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /src/evaluate.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | 30 | #include "nnue/network.h" 31 | #include "nnue/nnue_misc.h" 32 | #include "position.h" 33 | #include "types.h" 34 | #include "uci.h" 35 | #include "nnue/nnue_accumulator.h" 36 | 37 | namespace Stockfish { 38 | 39 | // Returns a static, purely materialistic evaluation of the position from 40 | // the point of view of the given color. It can be divided by PawnValue to get 41 | // an approximation of the material advantage on the board in terms of pawns. 42 | int Eval::simple_eval(const Position& pos, Color c) { 43 | return PawnValue * (pos.count(c) - pos.count(~c)) 44 | + (pos.non_pawn_material(c) - pos.non_pawn_material(~c)); 45 | } 46 | 47 | bool Eval::use_smallnet(const Position& pos) { 48 | int simpleEval = simple_eval(pos, pos.side_to_move()); 49 | return std::abs(simpleEval) > 1126 + 6 * pos.count(); 50 | } 51 | 52 | // Evaluate is the evaluator for the outer world. It returns a static evaluation 53 | // of the position from the point of view of the side to move. 54 | Value Eval::evaluate(const Eval::NNUE::Networks& networks, 55 | const Position& pos, 56 | Eval::NNUE::AccumulatorCaches& caches, 57 | int optimism) { 58 | 59 | assert(!pos.checkers()); 60 | 61 | int simpleEval = simple_eval(pos, pos.side_to_move()); 62 | bool smallNet = use_smallnet(pos); 63 | int nnueComplexity; 64 | int v; 65 | 66 | Value nnue = smallNet ? networks.small.evaluate(pos, &caches.small, true, &nnueComplexity) 67 | : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); 68 | 69 | if (smallNet && (nnue * simpleEval < 0 || std::abs(nnue) < 500)) 70 | { 71 | nnue = networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); 72 | smallNet = false; 73 | } 74 | 75 | // Blend optimism and eval with nnue complexity and material imbalance 76 | optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 584; 77 | nnue -= nnue * (nnueComplexity * 5 / 3) / 32395; 78 | 79 | int npm = pos.non_pawn_material() / 64; 80 | v = (nnue * (npm + 943 + 11 * pos.count()) + optimism * (npm + 140)) / 1058; 81 | 82 | // Damp down the evaluation linearly when shuffling 83 | v = v * ((smallNet ? 206 : 178) - pos.rule50_count()) / 207; 84 | 85 | // Guarantee evaluation does not hit the tablebase range 86 | v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); 87 | 88 | return v; 89 | } 90 | 91 | // Like evaluate(), but instead of returning a value, it returns 92 | // a string (suitable for outputting to stdout) that contains the detailed 93 | // descriptions and values of each evaluation term. Useful for debugging. 94 | // Trace scores are from white's point of view 95 | std::string Eval::trace(Position& pos, const Eval::NNUE::Networks& networks) { 96 | 97 | if (pos.checkers()) 98 | return "Final evaluation: none (in check)"; 99 | 100 | auto caches = std::make_unique(networks); 101 | 102 | std::stringstream ss; 103 | ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); 104 | ss << '\n' << NNUE::trace(pos, networks, *caches) << '\n'; 105 | 106 | ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15); 107 | 108 | Value v = networks.big.evaluate(pos, &caches->big, false); 109 | v = pos.side_to_move() == WHITE ? v : -v; 110 | ss << "NNUE evaluation " << 0.01 * UCIEngine::to_cp(v, pos) << " (white side)\n"; 111 | 112 | v = evaluate(networks, pos, *caches, VALUE_ZERO); 113 | v = pos.side_to_move() == WHITE ? v : -v; 114 | ss << "Final evaluation " << 0.01 * UCIEngine::to_cp(v, pos) << " (white side)"; 115 | ss << " [with scaled NNUE, ...]"; 116 | ss << "\n"; 117 | 118 | return ss.str(); 119 | } 120 | 121 | } // namespace Stockfish 122 | -------------------------------------------------------------------------------- /src/timeman.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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( 48 | Search::LimitsType& limits, Color us, int ply, const OptionsMap& options, int& originalPly) { 49 | TimePoint npmsec = TimePoint(options["nodestime"]); 50 | 51 | // If we have no time, we don't need to fully initialize TM. 52 | // startTime is used by movetime and useNodesTime is used in elapsed calls. 53 | startTime = limits.startTime; 54 | useNodesTime = npmsec != 0; 55 | 56 | if (limits.time[us] == 0) 57 | return; 58 | 59 | if (originalPly == -1) 60 | originalPly = ply; 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 of 50 moves 91 | int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50; 92 | 93 | // If less than one second, gradually reduce mtg 94 | if (scaledTime < 1000 && double(mtg) / scaledInc > 0.05) 95 | { 96 | mtg = scaledTime * 0.05; 97 | } 98 | 99 | // Make sure timeLeft is > 0 since we may use it as a divisor 100 | TimePoint timeLeft = std::max(TimePoint(1), limits.time[us] + limits.inc[us] * (mtg - 1) 101 | - moveOverhead * (2 + mtg)); 102 | 103 | // x basetime (+ z increment) 104 | // If there is a healthy increment, timeLeft can exceed the actual available 105 | // game time for the current move, so also cap to a percentage of available game time. 106 | if (limits.movestogo == 0) 107 | { 108 | // Use extra time with larger increments 109 | double optExtra = scaledInc < 500 ? 1.0 : 1.13; 110 | if (ply - originalPly < 2) 111 | optExtra *= 0.95; 112 | 113 | // Calculate time constants based on current time left. 114 | double logTimeInSec = std::log10(scaledTime / 1000.0); 115 | double optConstant = std::min(0.00308 + 0.000319 * logTimeInSec, 0.00506); 116 | double maxConstant = std::max(3.39 + 3.01 * logTimeInSec, 2.93); 117 | 118 | optScale = std::min(0.0122 + std::pow(ply + 2.95, 0.462) * optConstant, 119 | 0.213 * limits.time[us] / timeLeft) 120 | * optExtra; 121 | maxScale = std::min(6.64, maxConstant + ply / 12.0); 122 | } 123 | 124 | // x moves in y seconds (+ z increment) 125 | else 126 | { 127 | optScale = std::min((0.88 + ply / 116.4) / mtg, 0.88 * limits.time[us] / timeLeft); 128 | maxScale = std::min(6.3, 1.5 + 0.11 * mtg); 129 | } 130 | 131 | // Limit the maximum possible time for this move 132 | optimumTime = TimePoint(optScale * timeLeft); 133 | maximumTime = 134 | TimePoint(std::min(0.825 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10; 135 | 136 | if (options["Ponder"]) 137 | optimumTime += optimumTime / 4; 138 | } 139 | 140 | } // namespace Stockfish 141 | -------------------------------------------------------------------------------- /src/ucioption.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | 28 | #include "misc.h" 29 | 30 | namespace Stockfish { 31 | 32 | bool CaseInsensitiveLess::operator()(const std::string& s1, const std::string& s2) const { 33 | 34 | return std::lexicographical_compare( 35 | s1.begin(), s1.end(), s2.begin(), s2.end(), 36 | [](char c1, char c2) { return std::tolower(c1) < std::tolower(c2); }); 37 | } 38 | 39 | void OptionsMap::setoption(std::istringstream& is) { 40 | std::string token, name, value; 41 | 42 | is >> token; // Consume the "name" token 43 | 44 | // Read the option name (can contain spaces) 45 | while (is >> token && token != "value") 46 | name += (name.empty() ? "" : " ") + token; 47 | 48 | // Read the option value (can contain spaces) 49 | while (is >> token) 50 | value += (value.empty() ? "" : " ") + token; 51 | 52 | if (options_map.count(name)) 53 | options_map[name] = value; 54 | else 55 | sync_cout << "No such option: " << name << sync_endl; 56 | } 57 | 58 | Option OptionsMap::operator[](const std::string& name) const { 59 | auto it = options_map.find(name); 60 | return it != options_map.end() ? it->second : Option(); 61 | } 62 | 63 | Option& OptionsMap::operator[](const std::string& name) { return options_map[name]; } 64 | 65 | std::size_t OptionsMap::count(const std::string& name) const { return options_map.count(name); } 66 | 67 | Option::Option(const char* v, OnChange f) : 68 | type("string"), 69 | min(0), 70 | max(0), 71 | on_change(std::move(f)) { 72 | defaultValue = currentValue = v; 73 | } 74 | 75 | Option::Option(bool v, OnChange f) : 76 | type("check"), 77 | min(0), 78 | max(0), 79 | on_change(std::move(f)) { 80 | defaultValue = currentValue = (v ? "true" : "false"); 81 | } 82 | 83 | Option::Option(OnChange f) : 84 | type("button"), 85 | min(0), 86 | max(0), 87 | on_change(std::move(f)) {} 88 | 89 | Option::Option(double v, int minv, int maxv, OnChange f) : 90 | type("spin"), 91 | min(minv), 92 | max(maxv), 93 | on_change(std::move(f)) { 94 | defaultValue = currentValue = std::to_string(v); 95 | } 96 | 97 | Option::Option(const char* v, const char* cur, OnChange f) : 98 | type("combo"), 99 | min(0), 100 | max(0), 101 | on_change(std::move(f)) { 102 | defaultValue = v; 103 | currentValue = cur; 104 | } 105 | 106 | Option::operator int() const { 107 | assert(type == "check" || type == "spin"); 108 | return (type == "spin" ? std::stoi(currentValue) : currentValue == "true"); 109 | } 110 | 111 | Option::operator std::string() const { 112 | assert(type == "string"); 113 | return currentValue; 114 | } 115 | 116 | bool Option::operator==(const char* s) const { 117 | assert(type == "combo"); 118 | return !CaseInsensitiveLess()(currentValue, s) && !CaseInsensitiveLess()(s, currentValue); 119 | } 120 | 121 | 122 | // Inits options and assigns idx in the correct printing order 123 | 124 | void Option::operator<<(const Option& o) { 125 | 126 | static size_t insert_order = 0; 127 | 128 | *this = o; 129 | idx = insert_order++; 130 | } 131 | 132 | 133 | // Updates currentValue and triggers on_change() action. It's up to 134 | // the GUI to check for option's limits, but we could receive the new value 135 | // from the user by console window, so let's check the bounds anyway. 136 | Option& Option::operator=(const std::string& v) { 137 | 138 | assert(!type.empty()); 139 | 140 | if ((type != "button" && type != "string" && v.empty()) 141 | || (type == "check" && v != "true" && v != "false") 142 | || (type == "spin" && (std::stof(v) < min || std::stof(v) > max))) 143 | return *this; 144 | 145 | if (type == "combo") 146 | { 147 | OptionsMap comboMap; // To have case insensitive compare 148 | std::string token; 149 | std::istringstream ss(defaultValue); 150 | while (ss >> token) 151 | comboMap[token] << Option(); 152 | if (!comboMap.count(v) || v == "var") 153 | return *this; 154 | } 155 | 156 | if (type != "button") 157 | currentValue = v; 158 | 159 | if (on_change) 160 | on_change(*this); 161 | 162 | return *this; 163 | } 164 | 165 | std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { 166 | for (size_t idx = 0; idx < om.options_map.size(); ++idx) 167 | for (const auto& it : om.options_map) 168 | if (it.second.idx == idx) 169 | { 170 | const Option& o = it.second; 171 | os << "\noption name " << it.first << " type " << o.type; 172 | 173 | if (o.type == "string" || o.type == "check" || o.type == "combo") 174 | os << " default " << o.defaultValue; 175 | 176 | if (o.type == "spin") 177 | os << " default " << int(stof(o.defaultValue)) << " min " << o.min << " max " 178 | << o.max; 179 | 180 | break; 181 | } 182 | 183 | return os; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/engine.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 "engine.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "evaluate.h" 32 | #include "misc.h" 33 | #include "nnue/network.h" 34 | #include "nnue/nnue_common.h" 35 | #include "perft.h" 36 | #include "position.h" 37 | #include "search.h" 38 | #include "syzygy/tbprobe.h" 39 | #include "types.h" 40 | #include "uci.h" 41 | #include "ucioption.h" 42 | 43 | namespace Stockfish { 44 | 45 | namespace NN = Eval::NNUE; 46 | 47 | constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; 48 | 49 | Engine::Engine(std::string path) : 50 | binaryDirectory(CommandLine::get_binary_directory(path)), 51 | states(new std::deque(1)), 52 | networks(NN::Networks( 53 | NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG), 54 | NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) { 55 | pos.set(StartFEN, false, &states->back()); 56 | capSq = SQ_NONE; 57 | } 58 | 59 | std::uint64_t Engine::perft(const std::string& fen, Depth depth, bool isChess960) { 60 | verify_networks(); 61 | 62 | return Benchmark::perft(fen, depth, isChess960); 63 | } 64 | 65 | void Engine::go(Search::LimitsType& limits) { 66 | assert(limits.perft == 0); 67 | verify_networks(); 68 | limits.capSq = capSq; 69 | 70 | threads.start_thinking(options, pos, states, limits); 71 | } 72 | void Engine::stop() { threads.stop = true; } 73 | 74 | void Engine::search_clear() { 75 | wait_for_search_finished(); 76 | 77 | tt.clear(options["Threads"]); 78 | threads.clear(); 79 | 80 | // @TODO wont work with multiple instances 81 | Tablebases::init(options["SyzygyPath"]); // Free mapped files 82 | } 83 | 84 | void Engine::set_on_update_no_moves(std::function&& f) { 85 | updateContext.onUpdateNoMoves = std::move(f); 86 | } 87 | 88 | void Engine::set_on_update_full(std::function&& f) { 89 | updateContext.onUpdateFull = std::move(f); 90 | } 91 | 92 | void Engine::set_on_iter(std::function&& f) { 93 | updateContext.onIter = std::move(f); 94 | } 95 | 96 | void Engine::set_on_bestmove(std::function&& f) { 97 | updateContext.onBestmove = std::move(f); 98 | } 99 | 100 | void Engine::wait_for_search_finished() { threads.main_thread()->wait_for_search_finished(); } 101 | 102 | void Engine::set_position(const std::string& fen, const std::vector& moves) { 103 | // Drop the old state and create a new one 104 | states = StateListPtr(new std::deque(1)); 105 | pos.set(fen, options["UCI_Chess960"], &states->back()); 106 | 107 | capSq = SQ_NONE; 108 | for (const auto& move : moves) 109 | { 110 | auto m = UCIEngine::to_move(pos, move); 111 | 112 | if (m == Move::none()) 113 | break; 114 | 115 | states->emplace_back(); 116 | pos.do_move(m, states->back()); 117 | 118 | capSq = SQ_NONE; 119 | DirtyPiece& dp = states->back().dirtyPiece; 120 | if (dp.dirty_num > 1 && dp.to[1] == SQ_NONE) 121 | capSq = m.to_sq(); 122 | } 123 | } 124 | 125 | // modifiers 126 | 127 | void Engine::resize_threads() { threads.set({options, threads, tt, networks}, updateContext); } 128 | 129 | void Engine::set_tt_size(size_t mb) { 130 | wait_for_search_finished(); 131 | tt.resize(mb, options["Threads"]); 132 | } 133 | 134 | void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; } 135 | 136 | // network related 137 | 138 | void Engine::verify_networks() const { 139 | networks.big.verify(options["EvalFile"]); 140 | networks.small.verify(options["EvalFileSmall"]); 141 | } 142 | 143 | void Engine::load_networks() { 144 | load_big_network(options["EvalFile"]); 145 | load_small_network(options["EvalFileSmall"]); 146 | } 147 | 148 | void Engine::load_big_network(const std::string& file) { 149 | networks.big.load(binaryDirectory, file); 150 | threads.clear(); 151 | } 152 | 153 | void Engine::load_small_network(const std::string& file) { 154 | networks.small.load(binaryDirectory, file); 155 | threads.clear(); 156 | } 157 | 158 | void Engine::save_network(const std::pair, std::string> files[2]) { 159 | networks.big.save(files[0].first); 160 | networks.small.save(files[1].first); 161 | } 162 | 163 | // utility functions 164 | 165 | void Engine::trace_eval() const { 166 | StateListPtr trace_states(new std::deque(1)); 167 | Position p; 168 | p.set(pos.fen(), options["UCI_Chess960"], &trace_states->back()); 169 | 170 | verify_networks(); 171 | 172 | sync_cout << "\n" << Eval::trace(p, networks) << sync_endl; 173 | } 174 | 175 | OptionsMap& Engine::get_options() { return options; } 176 | 177 | std::string Engine::fen() const { return pos.fen(); } 178 | 179 | void Engine::flip() { pos.flip(); } 180 | 181 | std::string Engine::visualize() const { 182 | std::stringstream ss; 183 | ss << pos; 184 | return ss.str(); 185 | } 186 | 187 | } 188 | -------------------------------------------------------------------------------- /src/tt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 "tt.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 | // Populates the TTEntry with a new node's data, possibly 34 | // overwriting an old position. The update is not atomic and can be racy. 35 | void TTEntry::save( 36 | Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev, uint8_t generation8) { 37 | 38 | // Preserve the old ttmove if we don't have a new one 39 | if (m || uint16_t(k) != key16) 40 | move16 = m; 41 | 42 | // Overwrite less valuable entries (cheapest checks first) 43 | if (b == BOUND_EXACT || uint16_t(k) != key16 || d - DEPTH_OFFSET + 2 * pv > depth8 - 4 44 | || relative_age(generation8)) 45 | { 46 | assert(d > DEPTH_OFFSET); 47 | assert(d < 256 + DEPTH_OFFSET); 48 | 49 | key16 = uint16_t(k); 50 | depth8 = uint8_t(d - DEPTH_OFFSET); 51 | genBound8 = uint8_t(generation8 | uint8_t(pv) << 2 | b); 52 | value16 = int16_t(v); 53 | eval16 = int16_t(ev); 54 | } 55 | } 56 | 57 | 58 | uint8_t TTEntry::relative_age(const uint8_t generation8) const { 59 | // Due to our packed storage format for generation and its cyclic 60 | // nature we add GENERATION_CYCLE (256 is the modulus, plus what 61 | // is needed to keep the unrelated lowest n bits from affecting 62 | // the result) to calculate the entry age correctly even after 63 | // generation8 overflows into the next cycle. 64 | 65 | return (TranspositionTable::GENERATION_CYCLE + generation8 - genBound8) 66 | & TranspositionTable::GENERATION_MASK; 67 | } 68 | 69 | 70 | // Sets the size of the transposition table, 71 | // measured in megabytes. Transposition table consists 72 | // of clusters and each cluster consists of ClusterSize number of TTEntry. 73 | void TranspositionTable::resize(size_t mbSize, int threadCount) { 74 | aligned_large_pages_free(table); 75 | 76 | clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); 77 | 78 | table = static_cast(aligned_large_pages_alloc(clusterCount * sizeof(Cluster))); 79 | if (!table) 80 | { 81 | std::cerr << "Failed to allocate " << mbSize << "MB for transposition table." << std::endl; 82 | exit(EXIT_FAILURE); 83 | } 84 | 85 | clear(threadCount); 86 | } 87 | 88 | 89 | // Initializes the entire transposition table to zero, 90 | // in a multi-threaded way. 91 | void TranspositionTable::clear(size_t threadCount) { 92 | std::vector threads; 93 | 94 | for (size_t idx = 0; idx < size_t(threadCount); ++idx) 95 | { 96 | threads.emplace_back([this, idx, threadCount]() { 97 | // Thread binding gives faster search on systems with a first-touch policy 98 | if (threadCount > 8) 99 | WinProcGroup::bind_this_thread(idx); 100 | 101 | // Each thread will zero its part of the hash table 102 | const size_t stride = size_t(clusterCount / threadCount), start = size_t(stride * idx), 103 | len = idx != size_t(threadCount) - 1 ? stride : clusterCount - start; 104 | 105 | std::memset(&table[start], 0, len * sizeof(Cluster)); 106 | }); 107 | } 108 | 109 | for (std::thread& th : threads) 110 | th.join(); 111 | } 112 | 113 | 114 | // Looks up the current position in the transposition 115 | // table. It returns true and a pointer to the TTEntry if the position is found. 116 | // Otherwise, it returns false and a pointer to an empty or least valuable TTEntry 117 | // to be replaced later. The replace value of an entry is calculated as its depth 118 | // minus 8 times its relative age. TTEntry t1 is considered more valuable than 119 | // TTEntry t2 if its replace value is greater than that of t2. 120 | TTEntry* TranspositionTable::probe(const Key key, bool& found) const { 121 | 122 | TTEntry* const tte = first_entry(key); 123 | const uint16_t key16 = uint16_t(key); // Use the low 16 bits as key inside the cluster 124 | 125 | for (int i = 0; i < ClusterSize; ++i) 126 | if (tte[i].key16 == key16 || !tte[i].depth8) 127 | return found = bool(tte[i].depth8), &tte[i]; 128 | 129 | // Find an entry to be replaced according to the replacement strategy 130 | TTEntry* replace = tte; 131 | for (int i = 1; i < ClusterSize; ++i) 132 | if (replace->depth8 - replace->relative_age(generation8) * 2 133 | > tte[i].depth8 - tte[i].relative_age(generation8) * 2) 134 | replace = &tte[i]; 135 | 136 | return found = false, replace; 137 | } 138 | 139 | 140 | // Returns an approximation of the hashtable 141 | // occupation during a search. The hash is x permill full, as per UCI protocol. 142 | // Only counts entries which match the current generation. 143 | int TranspositionTable::hashfull() const { 144 | 145 | int cnt = 0; 146 | for (int i = 0; i < 1000; ++i) 147 | for (int j = 0; j < ClusterSize; ++j) 148 | cnt += table[i].entry[j].depth8 149 | && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8; 150 | 151 | return cnt / ClusterSize; 152 | } 153 | 154 | } // namespace Stockfish 155 | -------------------------------------------------------------------------------- /src/nnue/layers/simd.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | /* 47 | Parameters: 48 | sum0 = [zmm0.i128[0], zmm0.i128[1], zmm0.i128[2], zmm0.i128[3]] 49 | sum1 = [zmm1.i128[0], zmm1.i128[1], zmm1.i128[2], zmm1.i128[3]] 50 | sum2 = [zmm2.i128[0], zmm2.i128[1], zmm2.i128[2], zmm2.i128[3]] 51 | sum3 = [zmm3.i128[0], zmm3.i128[1], zmm3.i128[2], zmm3.i128[3]] 52 | 53 | Returns: 54 | ret = [ 55 | reduce_add_epi32(zmm0.i128[0]), reduce_add_epi32(zmm1.i128[0]), reduce_add_epi32(zmm2.i128[0]), reduce_add_epi32(zmm3.i128[0]), 56 | reduce_add_epi32(zmm0.i128[1]), reduce_add_epi32(zmm1.i128[1]), reduce_add_epi32(zmm2.i128[1]), reduce_add_epi32(zmm3.i128[1]), 57 | reduce_add_epi32(zmm0.i128[2]), reduce_add_epi32(zmm1.i128[2]), reduce_add_epi32(zmm2.i128[2]), reduce_add_epi32(zmm3.i128[2]), 58 | reduce_add_epi32(zmm0.i128[3]), reduce_add_epi32(zmm1.i128[3]), reduce_add_epi32(zmm2.i128[3]), reduce_add_epi32(zmm3.i128[3]) 59 | ] 60 | */ 61 | [[maybe_unused]] static __m512i 62 | m512_hadd128x16_interleave(__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) { 63 | 64 | __m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1); 65 | __m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1); 66 | 67 | __m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3); 68 | __m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3); 69 | 70 | __m512i sum01 = _mm512_add_epi32(sum01a, sum01b); 71 | __m512i sum23 = _mm512_add_epi32(sum23a, sum23b); 72 | 73 | __m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23); 74 | __m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23); 75 | 76 | return _mm512_add_epi32(sum0123a, sum0123b); 77 | } 78 | 79 | [[maybe_unused]] static void m512_add_dpbusd_epi32(__m512i& acc, __m512i a, __m512i b) { 80 | 81 | #if defined(USE_VNNI) 82 | acc = _mm512_dpbusd_epi32(acc, a, b); 83 | #else 84 | __m512i product0 = _mm512_maddubs_epi16(a, b); 85 | product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); 86 | acc = _mm512_add_epi32(acc, product0); 87 | #endif 88 | } 89 | 90 | #endif 91 | 92 | #if defined(USE_AVX2) 93 | 94 | [[maybe_unused]] static int m256_hadd(__m256i sum, int bias) { 95 | __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); 96 | sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC)); 97 | sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB)); 98 | return _mm_cvtsi128_si32(sum128) + bias; 99 | } 100 | 101 | [[maybe_unused]] static void m256_add_dpbusd_epi32(__m256i& acc, __m256i a, __m256i b) { 102 | 103 | #if defined(USE_VNNI) 104 | acc = _mm256_dpbusd_epi32(acc, a, b); 105 | #else 106 | __m256i product0 = _mm256_maddubs_epi16(a, b); 107 | product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); 108 | acc = _mm256_add_epi32(acc, product0); 109 | #endif 110 | } 111 | 112 | #endif 113 | 114 | #if defined(USE_SSSE3) 115 | 116 | [[maybe_unused]] static int m128_hadd(__m128i sum, int bias) { 117 | sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC 118 | sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB 119 | return _mm_cvtsi128_si32(sum) + bias; 120 | } 121 | 122 | [[maybe_unused]] static void m128_add_dpbusd_epi32(__m128i& acc, __m128i a, __m128i b) { 123 | 124 | __m128i product0 = _mm_maddubs_epi16(a, b); 125 | product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); 126 | acc = _mm_add_epi32(acc, product0); 127 | } 128 | 129 | #endif 130 | 131 | #if defined(USE_NEON_DOTPROD) 132 | 133 | [[maybe_unused]] static void 134 | dotprod_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) { 135 | 136 | acc = vdotq_s32(acc, a, b); 137 | } 138 | #endif 139 | 140 | #if defined(USE_NEON) 141 | 142 | [[maybe_unused]] static int neon_m128_reduce_add_epi32(int32x4_t s) { 143 | #if USE_NEON >= 8 144 | return vaddvq_s32(s); 145 | #else 146 | return s[0] + s[1] + s[2] + s[3]; 147 | #endif 148 | } 149 | 150 | [[maybe_unused]] static int neon_m128_hadd(int32x4_t sum, int bias) { 151 | return neon_m128_reduce_add_epi32(sum) + bias; 152 | } 153 | 154 | #endif 155 | 156 | #if USE_NEON >= 8 157 | [[maybe_unused]] static void neon_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) { 158 | 159 | int16x8_t product0 = vmull_s8(vget_low_s8(a), vget_low_s8(b)); 160 | int16x8_t product1 = vmull_high_s8(a, b); 161 | int16x8_t sum = vpaddq_s16(product0, product1); 162 | acc = vpadalq_s16(acc, sum); 163 | } 164 | #endif 165 | } 166 | 167 | #endif // STOCKFISH_SIMD_H_INCLUDED 168 | -------------------------------------------------------------------------------- /src/nnue/nnue_architecture.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | //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 | struct StateInfo; 32 | class Position; 33 | } 34 | 35 | namespace Stockfish::Eval::NNUE::Features { 36 | 37 | // Feature HalfKAv2_hm: Combination of the position of own king and the 38 | // position of pieces. Position mirrored such that king is always on e..h files. 39 | class HalfKAv2_hm { 40 | 41 | // Unique number for each piece type on each square 42 | enum { 43 | PS_NONE = 0, 44 | PS_W_PAWN = 0, 45 | PS_B_PAWN = 1 * SQUARE_NB, 46 | PS_W_KNIGHT = 2 * SQUARE_NB, 47 | PS_B_KNIGHT = 3 * SQUARE_NB, 48 | PS_W_BISHOP = 4 * SQUARE_NB, 49 | PS_B_BISHOP = 5 * SQUARE_NB, 50 | PS_W_ROOK = 6 * SQUARE_NB, 51 | PS_B_ROOK = 7 * SQUARE_NB, 52 | PS_W_QUEEN = 8 * SQUARE_NB, 53 | PS_B_QUEEN = 9 * SQUARE_NB, 54 | PS_KING = 10 * SQUARE_NB, 55 | PS_NB = 11 * SQUARE_NB 56 | }; 57 | 58 | static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = { 59 | // Convention: W - us, B - them 60 | // Viewed from other side, W and B are reversed 61 | {PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_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_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE, 64 | PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE}}; 65 | 66 | public: 67 | // Feature name 68 | static constexpr const char* Name = "HalfKAv2_hm(Friend)"; 69 | 70 | // Hash value embedded in the evaluation file 71 | static constexpr std::uint32_t HashValue = 0x7f234cb8u; 72 | 73 | // Number of feature dimensions 74 | static constexpr IndexType Dimensions = 75 | static_cast(SQUARE_NB) * static_cast(PS_NB) / 2; 76 | 77 | #define B(v) (v * PS_NB) 78 | // clang-format off 79 | static constexpr int KingBuckets[COLOR_NB][SQUARE_NB] = { 80 | { B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28), 81 | B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24), 82 | B(20), B(21), B(22), B(23), B(23), B(22), B(21), B(20), 83 | B(16), B(17), B(18), B(19), B(19), B(18), B(17), B(16), 84 | B(12), B(13), B(14), B(15), B(15), B(14), B(13), B(12), 85 | B( 8), B( 9), B(10), B(11), B(11), B(10), B( 9), B( 8), 86 | B( 4), B( 5), B( 6), B( 7), B( 7), B( 6), B( 5), B( 4), 87 | B( 0), B( 1), B( 2), B( 3), B( 3), B( 2), B( 1), B( 0) }, 88 | { B( 0), B( 1), B( 2), B( 3), B( 3), B( 2), B( 1), B( 0), 89 | B( 4), B( 5), B( 6), B( 7), B( 7), B( 6), B( 5), B( 4), 90 | B( 8), B( 9), B(10), B(11), B(11), B(10), B( 9), B( 8), 91 | B(12), B(13), B(14), B(15), B(15), B(14), B(13), B(12), 92 | B(16), B(17), B(18), B(19), B(19), B(18), B(17), B(16), 93 | B(20), B(21), B(22), B(23), B(23), B(22), B(21), B(20), 94 | B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24), 95 | B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28) } 96 | }; 97 | // clang-format on 98 | #undef B 99 | // clang-format off 100 | // Orient a square according to perspective (rotates by 180 for black) 101 | static constexpr int OrientTBL[COLOR_NB][SQUARE_NB] = { 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_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1 }, 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 | SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8 } 118 | }; 119 | // clang-format on 120 | 121 | // Maximum number of simultaneously active features. 122 | static constexpr IndexType MaxActiveDimensions = 32; 123 | using IndexList = ValueList; 124 | 125 | // Index of a feature for a given king position and another piece on some square 126 | template 127 | static IndexType make_index(Square s, Piece pc, Square ksq); 128 | 129 | // Get a list of indices for active features 130 | template 131 | static void append_active_indices(const Position& pos, IndexList& active); 132 | 133 | // Get a list of indices for recently changed features 134 | template 135 | static void 136 | append_changed_indices(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added); 137 | 138 | // Returns the cost of updating one perspective, the most costly one. 139 | // Assumes no refresh needed. 140 | static int update_cost(const StateInfo* st); 141 | static int refresh_cost(const Position& pos); 142 | 143 | // Returns whether the change stored in this StateInfo means 144 | // that a full accumulator refresh is required. 145 | static bool requires_refresh(const StateInfo* st, Color perspective); 146 | }; 147 | 148 | } // namespace Stockfish::Eval::NNUE::Features 149 | 150 | #endif // #ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED 151 | -------------------------------------------------------------------------------- /src/tune.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | std::vector> list; 149 | 150 | public: 151 | template 152 | static int add(const std::string& names, Args&&... args) { 153 | return instance().add(SetDefaultRange, names.substr(1, names.size() - 2), 154 | args...); // Remove trailing parenthesis 155 | } 156 | static void init(OptionsMap& o) { 157 | options = &o; 158 | for (auto& e : instance().list) 159 | e->init_option(); 160 | read_options(); 161 | } // Deferred, due to UCIEngine::Options access 162 | static void read_options() { 163 | for (auto& e : instance().list) 164 | e->read_option(); 165 | } 166 | 167 | static bool update_on_last; 168 | static OptionsMap* options; 169 | }; 170 | 171 | // Some macro magic :-) we define a dummy int variable that the compiler initializes calling Tune::add() 172 | #define STRINGIFY(x) #x 173 | #define UNIQUE2(x, y) x##y 174 | #define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__ 175 | #define TUNE(...) int UNIQUE(p, __LINE__) = Tune::add(STRINGIFY((__VA_ARGS__)), __VA_ARGS__) 176 | 177 | #define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true 178 | 179 | } // namespace Stockfish 180 | 181 | #endif // #ifndef TUNE_H_INCLUDED 182 | -------------------------------------------------------------------------------- /src/benchmark.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 "benchmark.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | namespace { 27 | 28 | // clang-format off 29 | const std::vector Defaults = { 30 | "setoption name UCI_Chess960 value false", 31 | "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 32 | "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10", 33 | "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11", 34 | "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19", 35 | "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14 moves d4e6", 36 | "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14 moves g2g4", 37 | "r3r1k1/2p2ppp/p1p1bn2/8/1q2P3/2NPQN2/PPP3PP/R4RK1 b - - 2 15", 38 | "r1bbk1nr/pp3p1p/2n5/1N4p1/2Np1B2/8/PPP2PPP/2KR1B1R w kq - 0 13", 39 | "r1bq1rk1/ppp1nppp/4n3/3p3Q/3P4/1BP1B3/PP1N2PP/R4RK1 w - - 1 16", 40 | "4r1k1/r1q2ppp/ppp2n2/4P3/5Rb1/1N1BQ3/PPP3PP/R5K1 w - - 1 17", 41 | "2rqkb1r/ppp2p2/2npb1p1/1N1Nn2p/2P1PP2/8/PP2B1PP/R1BQK2R b KQ - 0 11", 42 | "r1bq1r1k/b1p1npp1/p2p3p/1p6/3PP3/1B2NN2/PP3PPP/R2Q1RK1 w - - 1 16", 43 | "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22", 44 | "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18", 45 | "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22", 46 | "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26", 47 | "6k1/6p1/6Pp/ppp5/3pn2P/1P3K2/1PP2P2/3N4 b - - 0 1", 48 | "3b4/5kp1/1p1p1p1p/pP1PpP1P/P1P1P3/3KN3/8/8 w - - 0 1", 49 | "2K5/p7/7P/5pR1/8/5k2/r7/8 w - - 0 1 moves g5g6 f3e3 g6g5 e3f3", 50 | "8/6pk/1p6/8/PP3p1p/5P2/4KP1q/3Q4 w - - 0 1", 51 | "7k/3p2pp/4q3/8/4Q3/5Kp1/P6b/8 w - - 0 1", 52 | "8/2p5/8/2kPKp1p/2p4P/2P5/3P4/8 w - - 0 1", 53 | "8/1p3pp1/7p/5P1P/2k3P1/8/2K2P2/8 w - - 0 1", 54 | "8/pp2r1k1/2p1p3/3pP2p/1P1P1P1P/P5KR/8/8 w - - 0 1", 55 | "8/3p4/p1bk3p/Pp6/1Kp1PpPp/2P2P1P/2P5/5B2 b - - 0 1", 56 | "5k2/7R/4P2p/5K2/p1r2P1p/8/8/8 b - - 0 1", 57 | "6k1/6p1/P6p/r1N5/5p2/7P/1b3PP1/4R1K1 w - - 0 1", 58 | "1r3k2/4q3/2Pp3b/3Bp3/2Q2p2/1p1P2P1/1P2KP2/3N4 w - - 0 1", 59 | "6k1/4pp1p/3p2p1/P1pPb3/R7/1r2P1PP/3B1P2/6K1 w - - 0 1", 60 | "8/3p3B/5p2/5P2/p7/PP5b/k7/6K1 w - - 0 1", 61 | "5rk1/q6p/2p3bR/1pPp1rP1/1P1Pp3/P3B1Q1/1K3P2/R7 w - - 93 90", 62 | "4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21", 63 | "r3k2r/3nnpbp/q2pp1p1/p7/Pp1PPPP1/4BNN1/1P5P/R2Q1RK1 w kq - 0 16", 64 | "3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40", 65 | "4k3/3q1r2/1N2r1b1/3ppN2/2nPP3/1B1R2n1/2R1Q3/3K4 w - - 5 1", 66 | 67 | // 5-man positions 68 | "8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate 69 | "8/8/8/5N2/8/p7/8/2NK3k w - - 0 1", // Na2 - mate 70 | "8/3k4/8/8/8/4B3/4KB2/2B5 w - - 0 1", // draw 71 | 72 | // 6-man positions 73 | "8/8/1P6/5pr1/8/4R3/7k/2K5 w - - 0 1", // Re5 - mate 74 | "8/2p4P/8/kr6/6R1/8/8/1K6 w - - 0 1", // Ka2 - mate 75 | "8/8/3P3k/8/1p6/8/1P6/1K3n2 b - - 0 1", // Nd2 - draw 76 | 77 | // 7-man positions 78 | "8/R7/2q5/8/6k1/8/1P5p/K6R w - - 0 124", // Draw 79 | 80 | // Mate and stalemate positions 81 | "6k1/3b3r/1p1p4/p1n2p2/1PPNpP1q/P3Q1p1/1R1RB1P1/5K2 b - - 0 1", 82 | "r2r1n2/pp2bk2/2p1p2p/3q4/3PN1QP/2P3R1/P4PP1/5RK1 w - - 0 1", 83 | "8/8/8/8/8/6k1/6p1/6K1 w - -", 84 | "7k/7P/6K1/8/3B4/8/8/8 b - -", 85 | 86 | // Chess 960 87 | "setoption name UCI_Chess960 value true", 88 | "bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w HFhf - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6", 89 | "nqbnrkrb/pppppppp/8/8/8/8/PPPPPPPP/NQBNRKRB w KQkq - 0 1", 90 | "setoption name UCI_Chess960 value false" 91 | }; 92 | // clang-format on 93 | 94 | } // namespace 95 | 96 | namespace Stockfish::Benchmark { 97 | 98 | // Builds a list of UCI commands to be run by bench. There 99 | // are five parameters: TT size in MB, number of search threads that 100 | // should be used, the limit value spent for each position, a file name 101 | // where to look for positions in FEN format, and the type of the limit: 102 | // depth, perft, nodes and movetime (in milliseconds). Examples: 103 | // 104 | // bench : search default positions up to depth 13 105 | // bench 64 1 15 : search default positions up to depth 15 (TT = 64MB) 106 | // bench 64 1 100000 default nodes : search default positions for 100K nodes each 107 | // bench 64 4 5000 current movetime : search current position with 4 threads for 5 sec 108 | // bench 16 1 5 blah perft : run a perft 5 on positions in file "blah" 109 | std::vector setup_bench(const std::string& currentFen, std::istream& is) { 110 | 111 | std::vector fens, list; 112 | std::string go, token; 113 | 114 | // Assign default values to missing arguments 115 | std::string ttSize = (is >> token) ? token : "16"; 116 | std::string threads = (is >> token) ? token : "1"; 117 | std::string limit = (is >> token) ? token : "13"; 118 | std::string fenFile = (is >> token) ? token : "default"; 119 | std::string limitType = (is >> token) ? token : "depth"; 120 | 121 | go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit; 122 | 123 | if (fenFile == "default") 124 | fens = Defaults; 125 | 126 | else if (fenFile == "current") 127 | fens.push_back(currentFen); 128 | 129 | else 130 | { 131 | std::string fen; 132 | std::ifstream file(fenFile); 133 | 134 | if (!file.is_open()) 135 | { 136 | std::cerr << "Unable to open file " << fenFile << std::endl; 137 | exit(EXIT_FAILURE); 138 | } 139 | 140 | while (getline(file, fen)) 141 | if (!fen.empty()) 142 | fens.push_back(fen); 143 | 144 | file.close(); 145 | } 146 | 147 | list.emplace_back("setoption name Threads value " + threads); 148 | list.emplace_back("setoption name Hash value " + ttSize); 149 | list.emplace_back("ucinewgame"); 150 | 151 | for (const std::string& fen : fens) 152 | if (fen.find("setoption") != std::string::npos) 153 | list.emplace_back(fen); 154 | else 155 | { 156 | list.emplace_back("position fen " + fen); 157 | list.emplace_back(go); 158 | } 159 | 160 | return list; 161 | } 162 | 163 | } // namespace Stockfish -------------------------------------------------------------------------------- /src/nnue/nnue_misc.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 | // 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 | 32 | #include "../evaluate.h" 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 | void hint_common_parent_position(const Position& pos, 46 | const Networks& networks, 47 | AccumulatorCaches& caches) { 48 | if (Eval::use_smallnet(pos)) 49 | networks.small.hint_common_access(pos, &caches.small); 50 | else 51 | networks.big.hint_common_access(pos, &caches.big); 52 | } 53 | 54 | namespace { 55 | // Converts a Value into (centi)pawns and writes it in a buffer. 56 | // The buffer must have capacity for at least 5 chars. 57 | void format_cp_compact(Value v, char* buffer, const Position& pos) { 58 | 59 | buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); 60 | 61 | int cp = std::abs(UCIEngine::to_cp(v, pos)); 62 | if (cp >= 10000) 63 | { 64 | buffer[1] = '0' + cp / 10000; 65 | cp %= 10000; 66 | buffer[2] = '0' + cp / 1000; 67 | cp %= 1000; 68 | buffer[3] = '0' + cp / 100; 69 | buffer[4] = ' '; 70 | } 71 | else if (cp >= 1000) 72 | { 73 | buffer[1] = '0' + cp / 1000; 74 | cp %= 1000; 75 | buffer[2] = '0' + cp / 100; 76 | cp %= 100; 77 | buffer[3] = '.'; 78 | buffer[4] = '0' + cp / 10; 79 | } 80 | else 81 | { 82 | buffer[1] = '0' + cp / 100; 83 | cp %= 100; 84 | buffer[2] = '.'; 85 | buffer[3] = '0' + cp / 10; 86 | cp %= 10; 87 | buffer[4] = '0' + cp / 1; 88 | } 89 | } 90 | 91 | 92 | // Converts a Value into pawns, always keeping two decimals 93 | void format_cp_aligned_dot(Value v, std::stringstream& stream, const Position& pos) { 94 | 95 | const double pawns = std::abs(0.01 * UCIEngine::to_cp(v, pos)); 96 | 97 | stream << (v < 0 ? '-' 98 | : v > 0 ? '+' 99 | : ' ') 100 | << std::setiosflags(std::ios::fixed) << std::setw(6) << std::setprecision(2) << pawns; 101 | } 102 | } 103 | 104 | 105 | // Returns a string with the value of each piece on a board, 106 | // and a table for (PSQT, Layers) values bucket by bucket. 107 | std::string 108 | trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::AccumulatorCaches& caches) { 109 | 110 | std::stringstream ss; 111 | 112 | char board[3 * 8 + 1][8 * 8 + 2]; 113 | std::memset(board, ' ', sizeof(board)); 114 | for (int row = 0; row < 3 * 8 + 1; ++row) 115 | board[row][8 * 8 + 1] = '\0'; 116 | 117 | // A lambda to output one box of the board 118 | auto writeSquare = [&board, &pos](File file, Rank rank, Piece pc, Value value) { 119 | const int x = int(file) * 8; 120 | const int y = (7 - int(rank)) * 3; 121 | for (int i = 1; i < 8; ++i) 122 | board[y][x + i] = board[y + 3][x + i] = '-'; 123 | for (int i = 1; i < 3; ++i) 124 | board[y + i][x] = board[y + i][x + 8] = '|'; 125 | board[y][x] = board[y][x + 8] = board[y + 3][x + 8] = board[y + 3][x] = '+'; 126 | if (pc != NO_PIECE) 127 | board[y + 1][x + 4] = PieceToChar[pc]; 128 | if (value != VALUE_NONE) 129 | format_cp_compact(value, &board[y + 2][x + 2], pos); 130 | }; 131 | 132 | // We estimate the value of each piece by doing a differential evaluation from 133 | // the current base eval, simulating the removal of the piece from its square. 134 | Value base = networks.big.evaluate(pos, &caches.big); 135 | base = pos.side_to_move() == WHITE ? base : -base; 136 | 137 | for (File f = FILE_A; f <= FILE_H; ++f) 138 | for (Rank r = RANK_1; r <= RANK_8; ++r) 139 | { 140 | Square sq = make_square(f, r); 141 | Piece pc = pos.piece_on(sq); 142 | Value v = VALUE_NONE; 143 | 144 | if (pc != NO_PIECE && type_of(pc) != KING) 145 | { 146 | auto st = pos.state(); 147 | 148 | pos.remove_piece(sq); 149 | st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = false; 150 | 151 | Value eval = networks.big.evaluate(pos, &caches.big); 152 | eval = pos.side_to_move() == WHITE ? eval : -eval; 153 | v = base - eval; 154 | 155 | pos.put_piece(pc, sq); 156 | st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = false; 157 | } 158 | 159 | writeSquare(f, r, pc, v); 160 | } 161 | 162 | ss << " NNUE derived piece values:\n"; 163 | for (int row = 0; row < 3 * 8 + 1; ++row) 164 | ss << board[row] << '\n'; 165 | ss << '\n'; 166 | 167 | auto t = networks.big.trace_evaluate(pos, &caches.big); 168 | 169 | ss << " NNUE network contributions " 170 | << (pos.side_to_move() == WHITE ? "(White to move)" : "(Black to move)") << std::endl 171 | << "+------------+------------+------------+------------+\n" 172 | << "| Bucket | Material | Positional | Total |\n" 173 | << "| | (PSQT) | (Layers) | |\n" 174 | << "+------------+------------+------------+------------+\n"; 175 | 176 | for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket) 177 | { 178 | ss << "| " << bucket << " "; 179 | ss << " | "; 180 | format_cp_aligned_dot(t.psqt[bucket], ss, pos); 181 | ss << " " 182 | << " | "; 183 | format_cp_aligned_dot(t.positional[bucket], ss, pos); 184 | ss << " " 185 | << " | "; 186 | format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss, pos); 187 | ss << " " 188 | << " |"; 189 | if (bucket == t.correctBucket) 190 | ss << " <-- this bucket is used"; 191 | ss << '\n'; 192 | } 193 | 194 | ss << "+------------+------------+------------+------------+\n"; 195 | 196 | return ss.str(); 197 | } 198 | 199 | 200 | } // namespace Stockfish::Eval::NNUE 201 | -------------------------------------------------------------------------------- /src/nnue/layers/clipped_relu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 Zero = _mm256_setzero_si256(); 69 | const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); 70 | const auto in = reinterpret_cast(input); 71 | const auto out = reinterpret_cast<__m256i*>(output); 72 | for (IndexType i = 0; i < NumChunks; ++i) 73 | { 74 | const __m256i words0 = 75 | _mm256_srai_epi16(_mm256_packs_epi32(_mm256_load_si256(&in[i * 4 + 0]), 76 | _mm256_load_si256(&in[i * 4 + 1])), 77 | WeightScaleBits); 78 | const __m256i words1 = 79 | _mm256_srai_epi16(_mm256_packs_epi32(_mm256_load_si256(&in[i * 4 + 2]), 80 | _mm256_load_si256(&in[i * 4 + 3])), 81 | WeightScaleBits); 82 | _mm256_store_si256( 83 | &out[i], _mm256_permutevar8x32_epi32( 84 | _mm256_max_epi8(_mm256_packs_epi16(words0, words1), Zero), Offsets)); 85 | } 86 | } 87 | else 88 | { 89 | constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); 90 | const __m128i Zero = _mm_setzero_si128(); 91 | const auto in = reinterpret_cast(input); 92 | const auto out = reinterpret_cast<__m128i*>(output); 93 | for (IndexType i = 0; i < NumChunks; ++i) 94 | { 95 | const __m128i words0 = _mm_srai_epi16( 96 | _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1])), 97 | WeightScaleBits); 98 | const __m128i words1 = _mm_srai_epi16( 99 | _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])), 100 | WeightScaleBits); 101 | const __m128i packedbytes = _mm_packs_epi16(words0, words1); 102 | _mm_store_si128(&out[i], _mm_max_epi8(packedbytes, Zero)); 103 | } 104 | } 105 | constexpr IndexType Start = InputDimensions % SimdWidth == 0 106 | ? InputDimensions / SimdWidth * SimdWidth 107 | : InputDimensions / (SimdWidth / 2) * (SimdWidth / 2); 108 | 109 | #elif defined(USE_SSE2) 110 | constexpr IndexType NumChunks = InputDimensions / SimdWidth; 111 | 112 | #ifdef USE_SSE41 113 | const __m128i Zero = _mm_setzero_si128(); 114 | #else 115 | const __m128i k0x80s = _mm_set1_epi8(-128); 116 | #endif 117 | 118 | const auto in = reinterpret_cast(input); 119 | const auto out = reinterpret_cast<__m128i*>(output); 120 | for (IndexType i = 0; i < NumChunks; ++i) 121 | { 122 | const __m128i words0 = _mm_srai_epi16( 123 | _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1])), 124 | WeightScaleBits); 125 | const __m128i words1 = _mm_srai_epi16( 126 | _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])), 127 | WeightScaleBits); 128 | const __m128i packedbytes = _mm_packs_epi16(words0, words1); 129 | _mm_store_si128(&out[i], 130 | 131 | #ifdef USE_SSE41 132 | _mm_max_epi8(packedbytes, Zero) 133 | #else 134 | _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) 135 | #endif 136 | 137 | ); 138 | } 139 | constexpr IndexType Start = NumChunks * SimdWidth; 140 | 141 | #elif defined(USE_NEON) 142 | constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); 143 | const int8x8_t Zero = {0}; 144 | const auto in = reinterpret_cast(input); 145 | const auto out = reinterpret_cast(output); 146 | for (IndexType i = 0; i < NumChunks; ++i) 147 | { 148 | int16x8_t shifted; 149 | const auto pack = reinterpret_cast(&shifted); 150 | pack[0] = vqshrn_n_s32(in[i * 2 + 0], WeightScaleBits); 151 | pack[1] = vqshrn_n_s32(in[i * 2 + 1], WeightScaleBits); 152 | out[i] = vmax_s8(vqmovn_s16(shifted), Zero); 153 | } 154 | constexpr IndexType Start = NumChunks * (SimdWidth / 2); 155 | #else 156 | constexpr IndexType Start = 0; 157 | #endif 158 | 159 | for (IndexType i = Start; i < InputDimensions; ++i) 160 | { 161 | output[i] = static_cast(std::clamp(input[i] >> WeightScaleBits, 0, 127)); 162 | } 163 | } 164 | }; 165 | 166 | } // namespace Stockfish::Eval::NNUE::Layers 167 | 168 | #endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED 169 | -------------------------------------------------------------------------------- /src/misc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef MISC_H_INCLUDED 20 | #define MISC_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define stringify2(x) #x 33 | #define stringify(x) stringify2(x) 34 | 35 | namespace Stockfish { 36 | 37 | std::string engine_info(bool to_uci = false); 38 | std::string compiler_info(); 39 | 40 | // Preloads the given address in L1/L2 cache. This is a non-blocking 41 | // function that doesn't stall the CPU waiting for data to be loaded from memory, 42 | // which can be quite slow. 43 | void prefetch(void* addr); 44 | 45 | void start_logger(const std::string& fname); 46 | void* std_aligned_alloc(size_t alignment, size_t size); 47 | void std_aligned_free(void* ptr); 48 | // memory aligned by page size, min alignment: 4096 bytes 49 | void* aligned_large_pages_alloc(size_t size); 50 | // nop if mem == nullptr 51 | void aligned_large_pages_free(void* mem); 52 | 53 | // Deleter for automating release of memory area 54 | template 55 | struct AlignedDeleter { 56 | void operator()(T* ptr) const { 57 | ptr->~T(); 58 | std_aligned_free(ptr); 59 | } 60 | }; 61 | 62 | template 63 | struct LargePageDeleter { 64 | void operator()(T* ptr) const { 65 | ptr->~T(); 66 | aligned_large_pages_free(ptr); 67 | } 68 | }; 69 | 70 | template 71 | using AlignedPtr = std::unique_ptr>; 72 | 73 | template 74 | using LargePagePtr = std::unique_ptr>; 75 | 76 | 77 | void dbg_hit_on(bool cond, int slot = 0); 78 | void dbg_mean_of(int64_t value, int slot = 0); 79 | void dbg_stdev_of(int64_t value, int slot = 0); 80 | void dbg_correl_of(int64_t value1, int64_t value2, int slot = 0); 81 | void dbg_print(); 82 | 83 | using TimePoint = std::chrono::milliseconds::rep; // A value in milliseconds 84 | static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits"); 85 | inline TimePoint now() { 86 | return std::chrono::duration_cast( 87 | std::chrono::steady_clock::now().time_since_epoch()) 88 | .count(); 89 | } 90 | 91 | 92 | enum SyncCout { 93 | IO_LOCK, 94 | IO_UNLOCK 95 | }; 96 | std::ostream& operator<<(std::ostream&, SyncCout); 97 | 98 | #define sync_cout std::cout << IO_LOCK 99 | #define sync_endl std::endl << IO_UNLOCK 100 | 101 | 102 | // Get the first aligned element of an array. 103 | // ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes, 104 | // where N is the number of elements in the array. 105 | template 106 | T* align_ptr_up(T* ptr) { 107 | static_assert(alignof(T) < Alignment); 108 | 109 | const uintptr_t ptrint = reinterpret_cast(reinterpret_cast(ptr)); 110 | return reinterpret_cast( 111 | reinterpret_cast((ptrint + (Alignment - 1)) / Alignment * Alignment)); 112 | } 113 | 114 | 115 | // True if and only if the binary is compiled on a little-endian machine 116 | static inline const union { 117 | uint32_t i; 118 | char c[4]; 119 | } Le = {0x01020304}; 120 | static inline const bool IsLittleEndian = (Le.c[0] == 4); 121 | 122 | 123 | template 124 | class ValueList { 125 | 126 | public: 127 | std::size_t size() const { return size_; } 128 | void push_back(const T& value) { values_[size_++] = value; } 129 | const T* begin() const { return values_; } 130 | const T* end() const { return values_ + size_; } 131 | const T& operator[](int index) const { return values_[index]; } 132 | 133 | private: 134 | T values_[MaxSize]; 135 | std::size_t size_ = 0; 136 | }; 137 | 138 | 139 | // xorshift64star Pseudo-Random Number Generator 140 | // This class is based on original code written and dedicated 141 | // to the public domain by Sebastiano Vigna (2014). 142 | // It has the following characteristics: 143 | // 144 | // - Outputs 64-bit numbers 145 | // - Passes Dieharder and SmallCrush test batteries 146 | // - Does not require warm-up, no zeroland to escape 147 | // - Internal state is a single 64-bit integer 148 | // - Period is 2^64 - 1 149 | // - Speed: 1.60 ns/call (Core i7 @3.40GHz) 150 | // 151 | // For further analysis see 152 | // 153 | 154 | class PRNG { 155 | 156 | uint64_t s; 157 | 158 | uint64_t rand64() { 159 | 160 | s ^= s >> 12, s ^= s << 25, s ^= s >> 27; 161 | return s * 2685821657736338717LL; 162 | } 163 | 164 | public: 165 | PRNG(uint64_t seed) : 166 | s(seed) { 167 | assert(seed); 168 | } 169 | 170 | template 171 | T rand() { 172 | return T(rand64()); 173 | } 174 | 175 | // Special generator used to fast init magic numbers. 176 | // Output values only have 1/8th of their bits set on average. 177 | template 178 | T sparse_rand() { 179 | return T(rand64() & rand64() & rand64()); 180 | } 181 | }; 182 | 183 | inline uint64_t mul_hi64(uint64_t a, uint64_t b) { 184 | #if defined(__GNUC__) && defined(IS_64BIT) 185 | __extension__ using uint128 = unsigned __int128; 186 | return (uint128(a) * uint128(b)) >> 64; 187 | #else 188 | uint64_t aL = uint32_t(a), aH = a >> 32; 189 | uint64_t bL = uint32_t(b), bH = b >> 32; 190 | uint64_t c1 = (aL * bL) >> 32; 191 | uint64_t c2 = aH * bL + c1; 192 | uint64_t c3 = aL * bH + uint32_t(c2); 193 | return aH * bH + (c2 >> 32) + (c3 >> 32); 194 | #endif 195 | } 196 | 197 | // Under Windows it is not possible for a process to run on more than one 198 | // logical processor group. This usually means being limited to using max 64 199 | // cores. To overcome this, some special platform-specific API should be 200 | // called to set group affinity for each thread. Original code from Texel by 201 | // Peter Österlund. 202 | namespace WinProcGroup { 203 | void bind_this_thread(size_t idx); 204 | } 205 | 206 | 207 | struct CommandLine { 208 | public: 209 | CommandLine(int _argc, char** _argv) : 210 | argc(_argc), 211 | argv(_argv) {} 212 | 213 | static std::string get_binary_directory(std::string argv0); 214 | static std::string get_working_directory(); 215 | 216 | int argc; 217 | char** argv; 218 | }; 219 | 220 | namespace Utility { 221 | 222 | template 223 | void move_to_front(std::vector& vec, Predicate pred) { 224 | auto it = std::find_if(vec.begin(), vec.end(), pred); 225 | 226 | if (it != vec.end()) 227 | { 228 | std::rotate(vec.begin(), it, it + 1); 229 | } 230 | } 231 | } 232 | 233 | } // namespace Stockfish 234 | 235 | #endif // #ifndef MISC_H_INCLUDED 236 | -------------------------------------------------------------------------------- /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 ARCH=x86-64-avx2 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 | 124 | [authors-link]: https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS 125 | [build-link]: https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml 126 | [commits-link]: https://github.com/official-stockfish/Stockfish/commits/master 127 | [discord-link]: https://discord.gg/GWDRS3kU6R 128 | [issue-link]: https://github.com/official-stockfish/Stockfish/issues/new?assignees=&labels=&template=BUG-REPORT.yml 129 | [discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new 130 | [fishtest-link]: https://tests.stockfishchess.org/tests 131 | [guideline-link]: https://github.com/official-stockfish/fishtest/wiki/Creating-my-first-test 132 | [license-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt 133 | [programming-link]: https://www.chessprogramming.org/Main_Page 134 | [programmingsf-link]: https://www.chessprogramming.org/Stockfish 135 | [readme-link]: https://github.com/official-stockfish/Stockfish/blob/master/README.md 136 | [release-link]: https://github.com/official-stockfish/Stockfish/releases/latest 137 | [src-link]: https://github.com/official-stockfish/Stockfish/tree/master/src 138 | [stockfish128-logo]: https://stockfishchess.org/images/logo/icon_128x128.png 139 | [uci-link]: https://backscattering.de/chess/uci/ 140 | [website-link]: https://stockfishchess.org 141 | [website-blog-link]: https://stockfishchess.org/blog/ 142 | [wiki-link]: https://github.com/official-stockfish/Stockfish/wiki 143 | [wiki-compile-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source 144 | [wiki-uci-link]: https://github.com/official-stockfish/Stockfish/wiki/UCI-&-Commands 145 | [wiki-usage-link]: https://github.com/official-stockfish/Stockfish/wiki/Download-and-usage 146 | [worker-link]: https://github.com/official-stockfish/fishtest/wiki/Running-the-worker 147 | 148 | [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 149 | [commits-badge]: https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge 150 | [discord-badge]: https://img.shields.io/discord/435943710472011776?style=for-the-badge&label=discord&logo=Discord 151 | [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 152 | [license-badge]: https://img.shields.io/github/license/official-stockfish/Stockfish?style=for-the-badge&label=license&color=success 153 | [release-badge]: https://img.shields.io/github/v/release/official-stockfish/Stockfish?style=for-the-badge&label=official%20release 154 | [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 155 | -------------------------------------------------------------------------------- /src/movepick.h: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) 4 | 5 | Stockfish is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Stockfish is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | #ifndef MOVEPICK_H_INCLUDED 20 | #define MOVEPICK_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include // IWYU pragma: keep 30 | 31 | #include "movegen.h" 32 | #include "position.h" 33 | #include "types.h" 34 | 35 | namespace Stockfish { 36 | 37 | constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 38 | constexpr int CORRECTION_HISTORY_SIZE = 16384; // has to be a power of 2 39 | constexpr int CORRECTION_HISTORY_LIMIT = 1024; 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 | // StatsEntry stores the stat table value. It is usually a number but could 58 | // be a move or even a nested history. We use a class instead of a naked value 59 | // to directly call history update operator<<() on the entry so to use stats 60 | // tables at caller sites as simple multi-dim arrays. 61 | template 62 | class StatsEntry { 63 | 64 | T entry; 65 | 66 | public: 67 | void operator=(const T& v) { entry = v; } 68 | T* operator&() { return &entry; } 69 | T* operator->() { return &entry; } 70 | operator const T&() const { return entry; } 71 | 72 | void operator<<(int bonus) { 73 | static_assert(D <= std::numeric_limits::max(), "D overflows T"); 74 | 75 | // Make sure that bonus is in range [-D, D] 76 | int clampedBonus = std::clamp(bonus, -D, D); 77 | entry += clampedBonus - entry * std::abs(clampedBonus) / D; 78 | 79 | assert(std::abs(entry) <= D); 80 | } 81 | }; 82 | 83 | // Stats is a generic N-dimensional array used to store various statistics. 84 | // The first template parameter T is the base type of the array, and the second 85 | // template parameter D limits the range of updates in [-D, D] when we update 86 | // values with the << operator, while the last parameters (Size and Sizes) 87 | // encode the dimensions of the array. 88 | template 89 | struct Stats: public std::array, Size> { 90 | using stats = Stats; 91 | 92 | void fill(const T& v) { 93 | 94 | // For standard-layout 'this' points to the first struct member 95 | assert(std::is_standard_layout_v); 96 | 97 | using entry = StatsEntry; 98 | entry* p = reinterpret_cast(this); 99 | std::fill(p, p + sizeof(*this) / sizeof(entry), v); 100 | } 101 | }; 102 | 103 | template 104 | struct Stats: public std::array, Size> {}; 105 | 106 | // In stats table, D=0 means that the template parameter is not used 107 | enum StatsParams { 108 | NOT_USED = 0 109 | }; 110 | enum StatsType { 111 | NoCaptures, 112 | Captures 113 | }; 114 | 115 | // ButterflyHistory records how often quiet moves have been successful or unsuccessful 116 | // during the current search, and is used for reduction and move ordering decisions. 117 | // It uses 2 tables (one for each color) indexed by the move's from and to squares, 118 | // see www.chessprogramming.org/Butterfly_Boards (~11 elo) 119 | using ButterflyHistory = Stats; 120 | 121 | // CounterMoveHistory stores counter moves indexed by [piece][to] of the previous 122 | // move, see www.chessprogramming.org/Countermove_Heuristic 123 | using CounterMoveHistory = Stats; 124 | 125 | // CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] 126 | using CapturePieceToHistory = Stats; 127 | 128 | // PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] 129 | using PieceToHistory = Stats; 130 | 131 | // ContinuationHistory is the combined history of a given pair of moves, usually 132 | // the current one given a previous one. The nested history table is based on 133 | // PieceToHistory instead of ButterflyBoards. 134 | // (~63 elo) 135 | using ContinuationHistory = Stats; 136 | 137 | // PawnHistory is addressed by the pawn structure and a move's [piece][to] 138 | using PawnHistory = Stats; 139 | 140 | // CorrectionHistory is addressed by color and pawn structure 141 | using CorrectionHistory = 142 | Stats; 143 | 144 | // MovePicker class is used to pick one pseudo-legal move at a time from the 145 | // current position. The most important method is next_move(), which returns a 146 | // new pseudo-legal move each time it is called, until there are no moves left, 147 | // when Move::none() is returned. In order to improve the efficiency of the 148 | // alpha-beta algorithm, MovePicker attempts to return the moves which are most 149 | // likely to get a cut-off first. 150 | class MovePicker { 151 | 152 | enum PickType { 153 | Next, 154 | Best 155 | }; 156 | 157 | public: 158 | MovePicker(const MovePicker&) = delete; 159 | MovePicker& operator=(const MovePicker&) = delete; 160 | MovePicker(const Position&, 161 | Move, 162 | Depth, 163 | const ButterflyHistory*, 164 | const CapturePieceToHistory*, 165 | const PieceToHistory**, 166 | const PawnHistory*, 167 | Move, 168 | const Move*); 169 | MovePicker(const Position&, 170 | Move, 171 | Depth, 172 | const ButterflyHistory*, 173 | const CapturePieceToHistory*, 174 | const PieceToHistory**, 175 | const PawnHistory*); 176 | MovePicker(const Position&, Move, int, const CapturePieceToHistory*); 177 | Move next_move(bool skipQuiets = false); 178 | 179 | private: 180 | template 181 | Move select(Pred); 182 | template 183 | void score(); 184 | ExtMove* begin() { return cur; } 185 | ExtMove* end() { return endMoves; } 186 | 187 | const Position& pos; 188 | const ButterflyHistory* mainHistory; 189 | const CapturePieceToHistory* captureHistory; 190 | const PieceToHistory** continuationHistory; 191 | const PawnHistory* pawnHistory; 192 | Move ttMove; 193 | ExtMove refutations[3], *cur, *endMoves, *endBadCaptures, *beginBadQuiets, *endBadQuiets; 194 | int stage; 195 | int threshold; 196 | Depth depth; 197 | ExtMove moves[MAX_MOVES]; 198 | }; 199 | 200 | } // namespace Stockfish 201 | 202 | #endif // #ifndef MOVEPICK_H_INCLUDED 203 | -------------------------------------------------------------------------------- /src/bitboard.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Stockfish, a UCI chess playing engine derived from Glaurung 2.1 3 | Copyright (C) 2004-2024 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 "bitboard.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "misc.h" 26 | 27 | namespace Stockfish { 28 | 29 | uint8_t PopCnt16[1 << 16]; 30 | uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; 31 | 32 | Bitboard LineBB[SQUARE_NB][SQUARE_NB]; 33 | Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; 34 | Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; 35 | Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; 36 | 37 | Magic RookMagics[SQUARE_NB]; 38 | Magic BishopMagics[SQUARE_NB]; 39 | 40 | namespace { 41 | 42 | Bitboard RookTable[0x19000]; // To store rook attacks 43 | Bitboard BishopTable[0x1480]; // To store bishop attacks 44 | 45 | void init_magics(PieceType pt, Bitboard table[], Magic magics[]); 46 | 47 | // Returns the bitboard of target square for the given step 48 | // from the given square. If the step is off the board, returns empty bitboard. 49 | Bitboard safe_destination(Square s, int step) { 50 | Square to = Square(s + step); 51 | return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); 52 | } 53 | } 54 | 55 | // Returns an ASCII representation of a bitboard suitable 56 | // to be printed to standard output. Useful for debugging. 57 | std::string Bitboards::pretty(Bitboard b) { 58 | 59 | std::string s = "+---+---+---+---+---+---+---+---+\n"; 60 | 61 | for (Rank r = RANK_8; r >= RANK_1; --r) 62 | { 63 | for (File f = FILE_A; f <= FILE_H; ++f) 64 | s += b & make_square(f, r) ? "| X " : "| "; 65 | 66 | s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+\n"; 67 | } 68 | s += " a b c d e f g h\n"; 69 | 70 | return s; 71 | } 72 | 73 | 74 | // Initializes various bitboard tables. It is called at 75 | // startup and relies on global objects to be already zero-initialized. 76 | void Bitboards::init() { 77 | 78 | for (unsigned i = 0; i < (1 << 16); ++i) 79 | PopCnt16[i] = uint8_t(std::bitset<16>(i).count()); 80 | 81 | for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) 82 | for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) 83 | SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); 84 | 85 | init_magics(ROOK, RookTable, RookMagics); 86 | init_magics(BISHOP, BishopTable, BishopMagics); 87 | 88 | for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) 89 | { 90 | PawnAttacks[WHITE][s1] = pawn_attacks_bb(square_bb(s1)); 91 | PawnAttacks[BLACK][s1] = pawn_attacks_bb(square_bb(s1)); 92 | 93 | for (int step : {-9, -8, -7, -1, 1, 7, 8, 9}) 94 | PseudoAttacks[KING][s1] |= safe_destination(s1, step); 95 | 96 | for (int step : {-17, -15, -10, -6, 6, 10, 15, 17}) 97 | PseudoAttacks[KNIGHT][s1] |= safe_destination(s1, step); 98 | 99 | PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb(s1, 0); 100 | PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ROOK][s1] = attacks_bb(s1, 0); 101 | 102 | for (PieceType pt : {BISHOP, ROOK}) 103 | for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) 104 | { 105 | if (PseudoAttacks[pt][s1] & s2) 106 | { 107 | LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2; 108 | BetweenBB[s1][s2] = 109 | (attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1))); 110 | } 111 | BetweenBB[s1][s2] |= s2; 112 | } 113 | } 114 | } 115 | 116 | namespace { 117 | 118 | Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) { 119 | 120 | Bitboard attacks = 0; 121 | Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST}; 122 | Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST}; 123 | 124 | for (Direction d : (pt == ROOK ? RookDirections : BishopDirections)) 125 | { 126 | Square s = sq; 127 | while (safe_destination(s, d)) 128 | { 129 | attacks |= (s += d); 130 | if (occupied & s) 131 | { 132 | break; 133 | } 134 | } 135 | } 136 | 137 | return attacks; 138 | } 139 | 140 | 141 | // Computes all rook and bishop attacks at startup. Magic 142 | // bitboards are used to look up attacks of sliding pieces. As a reference see 143 | // www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so 144 | // called "fancy" approach. 145 | void init_magics(PieceType pt, Bitboard table[], Magic magics[]) { 146 | 147 | // Optimal PRNG seeds to pick the correct magics in the shortest time 148 | int seeds[][RANK_NB] = {{8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020}, 149 | {728, 10316, 55013, 32803, 12281, 15100, 16645, 255}}; 150 | 151 | Bitboard occupancy[4096], reference[4096], edges, b; 152 | int epoch[4096] = {}, cnt = 0, size = 0; 153 | 154 | for (Square s = SQ_A1; s <= SQ_H8; ++s) 155 | { 156 | // Board edges are not considered in the relevant occupancies 157 | edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s)); 158 | 159 | // Given a square 's', the mask is the bitboard of sliding attacks from 160 | // 's' computed on an empty board. The index must be big enough to contain 161 | // all the attacks for each possible subset of the mask and so is 2 power 162 | // the number of 1s of the mask. Hence we deduce the size of the shift to 163 | // apply to the 64 or 32 bits word to get the index. 164 | Magic& m = magics[s]; 165 | m.mask = sliding_attack(pt, s, 0) & ~edges; 166 | m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask); 167 | 168 | // Set the offset for the attacks table of the square. We have individual 169 | // table sizes for each square with "Fancy Magic Bitboards". 170 | m.attacks = s == SQ_A1 ? table : magics[s - 1].attacks + size; 171 | 172 | // Use Carry-Rippler trick to enumerate all subsets of masks[s] and 173 | // store the corresponding sliding attack bitboard in reference[]. 174 | b = size = 0; 175 | do 176 | { 177 | occupancy[size] = b; 178 | reference[size] = sliding_attack(pt, s, b); 179 | 180 | if (HasPext) 181 | m.attacks[pext(b, m.mask)] = reference[size]; 182 | 183 | size++; 184 | b = (b - m.mask) & m.mask; 185 | } while (b); 186 | 187 | if (HasPext) 188 | continue; 189 | 190 | PRNG rng(seeds[Is64Bit][rank_of(s)]); 191 | 192 | // Find a magic for square 's' picking up an (almost) random number 193 | // until we find the one that passes the verification test. 194 | for (int i = 0; i < size;) 195 | { 196 | for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6;) 197 | m.magic = rng.sparse_rand(); 198 | 199 | // A good magic must map every possible occupancy to an index that 200 | // looks up the correct sliding attack in the attacks[s] database. 201 | // Note that we build up the database for square 's' as a side 202 | // effect of verifying the magic. Keep track of the attempt count 203 | // and save it in epoch[], little speed-up trick to avoid resetting 204 | // m.attacks[] after every failed attempt. 205 | for (++cnt, i = 0; i < size; ++i) 206 | { 207 | unsigned idx = m.index(occupancy[i]); 208 | 209 | if (epoch[idx] < cnt) 210 | { 211 | epoch[idx] = cnt; 212 | m.attacks[idx] = reference[i]; 213 | } 214 | else if (m.attacks[idx] != reference[i]) 215 | break; 216 | } 217 | } 218 | } 219 | } 220 | } 221 | 222 | } // namespace Stockfish 223 | --------------------------------------------------------------------------------