├── .clang-format ├── .gitignore ├── .vscode └── settings.json ├── LICENSE.txt ├── README.md ├── makefile ├── src ├── bench.cpp ├── bench.hpp ├── chess │ ├── attacks.hpp │ ├── bitboards.hpp │ ├── board.hpp │ ├── castling.hpp │ ├── fens.hpp │ ├── mailbox.hpp │ ├── movegen.hpp │ ├── movegencountonly.hpp │ ├── moves.hpp │ ├── square.hpp │ ├── types.hpp │ └── zobrist.hpp ├── evaluate.cpp ├── evaluation │ ├── evaluate.hpp │ ├── material.hpp │ └── psqt.hpp ├── hexadecane_512_v2.net ├── incbin │ └── incbin.h ├── main.cpp ├── misc │ ├── argparse.hpp │ └── utils.hpp ├── nnue.cpp ├── nnue │ ├── accumulator.hpp │ ├── constants.hpp │ ├── nnue.hpp │ └── types.hpp ├── perfsuite.hpp ├── perftsuite.cpp ├── search.cpp └── search │ ├── constants.hpp │ ├── history.hpp │ ├── moveorder.hpp │ ├── search.hpp │ ├── searchinfo.hpp │ ├── searchstack.hpp │ ├── searchthread.hpp │ ├── timeman.hpp │ ├── tt.hpp │ └── types.hpp └── standard.epd /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | BasedOnStyle: LLVM 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: true 6 | AlignConsecutiveDeclarations: true 7 | AlignEscapedNewlinesLeft: true 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: None 14 | AllowShortIfStatementsOnASingleLine: Never 15 | AllowShortLoopsOnASingleLine: false 16 | AlwaysBreakTemplateDeclarations: true 17 | BinPackArguments: true 18 | BinPackParameters: true 19 | BraceWrapping: 20 | AfterClass: true 21 | AfterControlStatement: true 22 | AfterEnum: true 23 | AfterFunction: true 24 | AfterNamespace: true 25 | AfterStruct: true 26 | AfterUnion: true 27 | AfterExternBlock: true 28 | BeforeCatch: true 29 | BeforeElse: true 30 | IndentBraces: true 31 | BreakBeforeBinaryOperators: None 32 | BreakBeforeTernaryOperators: true 33 | BreakConstructorInitializersBeforeComma: true 34 | ColumnLimit: 128 35 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 36 | ContinuationIndentWidth: 4 37 | DerivePointerAlignment: false 38 | ExperimentalAutoDetectBinPacking: false 39 | IndentCaseLabels: true 40 | IndentGotoLabels: true 41 | IndentPPDirectives: AfterHash 42 | IndentWidth: 4 43 | KeepEmptyLinesAtTheStartOfBlocks: false 44 | MacroBlockBegin: '' 45 | MacroBlockEnd: '' 46 | MaxEmptyLinesToKeep: 1 47 | NamespaceIndentation: All 48 | PointerAlignment: Left 49 | ReflowComments: true 50 | SortIncludes: true 51 | SpaceAfterCStyleCast: true 52 | SpaceBeforeAssignmentOperators: true 53 | SpaceBeforeParens: ControlStatements 54 | SpaceInEmptyParentheses: false 55 | SpacesBeforeTrailingComments: 1 56 | SpacesInAngles: false 57 | SpacesInCStyleCastParentheses: false 58 | SpacesInContainerLiterals: false 59 | SpacesInParentheses: false 60 | SpacesInSquareBrackets: false 61 | Standard: Cpp11 62 | TabWidth: 4 63 | UseTab: Never -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Jet/ 2 | .vscode/ 3 | 4 | # Compiled object files 5 | *.o 6 | *.obj 7 | *.bin 8 | 9 | # Executables 10 | bin/ 11 | *.exe 12 | 13 | # Build directory 14 | build/ 15 | testing/ 16 | 17 | # Dependency and package management directories 18 | vendor/ 19 | lib/ 20 | libs/ 21 | 22 | # macOS-specific 23 | .DS_Store 24 | 25 | # Linux-specific 26 | *.out 27 | *.so 28 | *.a 29 | 30 | # Windows-specific 31 | Thumbs.db -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "xstring": "cpp", 4 | "iostream": "cpp", 5 | "array": "cpp", 6 | "algorithm": "cpp", 7 | "cmath": "cpp", 8 | "atomic": "cpp", 9 | "bit": "cpp", 10 | "cctype": "cpp", 11 | "clocale": "cpp", 12 | "compare": "cpp", 13 | "concepts": "cpp", 14 | "cstddef": "cpp", 15 | "cstdint": "cpp", 16 | "cstdio": "cpp", 17 | "cstdlib": "cpp", 18 | "cstring": "cpp", 19 | "ctime": "cpp", 20 | "cwchar": "cpp", 21 | "exception": "cpp", 22 | "string": "cpp", 23 | "initializer_list": "cpp", 24 | "ios": "cpp", 25 | "iosfwd": "cpp", 26 | "istream": "cpp", 27 | "iterator": "cpp", 28 | "limits": "cpp", 29 | "memory": "cpp", 30 | "new": "cpp", 31 | "optional": "cpp", 32 | "ostream": "cpp", 33 | "stdexcept": "cpp", 34 | "streambuf": "cpp", 35 | "system_error": "cpp", 36 | "tuple": "cpp", 37 | "type_traits": "cpp", 38 | "typeinfo": "cpp", 39 | "utility": "cpp", 40 | "xfacet": "cpp", 41 | "xiosbase": "cpp", 42 | "xlocale": "cpp", 43 | "xlocinfo": "cpp", 44 | "xlocnum": "cpp", 45 | "xmemory": "cpp", 46 | "xstddef": "cpp", 47 | "xtr1common": "cpp", 48 | "xutility": "cpp", 49 | "bitset": "cpp", 50 | "list": "cpp", 51 | "vector": "cpp", 52 | "xhash": "cpp", 53 | "unordered_map": "cpp", 54 | "functional": "cpp", 55 | "charconv": "cpp", 56 | "random": "cpp", 57 | "fstream": "cpp", 58 | "sstream": "cpp", 59 | "iomanip": "cpp", 60 | "xlocmon": "cpp", 61 | "xloctime": "cpp", 62 | "chrono": "cpp", 63 | "forward_list": "cpp", 64 | "format": "cpp", 65 | "locale": "cpp", 66 | "mutex": "cpp", 67 | "ratio": "cpp", 68 | "stop_token": "cpp", 69 | "thread": "cpp", 70 | "xlocbuf": "cpp", 71 | "xlocmes": "cpp", 72 | "cassert": "cpp", 73 | "string_view": "cpp" 74 | } 75 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jet Chess Engine 🛠️ 2 | 3 | Jet is a cutting-edge chess engine renowned for its highly intelligent chess playing capabilities. With its ultra-fast move generator, advanced search algorithms, and neural network-based evaluation system, Jet pushes the boundaries of chess engine development. 🚀 4 | 5 | ## Key Features 🌟 6 | 7 | - **Ultra Fast Move Generator**: Jet's move generator is optimized for speed, capable of processing up to 1 billion nodes per second on high-end hardware like the i7 14700KF. ⚡️ 8 | 9 | - **GPL 3 Licensed**: Jet is released under the GNU General Public License Version 3, affirming its commitment to openness and collaboration within the community. 📜 10 | 11 | - **NNUE Evaluation with Autovectorized Inference**: Jet leverages Neural Network with Efficiently Updatable Neural Network (NNUE) evaluation, utilizing autovectorized inference for position evaluations. 🧠 12 | 13 | - **Search features**: Jet employs Alpha-beta pruning alongside advanced search techniques like Late Move Reduction and Singular Extensions to optimize its search, enhancing computational efficiency and playing strength. 🔍 14 | 15 | ## Building Instructions 🏗️ 16 | 17 | ### Required Tools 🛠️ 18 | 19 | - Make 20 | - Clang compiler (GCC works but not preferred) 21 | 22 | ### Building on Windows 🖥️ 23 | 24 | ```bash 25 | git clone https://github.com/rafid-dev/jet.git 26 | cd jet 27 | make -j CXX=clang++ 28 | ``` 29 | 30 | ### Building on Linux 🐧 31 | 32 | Replace `XX` with your Linux's clang version or leave as `clang++` if symlinked. 33 | 34 | ```bash 35 | git clone https://github.com/rafid-dev/jet 36 | cd jet 37 | make -j CXX=clang++-XX 38 | ``` 39 | 40 | Ensure everything works fine by running `./Jet bench` and verifying that the nodes match the current commit bench nodes. ✔️ 41 | 42 | ## Testing and Support 🛡️ 43 | 44 | Testing of Jet is supported by the OpenBench Instance at [https://rafiddev.pythonanywhere.com/](https://rafiddev.pythonanywhere.com/). 🧪 45 | 46 | Special thanks to contributors: 47 | 48 | - **Vast** ([GitHub](https://github.com/Vast342)), author of Clarity ([GitHub](https://github.com/Vast342/Clarity/)) 49 | - **Sazgr** ([GitHub](https://github.com/Sazgr/peacekeeper)) 50 | 51 | And my great friends: 52 | - **Raiyad** 53 | - **Ariyan737** 54 | - **Rafsan60** 55 | - **Uganiga** 56 | - **Daredevil1618** 57 | 58 | ## Acknowledgements 🙏 59 | 60 | Jet acknowledges the following projects for their contributions and inspiration: 61 | 62 | - **Disservin's Chess Library** ([GitHub](https://github.com/Disservin/chess-library)) for ideas on organizing move generation functions. 63 | - **Grapheus NNUE Trainer** by Luecx ([GitHub](https://github.com/luecx/Grapheus/)) 64 | - **OpenBench** for distributed SPRT testing by Andrew ([GitHub](https://github.com/AndyGrant/OpenBench)) 65 | 66 | ## Supporting Development 💡 67 | 68 | Support the development of Jet by contributing threads on [https://rafiddev.pythonanywhere.com/](https://rafiddev.pythonanywhere.com/). Register an account and contact the developer for more information. 69 | 70 | ## Contact 📧 71 | 72 | For inquiries and collaboration opportunities, reach out to the developer on Discord at `j.en`. 73 | 74 | Let's push the boundaries of chess engine development with Jet! ♟️ 75 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # Compiler and flags 2 | CXX := clang++ 3 | ARCH := -march=native 4 | CXXFLAGS := -std=c++20 -flto $(ARCH) -fexceptions -Wall -Wextra 5 | LDFLAGS := 6 | EVALFILE := src/hexadecane_512_v2.net 7 | 8 | CXXFLAGS += -DNNFILE=\"$(EVALFILE)\" 9 | 10 | # Debug compiler flags 11 | DEBUG_CXXFLAGS := -g3 -O1 -DDEBUG -fsanitize=address -fsanitize=undefined 12 | 13 | BUILD_CXXFLAGS := -DNDEBUG -O3 14 | 15 | # Directories 16 | SRC_DIR := src 17 | BUILD_DIR := build 18 | 19 | # Source files 20 | SRCS := $(wildcard $(SRC_DIR)/*.cpp) 21 | OBJS := $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SRCS)) 22 | 23 | # Binary name (set to Jet) 24 | EXE := Jet 25 | 26 | # Append .exe to the binary name on Windows 27 | ifeq ($(OS),Windows_NT) 28 | CXXFLAGS += -fuse-ld=lld 29 | override EXE := $(EXE).exe 30 | endif 31 | 32 | # Default target 33 | all: CXXFLAGS += $(BUILD_CXXFLAGS) 34 | all: $(EXE) 35 | 36 | # Rule to build the target binary 37 | $(EXE): $(OBJS) 38 | $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJS) 39 | 40 | # Rule to build object files 41 | $(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp | $(BUILD_DIR) 42 | $(CXX) $(CXXFLAGS) -c -o $@ $< 43 | 44 | # Create directories if they don't exist 45 | $(BUILD_DIR): 46 | mkdir -p $@ 47 | 48 | # Debug target 49 | debug: CXXFLAGS += $(DEBUG_CXXFLAGS) 50 | debug: $(EXE) 51 | 52 | # Clean the build 53 | clean: 54 | rm -rf $(BUILD_DIR) $(EXE) $(PGO_DIR) 55 | 56 | # Phony targets 57 | .PHONY: all debug clean pgo-generate 58 | 59 | # Disable built-in rules and variables 60 | .SUFFIXES: -------------------------------------------------------------------------------- /src/bench.cpp: -------------------------------------------------------------------------------- 1 | #include "bench.hpp" 2 | #include "misc/utils.hpp" 3 | #include "search/search.hpp" 4 | 5 | namespace jet { 6 | 7 | const std::array bench_fens = { 8 | "r3k2r/2pb1ppp/2pp1q2/p7/1nP1B3/1P2P3/P2N1PPP/R2QK2R w KQkq a6 0 14", 9 | "4rrk1/2p1b1p1/p1p3q1/4p3/2P2n1p/1P1NR2P/PB3PP1/3R1QK1 b - - 2 24", 10 | "r3qbrk/6p1/2b2pPp/p3pP1Q/PpPpP2P/3P1B2/2PB3K/R5R1 w - - 16 42", 11 | "6k1/1R3p2/6p1/2Bp3p/3P2q1/P7/1P2rQ1K/5R2 b - - 4 44", 12 | "8/8/1p2k1p1/3p3p/1p1P1P1P/1P2PK2/8/8 w - - 3 54", 13 | "7r/2p3k1/1p1p1qp1/1P1Bp3/p1P2r1P/P7/4R3/Q4RK1 w - - 0 36", 14 | "r1bq1rk1/pp2b1pp/n1pp1n2/3P1p2/2P1p3/2N1P2N/PP2BPPP/R1BQ1RK1 b - - 2 10", 15 | "3r3k/2r4p/1p1b3q/p4P2/P2Pp3/1B2P3/3BQ1RP/6K1 w - - 3 87", 16 | "2r4r/1p4k1/1Pnp4/3Qb1pq/8/4BpPp/5P2/2RR1BK1 w - - 0 42", 17 | "4q1bk/6b1/7p/p1p4p/PNPpP2P/KN4P1/3Q4/4R3 b - - 0 37", 18 | "2q3r1/1r2pk2/pp3pp1/2pP3p/P1Pb1BbP/1P4Q1/R3NPP1/4R1K1 w - - 2 34", 19 | "1r2r2k/1b4q1/pp5p/2pPp1p1/P3Pn2/1P1B1Q1P/2R3P1/4BR1K b - - 1 37", 20 | "r3kbbr/pp1n1p1P/3ppnp1/q5N1/1P1pP3/P1N1B3/2P1QP2/R3KB1R b KQkq b3 0 17", 21 | "8/6pk/2b1Rp2/3r4/1R1B2PP/P5K1/8/2r5 b - - 16 42", 22 | "1r4k1/4ppb1/2n1b1qp/pB4p1/1n1BP1P1/7P/2PNQPK1/3RN3 w - - 8 29", 23 | "8/p2B4/PkP5/4p1pK/4Pb1p/5P2/8/8 w - - 29 68", 24 | "3r4/ppq1ppkp/4bnp1/2pN4/2P1P3/1P4P1/PQ3PBP/R4K2 b - - 2 20", 25 | "5rr1/4n2k/4q2P/P1P2n2/3B1p2/4pP2/2N1P3/1RR1K2Q w - - 1 49", 26 | "1r5k/2pq2p1/3p3p/p1pP4/4QP2/PP1R3P/6PK/8 w - - 1 51", 27 | "q5k1/5ppp/1r3bn1/1B6/P1N2P2/BQ2P1P1/5K1P/8 b - - 2 34", 28 | "r1b2k1r/5n2/p4q2/1ppn1Pp1/3pp1p1/NP2P3/P1PPBK2/1RQN2R1 w - - 0 22", 29 | "r1bqk2r/pppp1ppp/5n2/4b3/4P3/P1N5/1PP2PPP/R1BQKB1R w KQkq - 0 5", 30 | "r1bqr1k1/pp1p1ppp/2p5/8/3N1Q2/P2BB3/1PP2PPP/R3K2n b Q - 1 12", 31 | "r1bq2k1/p4r1p/1pp2pp1/3p4/1P1B3Q/P2B1N2/2P3PP/4R1K1 b - - 2 19", 32 | "r4qk1/6r1/1p4p1/2ppBbN1/1p5Q/P7/2P3PP/5RK1 w - - 2 25", 33 | "r7/6k1/1p6/2pp1p2/7Q/8/p1P2K1P/8 w - - 0 32", 34 | "r3k2r/ppp1pp1p/2nqb1pn/3p4/4P3/2PP4/PP1NBPPP/R2QK1NR w KQkq - 1 5", 35 | "3r1rk1/1pp1pn1p/p1n1q1p1/3p4/Q3P3/2P5/PP1NBPPP/4RRK1 w - - 0 12", 36 | "5rk1/1pp1pn1p/p3Brp1/8/1n6/5N2/PP3PPP/2R2RK1 w - - 2 20", 37 | "8/1p2pk1p/p1p1r1p1/3n4/8/5R2/PP3PPP/4R1K1 b - - 3 27", 38 | "8/4pk2/1p1r2p1/p1p4p/Pn5P/3R4/1P3PP1/4RK2 w - - 1 33", 39 | "8/5k2/1pnrp1p1/p1p4p/P6P/4R1PK/1P3P2/4R3 b - - 1 38", 40 | "8/8/1p1kp1p1/p1pr1n1p/P6P/1R4P1/1P3PK1/1R6 b - - 15 45", 41 | "8/8/1p1k2p1/p1prp2p/P2n3P/6P1/1P1R1PK1/4R3 b - - 5 49", 42 | "8/8/1p4p1/p1p2k1p/P2npP1P/4K1P1/1P6/3R4 w - - 6 54", 43 | "8/8/1p4p1/p1p2k1p/P2n1P1P/4K1P1/1P6/6R1 b - - 6 59", 44 | "8/5k2/1p4p1/p1pK3p/P2n1P1P/6P1/1P6/4R3 b - - 14 63", 45 | "8/1R6/1p1K1kp1/p6p/P1p2P1P/6P1/1Pn5/8 w - - 0 67", 46 | "1rb1rn1k/p3q1bp/2p3p1/2p1p3/2P1P2N/PP1RQNP1/1B3P2/4R1K1 b - - 4 23", 47 | "4rrk1/pp1n1pp1/q5p1/P1pP4/2n3P1/7P/1P3PB1/R1BQ1RK1 w - - 3 22", 48 | "r2qr1k1/pb1nbppp/1pn1p3/2ppP3/3P4/2PB1NN1/PP3PPP/R1BQR1K1 w - - 4 12", 49 | "2r2k2/8/4P1R1/1p6/8/P4K1N/7b/2B5 b - - 0 55", 50 | "6k1/5pp1/8/2bKP2P/2P5/p4PNb/B7/8 b - - 1 44", 51 | "2rqr1k1/1p3p1p/p2p2p1/P1nPb3/2B1P3/5P2/1PQ2NPP/R1R4K w - - 3 25", 52 | "r1b2rk1/p1q1ppbp/6p1/2Q5/8/4BP2/PPP3PP/2KR1B1R b - - 2 14", 53 | "6r1/5k2/p1b1r2p/1pB1p1p1/1Pp3PP/2P1R1K1/2P2P2/3R4 w - - 1 36", 54 | "rnbqkb1r/pppppppp/5n2/8/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2", 55 | "2rr2k1/1p4bp/p1q1p1p1/4Pp1n/2PB4/1PN3P1/P3Q2P/2RR2K1 w - f6 0 20", 56 | "3br1k1/p1pn3p/1p3n2/5pNq/2P1p3/1PN3PP/P2Q1PB1/4R1K1 w - - 0 23", 57 | "2r2b2/5p2/5k2/p1r1pP2/P2pB3/1P3P2/K1P3R1/7R w - - 23 93", 58 | }; 59 | 60 | void StartBenchmark(search::SearchThread& st) { 61 | uint64_t nodes = 0; 62 | uint64_t time_elapsed = 0; 63 | 64 | int count = 0; 65 | 66 | for (const auto& fen : bench_fens) { 67 | st.board().setFen(fen); 68 | 69 | auto start = misc::tick(); 70 | search::search(st, 10); 71 | auto end = misc::tick(); 72 | 73 | nodes += st.nodes; 74 | time_elapsed += (end - start); 75 | 76 | count++; 77 | 78 | printf("Position [%2d]: %12d nodes %8d nps", count, static_cast(st.nodes), 79 | static_cast(1000.0f * nodes / (time_elapsed + 1))); 80 | std::cout << std::endl; 81 | } 82 | 83 | printf("Finished: %17d nodes %8d nps\n", static_cast(nodes), 84 | static_cast(1000.0f * nodes / (time_elapsed + 1))); 85 | std::cout << std::flush; 86 | } 87 | 88 | } // namespace jet 89 | -------------------------------------------------------------------------------- /src/bench.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "search/searchthread.hpp" 4 | 5 | namespace jet { 6 | 7 | void StartBenchmark(search::SearchThread& st); 8 | 9 | } // namespace jet 10 | -------------------------------------------------------------------------------- /src/chess/bitboards.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "square.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace chess { 12 | 13 | #define BitboardIterator(X) for (; X;) 14 | 15 | // Bit manipulation functions 16 | namespace bitops { 17 | static constexpr inline void resetLowestBit(U64& b) { 18 | // b = _blsr_u64(b); 19 | b &= b - 1; 20 | } 21 | constexpr inline int popcount(U64 b) { 22 | // return static_cast(_mm_popcnt_u64(b)); 23 | return __builtin_popcountll(b); // let compiler choose 24 | } 25 | 26 | static constexpr inline Square lsb(U64 b) { 27 | // #if defined(__GNUC__) || defined(__clang__) 28 | // return Square(__builtin_ctzll(b)); 29 | // #elif defined(_MSC_VER) 30 | // unsigned long idx; 31 | // _BitScanForward64(&idx, b); 32 | // return static_cast(int(idx)); 33 | // #endif 34 | return static_cast(_tzcnt_u64(b)); 35 | } 36 | 37 | static constexpr inline Square poplsb(U64& b) { 38 | Square sq = lsb(b); 39 | resetLowestBit(b); 40 | return sq; 41 | } 42 | } // namespace bitops 43 | 44 | class Bitboard { 45 | public: 46 | constexpr Bitboard() : m_squares(0){}; 47 | constexpr Bitboard(U64 squares) : m_squares(squares) { 48 | } 49 | 50 | constexpr Bitboard(Square square) : m_squares(1ULL << static_cast(square)) { 51 | } 52 | 53 | constexpr Bitboard(File f) : m_squares(MASK_FILE[static_cast(f)]) { 54 | } 55 | 56 | constexpr Bitboard(Rank r) : m_squares(MASK_RANK[static_cast(r)]) { 57 | } 58 | 59 | constexpr Bitboard(std::initializer_list squares) : m_squares(0) { 60 | for (const auto& sq : squares) { 61 | set(sq); 62 | } 63 | } 64 | 65 | constexpr inline void set(Square square) { 66 | m_squares |= square.bb(); 67 | } 68 | 69 | constexpr inline void set(File f) { 70 | m_squares |= MASK_FILE[static_cast(f)]; 71 | } 72 | 73 | constexpr inline void set(Rank r) { 74 | m_squares |= MASK_RANK[static_cast(r)]; 75 | } 76 | 77 | constexpr inline void zero() { 78 | m_squares = 0; 79 | } 80 | 81 | constexpr inline void clear(Square square) { 82 | m_squares &= ~square.bb(); 83 | } 84 | 85 | constexpr inline void clear(File f) { 86 | m_squares &= ~MASK_FILE[static_cast(f)]; 87 | } 88 | 89 | constexpr inline void clear(Rank r) { 90 | m_squares &= ~MASK_RANK[static_cast(r)]; 91 | } 92 | 93 | constexpr inline Square lsb() const { 94 | return bitops::lsb(m_squares); 95 | } 96 | constexpr inline int popcount() const { 97 | return bitops::popcount(m_squares); 98 | } 99 | 100 | constexpr inline Square poplsb() { 101 | return bitops::poplsb(m_squares); 102 | } 103 | 104 | template 105 | constexpr inline Bitboard shift() { 106 | return Bitboard(_shift_internal(m_squares)); 107 | } 108 | 109 | constexpr inline bool empty() const { 110 | return m_squares == 0; 111 | } 112 | 113 | constexpr inline bool nonEmpty() const { 114 | return !empty(); 115 | } 116 | 117 | constexpr inline bool single() const { 118 | return popcount() == 1; 119 | } 120 | 121 | constexpr inline bool multiple() const { 122 | return popcount() > 1; 123 | } 124 | 125 | constexpr Bitboard operator+(const Bitboard& rhs) const { 126 | return Bitboard(m_squares + rhs.m_squares); 127 | } 128 | 129 | constexpr Bitboard operator-(const Bitboard& rhs) const { 130 | return Bitboard(m_squares - rhs.m_squares); 131 | } 132 | 133 | constexpr Bitboard operator*(const Bitboard& rhs) const { 134 | return Bitboard(m_squares * rhs.m_squares); 135 | } 136 | 137 | constexpr Bitboard operator*(const bool& rhs) const { 138 | return Bitboard(m_squares * rhs); 139 | } 140 | 141 | constexpr Bitboard operator*(const U64& rhs) const { 142 | return Bitboard(m_squares * rhs); 143 | } 144 | 145 | constexpr Bitboard operator&(const Bitboard& rhs) const { 146 | return Bitboard(m_squares & rhs.m_squares); 147 | } 148 | 149 | constexpr Bitboard operator|(const Bitboard& rhs) const { 150 | return Bitboard(m_squares | rhs.m_squares); 151 | } 152 | 153 | constexpr Bitboard operator^(const Bitboard& rhs) const { 154 | return Bitboard(m_squares ^ rhs.m_squares); 155 | } 156 | 157 | constexpr Bitboard operator~() const { 158 | return Bitboard(~m_squares); 159 | } 160 | 161 | constexpr Bitboard operator<<(int i) const { 162 | return Bitboard(m_squares << i); 163 | } 164 | 165 | constexpr Bitboard operator>>(int i) const { 166 | return Bitboard(m_squares >> i); 167 | } 168 | 169 | constexpr Bitboard& operator&=(const Bitboard& rhs) { 170 | m_squares &= rhs.m_squares; 171 | return *this; 172 | } 173 | 174 | constexpr Bitboard& operator|=(const Bitboard& rhs) { 175 | m_squares |= rhs.m_squares; 176 | return *this; 177 | } 178 | 179 | constexpr Bitboard& operator^=(const Bitboard& rhs) { 180 | m_squares ^= rhs.m_squares; 181 | return *this; 182 | } 183 | 184 | constexpr Bitboard& operator<<=(int i) { 185 | m_squares <<= i; 186 | return *this; 187 | } 188 | 189 | constexpr Bitboard& operator>>=(int i) { 190 | m_squares >>= i; 191 | return *this; 192 | } 193 | 194 | constexpr bool operator==(const Bitboard& rhs) const { 195 | return m_squares == rhs.m_squares; 196 | } 197 | 198 | constexpr bool operator!=(const Bitboard& rhs) const { 199 | return m_squares != rhs.m_squares; 200 | } 201 | 202 | constexpr bool operator<(const Bitboard& rhs) const { 203 | return m_squares < rhs.m_squares; 204 | } 205 | 206 | constexpr bool operator>(const Bitboard& rhs) const { 207 | return m_squares > rhs.m_squares; 208 | } 209 | 210 | constexpr bool operator<=(const Bitboard& rhs) const { 211 | return m_squares <= rhs.m_squares; 212 | } 213 | 214 | constexpr bool operator>=(const Bitboard& rhs) const { 215 | return m_squares >= rhs.m_squares; 216 | } 217 | 218 | constexpr operator U64() const { 219 | return m_squares; 220 | } 221 | 222 | constexpr operator bool() const { 223 | return m_squares != 0; 224 | } 225 | 226 | private: 227 | U64 m_squares; 228 | 229 | template 230 | static constexpr inline U64 _shift_internal(U64 b) { 231 | if constexpr (d == Direction::NORTH) { 232 | return b << 8; 233 | } else if constexpr (d == Direction::SOUTH) { 234 | return b >> 8; 235 | } else if constexpr (d == Direction::EAST) { 236 | return (b & ~MASK_FILE[7]) << 1; 237 | } else if constexpr (d == Direction::WEST) { 238 | return (b & ~MASK_FILE[0]) >> 1; 239 | } else if constexpr (d == Direction::NORTH_EAST) { 240 | return (b & ~MASK_FILE[7]) << 9; 241 | } else if constexpr (d == Direction::NORTH_WEST) { 242 | return (b & ~MASK_FILE[0]) << 7; 243 | } else if constexpr (d == Direction::SOUTH_EAST) { 244 | return (b & ~MASK_FILE[7]) >> 7; 245 | } else if constexpr (d == Direction::SOUTH_WEST) { 246 | return (b & ~MASK_FILE[0]) >> 9; 247 | } else { 248 | return 0; 249 | } 250 | } 251 | }; 252 | 253 | inline std::ostream& operator<<(std::ostream& os, const Bitboard& bb) { 254 | constexpr int squareWidth = 8; // Width of each row 255 | 256 | // Convert the Bitboard to a string representation 257 | std::bitset b(static_cast(bb)); 258 | std::string str_bitset = b.to_string(); 259 | 260 | // Output the Bitboard in a more readable format 261 | for (int i = 0; i < chess::NUM_SQUARES; i += squareWidth) { 262 | // Extract a row of bits 263 | std::string row = str_bitset.substr(i, squareWidth); 264 | 265 | // Reverse the row for correct bit order 266 | std::reverse(row.begin(), row.end()); 267 | 268 | // Output the reversed row with spaces between each number 269 | for (char bit : row) { 270 | os << bit << ' '; 271 | } 272 | 273 | // Add a newline after each row 274 | os << '\n'; 275 | } 276 | 277 | // Add a newline for better separation 278 | os << std::endl; 279 | 280 | return os; 281 | } 282 | 283 | } // namespace chess -------------------------------------------------------------------------------- /src/chess/castling.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "square.hpp" 4 | #include "types.hpp" 5 | 6 | #include 7 | #include 8 | 9 | namespace chess { 10 | enum class CastlingSide : uint8_t { KING_SIDE, QUEEN_SIDE }; 11 | 12 | template 13 | struct CastlingRightsBase { 14 | T whiteKingSide : 1; 15 | T whiteQueenSide : 1; 16 | T blackKingSide : 1; 17 | T blackQueenSide : 1; 18 | }; 19 | 20 | class CastlingRights { 21 | public: 22 | CastlingRights() : m_rights({1, 1, 1, 1}) { 23 | } 24 | 25 | void clear() { 26 | std::memset(&m_rights, 0, sizeof(m_rights)); 27 | } 28 | 29 | void loadFromString(std::string_view str) { 30 | clear(); 31 | 32 | for (auto c : str) { 33 | switch (c) { 34 | case 'K': 35 | _setRights(Color::WHITE, CastlingSide::KING_SIDE, 1); 36 | break; 37 | case 'Q': 38 | _setRights(Color::WHITE, CastlingSide::QUEEN_SIDE, 1); 39 | break; 40 | case 'k': 41 | _setRights(Color::BLACK, CastlingSide::KING_SIDE, 1); 42 | break; 43 | case 'q': 44 | _setRights(Color::BLACK, CastlingSide::QUEEN_SIDE, 1); 45 | break; 46 | case '-': 47 | return; 48 | } 49 | } 50 | } 51 | 52 | bool hasCastlingRights(Color c, CastlingSide side) const { 53 | return _getRights(c, side); 54 | } 55 | 56 | bool hasCastlingRights(Color c) const { 57 | return _getRights(c, CastlingSide::KING_SIDE) || _getRights(c, CastlingSide::QUEEN_SIDE); 58 | } 59 | 60 | template 61 | bool hasCastlingRights() const { 62 | return _getRights(); 63 | } 64 | 65 | void setCastlingRights(Color c, CastlingSide side, bool value) { 66 | _setRights(c, side, value); 67 | } 68 | 69 | static constexpr inline Square kingTo(Color c, CastlingSide side) { 70 | const bool kingSide = side == CastlingSide::KING_SIDE; 71 | return Square::relativeSquare(Square::C1 + 4 * kingSide, c); 72 | } 73 | 74 | static constexpr inline Square rookTo(Color c, CastlingSide side) { 75 | const bool kingSide = side == CastlingSide::KING_SIDE; 76 | return Square::relativeSquare(Square::D1 + 2 * kingSide, c); 77 | } 78 | 79 | static constexpr inline Square rookFrom(Color c, CastlingSide side) { 80 | const bool kingSide = side == CastlingSide::KING_SIDE; 81 | return Square::relativeSquare(Square::H1 * kingSide, c); 82 | } 83 | 84 | template 85 | static constexpr inline Square kingTo() { 86 | if constexpr (side == CastlingSide::KING_SIDE) { 87 | return Square::relativeSquare(); 88 | } else { 89 | return Square::relativeSquare(); 90 | } 91 | } 92 | 93 | template 94 | static constexpr inline Square rookTo() { 95 | if constexpr (side == CastlingSide::KING_SIDE) { 96 | return Square::relativeSquare(); 97 | } else { 98 | return Square::relativeSquare(); 99 | } 100 | } 101 | 102 | template 103 | static constexpr inline Square rookFrom() { 104 | if constexpr (side == CastlingSide::KING_SIDE) { 105 | return Square::relativeSquare(); 106 | } else { 107 | return Square::relativeSquare(); 108 | } 109 | } 110 | 111 | inline std::string toString() const { 112 | std::string str; 113 | 114 | if (_getRights(Color::WHITE, CastlingSide::KING_SIDE)) { 115 | str += 'K'; 116 | } 117 | 118 | if (_getRights(Color::WHITE, CastlingSide::QUEEN_SIDE)) { 119 | str += 'Q'; 120 | } 121 | 122 | if (_getRights(Color::BLACK, CastlingSide::KING_SIDE)) { 123 | str += 'k'; 124 | } 125 | 126 | if (_getRights(Color::BLACK, CastlingSide::QUEEN_SIDE)) { 127 | str += 'q'; 128 | } 129 | 130 | return str; 131 | } 132 | 133 | static constexpr inline CastlingSide getCastlingSide(Square sq, Square kingSq) { 134 | return static_cast((sq > kingSq) ^ 1); 135 | } 136 | 137 | constexpr inline int index() const { 138 | return hasCastlingRights() + 139 | 2 * hasCastlingRights() + 140 | 4 * hasCastlingRights() + 141 | 8 * hasCastlingRights(); 142 | } 143 | 144 | private: 145 | using RightsType = CastlingRightsBase; 146 | 147 | RightsType m_rights; 148 | 149 | template 150 | constexpr bool _getRights() const { 151 | if constexpr (c == Color::WHITE) { 152 | if constexpr (side == CastlingSide::KING_SIDE) { 153 | return m_rights.whiteKingSide; 154 | } else { 155 | return m_rights.whiteQueenSide; 156 | } 157 | } else { 158 | if constexpr (side == CastlingSide::KING_SIDE) { 159 | return m_rights.blackKingSide; 160 | } else { 161 | return m_rights.blackQueenSide; 162 | } 163 | } 164 | } 165 | 166 | constexpr bool _getRights(Color c, CastlingSide side) const { 167 | if (c == Color::WHITE) { 168 | if (side == CastlingSide::KING_SIDE) { 169 | return m_rights.whiteKingSide; 170 | } else { 171 | return m_rights.whiteQueenSide; 172 | } 173 | } else { 174 | if (side == CastlingSide::KING_SIDE) { 175 | return m_rights.blackKingSide; 176 | } else { 177 | return m_rights.blackQueenSide; 178 | } 179 | } 180 | } 181 | 182 | void _setRights(Color c, CastlingSide side, bool value) { 183 | if (c == Color::WHITE) { 184 | if (side == CastlingSide::KING_SIDE) { 185 | m_rights.whiteKingSide = value; 186 | } else { 187 | m_rights.whiteQueenSide = value; 188 | } 189 | } else { 190 | if (side == CastlingSide::KING_SIDE) { 191 | m_rights.blackKingSide = value; 192 | } else { 193 | m_rights.blackQueenSide = value; 194 | } 195 | } 196 | } 197 | }; 198 | 199 | } // namespace chess -------------------------------------------------------------------------------- /src/chess/fens.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace chess { 6 | // Some FEN strings for testing 7 | namespace FENS { 8 | static constexpr std::string_view STARTPOS = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; 9 | static constexpr std::string_view KIWIPETE = "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1"; 10 | } // namespace FENS 11 | } // namespace chess -------------------------------------------------------------------------------- /src/chess/mailbox.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bitboards.hpp" 4 | #include "square.hpp" 5 | #include "types.hpp" 6 | 7 | #include 8 | 9 | namespace chess { 10 | 11 | // A simple mailbox of 64 squares containing a piece 12 | class Mailbox64 { 13 | public: 14 | Mailbox64() = default; 15 | 16 | Piece get(Square sq) const { 17 | return m_squares[sq]; 18 | } 19 | 20 | void set(Piece piece, Square sq) { 21 | m_squares[sq] = piece; 22 | } 23 | 24 | void move(Piece piece, Square from, Square to) { 25 | m_squares[to] = piece; 26 | m_squares[from] = Piece::NONE; 27 | } 28 | 29 | void clear(Square sq) { 30 | m_squares[sq] = Piece::NONE; 31 | } 32 | 33 | void clear() { 34 | m_squares.fill(Piece::NONE); 35 | } 36 | 37 | inline std::string toString() const { 38 | std::string str; 39 | for (Rank r = Rank::RANK_8; r >= Rank::RANK_1; r--) { 40 | for (File f = File::FILE_A; f <= File::FILE_H; f++) { 41 | Square sq = Square(f, r); 42 | Piece piece = get(sq); 43 | str += ' '; 44 | str += pieceToChar(piece); 45 | } 46 | str += '\n'; 47 | } 48 | 49 | return str; 50 | } 51 | 52 | private: 53 | std::array m_squares; 54 | }; 55 | 56 | }; // namespace chess -------------------------------------------------------------------------------- /src/chess/movegencountonly.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "board.hpp" 4 | #include "movegen.hpp" 5 | 6 | namespace chess { 7 | class MoveGenCountOnly : private MoveGen { 8 | private: 9 | template 10 | static inline void enumerateMoves(Bitboard mask, int& count, Function function) { 11 | BitboardIterator(mask) { 12 | const Square from = mask.poplsb(); 13 | auto moves = function(from); 14 | count += moves.popcount(); 15 | } 16 | } 17 | 18 | template 19 | static inline void generateEnpassantMoves(const Board& board, Square ep, Bitboard ep_bb, Bitboard all, Bitboard pinD, 20 | int& count) { 21 | if constexpr (double_ep_possible) { 22 | BitboardIterator(ep_bb) { 23 | Square from = ep_bb.poplsb(); 24 | if (!(pinD & Bitboard(from)) && !(pinD & Bitboard(ep))) { 25 | count++; 26 | } 27 | } 28 | } else { 29 | constexpr Direction down = relativeDirection(); 30 | const Square ep_pawn = ep + down; 31 | const Square from = ep_bb.lsb(); 32 | 33 | const Square kingSq = board.kingSq(); 34 | const Bitboard rook_queen = board.bitboard<~c, PieceType::ROOK>() | board.bitboard<~c, PieceType::QUEEN>(); 35 | 36 | const bool possible_pin = (board.bitboard() & Bitboard(ep_pawn.rank())) && rook_queen; 37 | 38 | const Bitboard connectingPawns = Bitboard(ep_pawn) | Bitboard(from); 39 | 40 | if (possible_pin && !(Attacks::rookAttacks(kingSq, all & ~connectingPawns) & rook_queen).empty()) { 41 | return; 42 | } 43 | 44 | count++; 45 | } 46 | } 47 | 48 | template 49 | static constexpr inline void enumerateMoves(Bitboard moves, int& count) { 50 | if constexpr (pmt == PawnMoveType::Promotion || pmt == PawnMoveType::LeftPromotion || 51 | pmt == PawnMoveType::RightPromotion) { 52 | count += moves.popcount() * 4; 53 | } else { 54 | count += moves.popcount(); 55 | } 56 | } 57 | 58 | template 59 | static inline void generatePawnMoves(const Board& board, Bitboard them, Bitboard all, Bitboard pinD, Bitboard pinHV, 60 | Bitboard checkmask, int& count) { 61 | constexpr Bitboard rank_before_promo = Bitboard(Square::relativeRank()); 62 | constexpr Direction down = relativeDirection(); 63 | const Bitboard pawns = board.bitboard(); 64 | const bool promo_possible = !(pawns & rank_before_promo).empty(); 65 | 66 | if constexpr (mt != MoveGenType::CAPTURE) { 67 | const Bitboard moveable_square = ~all & checkmask; 68 | 69 | Bitboard pinned_pushes = PawnMovesHandler::template pawnPinMaskHV(pawns, all, pinD, pinHV); 70 | Bitboard unpinned_pushes = PawnMovesHandler::template pawnPinMaskHV(pawns, all, pinD, pinHV); 71 | Bitboard legal_push = PawnMovesHandler::legalPawnPush(pinned_pushes, unpinned_pushes, moveable_square); 72 | Bitboard double_push = PawnMovesHandler::doublePushes(pinned_pushes, unpinned_pushes, moveable_square); 73 | Bitboard single_push = PawnMovesHandler::singlePushes(legal_push); 74 | 75 | if constexpr (mt != MoveGenType::QUIET) { 76 | Bitboard promotions = PawnMovesHandler::promotionPushes(legal_push, promo_possible); 77 | enumerateMoves(promotions, count); 78 | } 79 | 80 | enumerateMoves(single_push, count); 81 | enumerateMoves(double_push, count); 82 | } 83 | 84 | if constexpr (mt != MoveGenType::QUIET) { 85 | const Bitboard moveable_square = them & checkmask; 86 | const Bitboard pawns_d = pawns & ~pinHV; 87 | const Bitboard pinned_pawns_d = PawnMovesHandler::template pawnPinMaskD(pawns_d, pinD); 88 | const Bitboard unpinned_pawns_d = PawnMovesHandler::template pawnPinMaskD(pawns_d, pinD); 89 | 90 | Bitboard legal_left = PawnMovesHandler::template legalCaptures( 91 | pinned_pawns_d, unpinned_pawns_d, pinD, moveable_square); 92 | Bitboard legal_right = PawnMovesHandler::template legalCaptures( 93 | pinned_pawns_d, unpinned_pawns_d, pinD, moveable_square); 94 | 95 | Bitboard promotions_left = PawnMovesHandler::promotionCaptures(legal_left, promo_possible); 96 | Bitboard promotions_right = PawnMovesHandler::promotionCaptures(legal_right, promo_possible); 97 | Bitboard left_attacks = PawnMovesHandler::captures(legal_left); 98 | Bitboard right_attacks = PawnMovesHandler::captures(legal_right); 99 | 100 | enumerateMoves(promotions_left, count); 101 | enumerateMoves(promotions_right, count); 102 | enumerateMoves(left_attacks, count); 103 | enumerateMoves(right_attacks, count); 104 | 105 | Square ep = board.enPassant(); 106 | 107 | if (!ep.isValid()) { 108 | return; 109 | } 110 | 111 | if (((Bitboard(ep) | Bitboard(Square(ep + down))) & checkmask).empty()) { 112 | return; 113 | } 114 | 115 | Bitboard ep_bb = Attacks::pawnAttacks<~c>(ep) & pawns_d; 116 | 117 | if (ep_bb.single()) { 118 | generateEnpassantMoves(board, ep, ep_bb, all, pinD, count); 119 | } else if (ep_bb) { 120 | generateEnpassantMoves(board, ep, ep_bb, all, pinD, count); 121 | } 122 | } 123 | } 124 | 125 | template 126 | static inline void generateKingMoves(const Board& board, Bitboard moveable_squares, Bitboard seen, Bitboard all, 127 | Bitboard pinHV, int& count) { 128 | Square kingSq = board.kingSq(); 129 | Bitboard king = Bitboard(kingSq); 130 | 131 | enumerateMoves(king, count, [&](Square sq) { return Attacks::kingAttacks(sq) & moveable_squares & ~seen; }); 132 | 133 | if constexpr (mt != MoveGenType::CAPTURE) { 134 | if (seen & king) { 135 | return; 136 | } 137 | count += !generateCastlingMove(board, kingSq, seen, all, pinHV).empty(); 138 | count += !generateCastlingMove(board, kingSq, seen, all, pinHV).empty(); 139 | } 140 | } 141 | 142 | template 143 | static inline void generateLegalMoves(const Board& board, int& count) { 144 | Bitboard us = board.us(); 145 | Bitboard them = board.them(); 146 | Bitboard enemy_or_empty = ~us; 147 | Bitboard all = board.occupied(); 148 | Bitboard moveable_squares = enemy_or_empty; 149 | 150 | if constexpr (mt == MoveGenType::CAPTURE) { 151 | moveable_squares = them; 152 | } else if constexpr (mt == MoveGenType::QUIET) { 153 | moveable_squares = ~all; 154 | } 155 | 156 | Square kingSq = board.kingSq(); 157 | 158 | Bitboard leaper_checkers; 159 | Bitboard slider_checkers; 160 | 161 | int check_count = generateCheckers(board, kingSq, leaper_checkers, slider_checkers); 162 | 163 | Bitboard checkmask = default_checkmask; 164 | 165 | if (check_count) { 166 | checkmask = generateCheckMask(kingSq, check_count, leaper_checkers, slider_checkers); 167 | } 168 | 169 | Bitboard pinD = pinMaskDiagonal(board, kingSq, them, us); 170 | Bitboard pinHV = pinMaskHorizontalVertical(board, kingSq, them, us); 171 | Bitboard seen = generateSeenSquares<~c>(board, all, enemy_or_empty); 172 | 173 | generateKingMoves(board, moveable_squares, seen, all, pinHV, count); 174 | 175 | moveable_squares &= checkmask; 176 | 177 | moveable_squares = moveable_squares * !(check_count == 2); 178 | 179 | if (moveable_squares.empty()) { 180 | return; 181 | } 182 | 183 | generatePawnMoves(board, them, all, pinD, pinHV, checkmask, count); 184 | 185 | Bitboard knights = board.bitboard() & ~(pinD | pinHV); 186 | 187 | enumerateMoves(knights, count, [&](Square sq) { return Attacks::knightAttacks(sq) & moveable_squares; }); 188 | 189 | Bitboard bishops = board.bitboard() & ~pinHV; 190 | 191 | // clang-format off 192 | enumerateMoves(bishops,count, 193 | [&](Square sq) { 194 | const bool is_pinned = pinD & Bitboard(sq); 195 | 196 | Bitboard attack = Attacks::bishopAttacks(sq, all); 197 | 198 | if (is_pinned){ 199 | attack &= pinD; 200 | } 201 | 202 | return attack & moveable_squares; 203 | }); 204 | // clang-format on 205 | 206 | Bitboard rooks = board.bitboard() & ~pinD; 207 | 208 | // clang-format off 209 | enumerateMoves(rooks,count, 210 | [&](Square sq) { 211 | const bool is_pinned = pinHV & Bitboard(sq); 212 | 213 | Bitboard attack = Attacks::rookAttacks(sq, all); 214 | 215 | if (is_pinned){ 216 | attack &= pinHV; 217 | } 218 | 219 | return attack & moveable_squares; 220 | }); 221 | // clang-format on 222 | 223 | Bitboard queens = board.bitboard() & ~(pinD & pinHV); 224 | 225 | // clang-format off 226 | enumerateMoves(queens, count, 227 | [&](Square sq) { 228 | const bool pinned_hv = pinHV & Bitboard(sq); 229 | const bool pinned_d = pinD & Bitboard(sq); 230 | 231 | Bitboard attacks_d = Attacks::bishopAttacks(sq, all); 232 | Bitboard attacks_hv = Attacks::rookAttacks(sq, all); 233 | Bitboard attacks = attacks_d | attacks_hv; 234 | 235 | if (pinned_hv){ 236 | attacks &= attacks_hv & pinHV; 237 | } else if (pinned_d) { 238 | attacks &= attacks_d & pinD; 239 | } 240 | 241 | return attacks & moveable_squares; 242 | }); 243 | // clang-format on 244 | } 245 | 246 | public: 247 | template 248 | static inline int legalmoves(const Board& board) { 249 | int count = 0; 250 | if (board.sideToMove() == Color::WHITE) { 251 | generateLegalMoves(board, count); 252 | } else { 253 | generateLegalMoves(board, count); 254 | } 255 | return count; 256 | } 257 | }; 258 | } // namespace chess -------------------------------------------------------------------------------- /src/chess/moves.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "square.hpp" 4 | #include "types.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace chess { 12 | 13 | enum class MoveType : uint16_t { NORMAL = 0, ENPASSANT, CASTLING, PROMOTION }; 14 | 15 | struct Move { 16 | private: 17 | uint16_t m_data; 18 | int m_score; 19 | 20 | template 21 | static inline Move _make(Square from, Square to, PieceType promoted = PieceType::KNIGHT) { 22 | if constexpr (mt != MoveType::PROMOTION) { 23 | return Move((static_cast(from) << 10) | (static_cast(to) << 4) | 24 | (static_cast(mt))); 25 | } else { 26 | return Move((static_cast(from) << 10) | (static_cast(to) << 4) | 27 | (_encodePieceType(promoted) << 2) | (static_cast(mt))); 28 | } 29 | } 30 | 31 | static uint8_t _encodePieceType(PieceType pt) { 32 | assert(static_cast(pt) >= static_cast(PieceType::KNIGHT) && 33 | static_cast(pt) <= static_cast(PieceType::QUEEN)); 34 | return static_cast(pt) - 1; 35 | } 36 | 37 | static PieceType _decodePieceType(uint8_t pt) { 38 | assert(pt >= 0 && pt <= 3); 39 | return static_cast(pt + 1); 40 | } 41 | 42 | public: 43 | Move() = default; 44 | Move(uint16_t data) : m_data(data), m_score(0) { 45 | } 46 | 47 | static inline Move none() { 48 | return Move(0); 49 | } 50 | 51 | static inline Move nullmove() { 52 | return _make(63, 63); 53 | } 54 | 55 | static inline Move makeNormal(Square from, Square to) { 56 | return _make(from, to); 57 | } 58 | 59 | static inline Move makeEnpassant(Square from, Square to) { 60 | return _make(from, to); 61 | } 62 | 63 | static inline Move makeCastling(Square from, Square to) { 64 | return _make(from, to); 65 | } 66 | 67 | static inline Move makePromotion(Square from, Square to, PieceType promoted) { 68 | return _make(from, to, promoted); 69 | } 70 | 71 | Square from() const { 72 | return static_cast(m_data >> 10); 73 | } 74 | 75 | Square to() const { 76 | return static_cast((m_data >> 4) & 0x3f); 77 | } 78 | 79 | MoveType type() const { 80 | return static_cast(m_data & 0x3); 81 | } 82 | 83 | PieceType promoted() const { 84 | return _decodePieceType((m_data >> 2) & 0x3); 85 | } 86 | 87 | bool isPromotion() const { 88 | return type() == MoveType::PROMOTION; 89 | } 90 | 91 | bool isEnPassant() const { 92 | return type() == MoveType::ENPASSANT; 93 | } 94 | 95 | bool isCastling() const { 96 | return type() == MoveType::CASTLING; 97 | } 98 | 99 | auto move() const { 100 | return m_data; 101 | } 102 | 103 | bool isValid() const { 104 | return !(from() == to()); 105 | } 106 | 107 | void setScore(int s) { 108 | m_score = s; 109 | } 110 | 111 | auto score() const { 112 | return m_score; 113 | } 114 | 115 | auto data() const { 116 | return m_data; 117 | } 118 | 119 | bool operator==(const Move& rhs) const { 120 | return m_data == rhs.m_data; 121 | } 122 | }; 123 | 124 | inline std::ostream& operator<<(std::ostream& os, const Move& m) { 125 | os << m.from(); 126 | if (m.type() == MoveType::CASTLING) { 127 | os << (Square(m.to() > m.from() ? File::FILE_G : File::FILE_C, m.from().rank())); 128 | } else { 129 | os << m.to(); 130 | } 131 | 132 | if (m.isPromotion()) { 133 | os << pieceTypeToChar(m.promoted()); 134 | } 135 | 136 | return os; 137 | } 138 | 139 | struct Movelist { 140 | public: 141 | Movelist() = default; 142 | 143 | Movelist(std::initializer_list moves) : m_size(moves.size()) { 144 | assert(moves.size() <= MAX_SIZE); 145 | std::copy(moves.begin(), moves.end(), m_moves.begin()); 146 | } 147 | 148 | auto find(const Move& m) const { 149 | for (int i = 0; i < m_size; ++i) { 150 | if (m_moves[i] == m) { 151 | return i; 152 | } 153 | } 154 | 155 | return -1; 156 | } 157 | 158 | void add(Move m) { 159 | assert(m_size < MAX_SIZE); 160 | m_moves[m_size++] = m; 161 | } 162 | 163 | void addIf(Move m, bool condition) { 164 | assert(m_size < MAX_SIZE); 165 | if (!condition) { 166 | return; 167 | } 168 | m_moves[m_size++] = m; 169 | } 170 | 171 | void addPromotions(Square from, Square to) { 172 | add(Move::makePromotion(from, to, PieceType::KNIGHT)); 173 | add(Move::makePromotion(from, to, PieceType::BISHOP)); 174 | add(Move::makePromotion(from, to, PieceType::ROOK)); 175 | add(Move::makePromotion(from, to, PieceType::QUEEN)); 176 | } 177 | 178 | void clear() { 179 | m_size = 0; 180 | } 181 | 182 | void setSize(int size) { 183 | m_size = size; 184 | } 185 | 186 | int size() const { 187 | return m_size; 188 | } 189 | 190 | Move operator[](int i) const { 191 | assert(i >= 0 && i < m_size); 192 | return m_moves[i]; 193 | } 194 | 195 | Move& operator[](int i) { 196 | assert(i >= 0 && i < m_size); 197 | return m_moves[i]; 198 | } 199 | 200 | Move& front() { 201 | assert(m_size > 0); 202 | return m_moves[0]; 203 | } 204 | 205 | Move& back() { 206 | assert(m_size > 0); 207 | return m_moves[m_size - 1]; 208 | } 209 | 210 | Move front() const { 211 | assert(m_size > 0); 212 | return m_moves[0]; 213 | } 214 | 215 | Move back() const { 216 | assert(m_size > 0); 217 | return m_moves[m_size - 1]; 218 | } 219 | 220 | bool empty() const { 221 | return m_size == 0; 222 | } 223 | 224 | void pop_back() { 225 | assert(m_size > 0); 226 | --m_size; 227 | } 228 | 229 | void nextmove(const int move_index) { 230 | Move temp; 231 | 232 | int i = 0; 233 | int bestscore = INT32_MIN; 234 | 235 | for (int j = move_index; j < m_size; ++j) { 236 | if (m_moves[j].score() > bestscore) { 237 | bestscore = m_moves[j].score(); 238 | i = j; 239 | } 240 | } 241 | 242 | temp = m_moves[move_index]; 243 | m_moves[move_index] = m_moves[i]; 244 | m_moves[i] = temp; 245 | } 246 | 247 | using iterator = Move*; 248 | using const_iterator = const Move*; 249 | 250 | constexpr iterator begin() { 251 | return m_moves.data(); 252 | } 253 | 254 | constexpr iterator end() { 255 | return m_moves.data() + m_size; 256 | } 257 | 258 | constexpr const_iterator begin() const { 259 | return m_moves.data(); 260 | } 261 | 262 | constexpr const_iterator end() const { 263 | return m_moves.data() + m_size; 264 | } 265 | 266 | static constexpr int MAX_SIZE = 128; 267 | 268 | private: 269 | int m_size = 0; 270 | std::array m_moves{}; 271 | }; 272 | 273 | inline std::ostream& operator<<(std::ostream& os, const Movelist& ml) { 274 | for (int i = 0; i < ml.size(); ++i) { 275 | os << ml[i] << ": 1\n"; 276 | } 277 | 278 | os << "Total moves: " << ml.size() << "\n"; 279 | 280 | return os; 281 | } 282 | 283 | } // namespace chess 284 | -------------------------------------------------------------------------------- /src/chess/square.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.hpp" 4 | #include 5 | #include 6 | 7 | namespace chess { 8 | 9 | // clang-format off 10 | enum class Direction : int8_t { 11 | NORTH = 8, 12 | WEST = -1, 13 | SOUTH = -8, 14 | EAST = 1, 15 | NORTH_EAST = 9, 16 | NORTH_WEST = 7, 17 | SOUTH_WEST = -9, 18 | SOUTH_EAST = -7 19 | }; 20 | 21 | constexpr Direction relativeDirection(Color c, Direction d) { 22 | if (c == Color::WHITE) { 23 | return d; 24 | } else { 25 | return static_cast(-static_cast(d)); 26 | } 27 | } 28 | 29 | template 30 | constexpr Direction relativeDirection() { 31 | if constexpr (c == Color::WHITE) { 32 | return d; 33 | } else { 34 | return static_cast(-static_cast(d)); 35 | } 36 | } 37 | // clang-format on 38 | 39 | enum class File { FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, NO_FILE }; 40 | 41 | enum class Rank { RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, NO_RANK }; 42 | 43 | // Pre-increment overloads 44 | constexpr File& operator++(File& f) { 45 | return f = static_cast(static_cast(f) + 1); 46 | } 47 | 48 | constexpr Rank& operator++(Rank& r) { 49 | return r = static_cast(static_cast(r) + 1); 50 | } 51 | 52 | // Pre-decrement overloads 53 | constexpr File& operator--(File& f) { 54 | return f = static_cast(static_cast(f) - 1); 55 | } 56 | 57 | constexpr Rank& operator--(Rank& r) { 58 | return r = static_cast(static_cast(r) - 1); 59 | } 60 | 61 | // Post-increment overloads 62 | constexpr File operator++(File& f, int) { 63 | File old = f; 64 | ++f; 65 | return old; 66 | } 67 | 68 | constexpr Rank operator++(Rank& r, int) { 69 | Rank old = r; 70 | ++r; 71 | return old; 72 | } 73 | 74 | // Post-decremnt overloads 75 | constexpr File operator--(File& f, int) { 76 | File old = f; 77 | --f; 78 | return old; 79 | } 80 | 81 | constexpr Rank operator--(Rank& r, int) { 82 | Rank old = r; 83 | --r; 84 | return old; 85 | } 86 | 87 | // Addition overloads 88 | constexpr File operator+(File f, int i) { 89 | return static_cast(static_cast(f) + i); 90 | } 91 | 92 | constexpr Rank operator+(Rank r, int i) { 93 | return static_cast(static_cast(r) + i); 94 | } 95 | 96 | // Substraction overloads 97 | constexpr File operator-(File f, int i) { 98 | return static_cast(static_cast(f) - i); 99 | } 100 | 101 | constexpr Rank operator-(Rank r, int i) { 102 | return static_cast(static_cast(r) - i); 103 | } 104 | 105 | // clang-format off 106 | static constexpr U64 MASK_RANK[8] = { 107 | 0xff, 0xff00, 0xff0000, 0xff000000, 0xff00000000, 0xff0000000000, 0xff000000000000, 0xff00000000000000 108 | }; 109 | 110 | static constexpr U64 MASK_FILE[8] = { 111 | 0x101010101010101, 0x202020202020202, 0x404040404040404, 0x808080808080808, 0x1010101010101010, 0x2020202020202020, 0x4040404040404040, 0x8080808080808080, 112 | }; 113 | 114 | // clang-format on 115 | 116 | // clang-format off 117 | static constexpr inline Rank squareToRank[65] = { 118 | Rank::RANK_1, Rank::RANK_1, Rank::RANK_1, Rank::RANK_1, Rank::RANK_1, Rank::RANK_1, Rank::RANK_1, Rank::RANK_1, 119 | Rank::RANK_2, Rank::RANK_2, Rank::RANK_2, Rank::RANK_2, Rank::RANK_2, Rank::RANK_2, Rank::RANK_2, Rank::RANK_2, 120 | Rank::RANK_3, Rank::RANK_3, Rank::RANK_3, Rank::RANK_3, Rank::RANK_3, Rank::RANK_3, Rank::RANK_3, Rank::RANK_3, 121 | Rank::RANK_4, Rank::RANK_4, Rank::RANK_4, Rank::RANK_4, Rank::RANK_4, Rank::RANK_4, Rank::RANK_4, Rank::RANK_4, 122 | Rank::RANK_5, Rank::RANK_5, Rank::RANK_5, Rank::RANK_5, Rank::RANK_5, Rank::RANK_5, Rank::RANK_5, Rank::RANK_5, 123 | Rank::RANK_6, Rank::RANK_6, Rank::RANK_6, Rank::RANK_6, Rank::RANK_6, Rank::RANK_6, Rank::RANK_6, Rank::RANK_6, 124 | Rank::RANK_7, Rank::RANK_7, Rank::RANK_7, Rank::RANK_7, Rank::RANK_7, Rank::RANK_7, Rank::RANK_7, Rank::RANK_7, 125 | Rank::RANK_8, Rank::RANK_8, Rank::RANK_8, Rank::RANK_8, Rank::RANK_8, Rank::RANK_8, Rank::RANK_8, Rank::RANK_8, Rank::NO_RANK 126 | }; 127 | 128 | static constexpr inline File squareToFile[65] = { 129 | File::FILE_A, File::FILE_B, File::FILE_C, File::FILE_D, File::FILE_E, File::FILE_F, File::FILE_G, File::FILE_H, 130 | File::FILE_A, File::FILE_B, File::FILE_C, File::FILE_D, File::FILE_E, File::FILE_F, File::FILE_G, File::FILE_H, 131 | File::FILE_A, File::FILE_B, File::FILE_C, File::FILE_D, File::FILE_E, File::FILE_F, File::FILE_G, File::FILE_H, 132 | File::FILE_A, File::FILE_B, File::FILE_C, File::FILE_D, File::FILE_E, File::FILE_F, File::FILE_G, File::FILE_H, 133 | File::FILE_A, File::FILE_B, File::FILE_C, File::FILE_D, File::FILE_E, File::FILE_F, File::FILE_G, File::FILE_H, 134 | File::FILE_A, File::FILE_B, File::FILE_C, File::FILE_D, File::FILE_E, File::FILE_F, File::FILE_G, File::FILE_H, 135 | File::FILE_A, File::FILE_B, File::FILE_C, File::FILE_D, File::FILE_E, File::FILE_F, File::FILE_G, File::FILE_H, 136 | File::FILE_A, File::FILE_B, File::FILE_C, File::FILE_D, File::FILE_E, File::FILE_F, File::FILE_G, File::FILE_H, File::NO_FILE 137 | }; 138 | 139 | // clang-format on 140 | 141 | static constexpr auto generateSquareDistanceMap() { 142 | std::array, 64> arr; 143 | 144 | for (int x = 0; x < 64; x++) { 145 | for (int y = 0; y < 64; y++) { 146 | int xr = static_cast(squareToRank[x]); 147 | int yr = static_cast(squareToRank[y]); 148 | int xf = static_cast(squareToFile[x]); 149 | int yf = static_cast(squareToFile[y]); 150 | 151 | int dx = (xf > yf) ? (xf - yf) : (yf - xf); 152 | int dy = (xr > yr) ? (xr - yr) : (yr - xr); 153 | 154 | arr[x][y] = std::max(dx, dy); 155 | } 156 | } 157 | 158 | return arr; 159 | } 160 | 161 | static constexpr std::array, 64> squareDistanceMap = generateSquareDistanceMap(); 162 | 163 | class Square { 164 | private: 165 | uint8_t m_sq; 166 | 167 | // clang-format off 168 | static constexpr inline std::string_view squareToString[65] = { 169 | "a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1", 170 | "a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2", 171 | "a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3", 172 | "a4", "b4", "c4", "d4", "e4", "f4", "g4", "h4", 173 | "a5", "b5", "c5", "d5", "e5", "f5", "g5", "h5", 174 | "a6", "b6", "c6", "d6", "e6", "f6", "g6", "h6", 175 | "a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7", 176 | "a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8", 177 | "no_sq", 178 | }; 179 | // clang-format on 180 | 181 | public: 182 | // clang-format off 183 | enum : uint8_t { 184 | A1, B1, C1, D1, E1, F1, G1, H1, 185 | A2, B2, C2, D2, E2, F2, G2, H2, 186 | A3, B3, C3, D3, E3, F3, G3, H3, 187 | A4, B4, C4, D4, E4, F4, G4, H4, 188 | A5, B5, C5, D5, E5, F5, G5, H5, 189 | A6, B6, C6, D6, E6, F6, G6, H6, 190 | A7, B7, C7, D7, E7, F7, G7, H7, 191 | A8, B8, C8, D8, E8, F8, G8, H8, 192 | NO_SQ 193 | }; 194 | // clang-format on 195 | 196 | constexpr Square() : m_sq(NO_SQ) { 197 | } 198 | 199 | constexpr Square(uint8_t sq) : m_sq(sq) { 200 | } 201 | 202 | constexpr Square(int file, int rank) : m_sq(rank * 8 + file) { 203 | } 204 | 205 | constexpr Square(File file, Rank rank) : m_sq(static_cast(rank) * 8 + static_cast(file)) { 206 | } 207 | 208 | constexpr Square(std::string_view str) : m_sq((str[0] - 'a') + (str[1] - '1') * 8) { 209 | } 210 | 211 | constexpr inline int sq() const { 212 | return m_sq; 213 | } 214 | constexpr inline U64 bb() const { 215 | return 1ULL << m_sq; 216 | } 217 | 218 | constexpr inline File file() const { 219 | return squareToFile[m_sq]; 220 | } 221 | 222 | constexpr inline Rank rank() const { 223 | return squareToRank[m_sq]; 224 | } 225 | 226 | constexpr uint8_t index() const { 227 | return m_sq; 228 | } 229 | 230 | constexpr int diagonal() const { 231 | return 7 + int(file()) - int(rank()); 232 | } 233 | 234 | constexpr int antiDiagonal() const { 235 | return int(file()) + int(rank()); 236 | } 237 | 238 | constexpr bool isValid() const { 239 | return m_sq != NO_SQ; 240 | } 241 | 242 | constexpr bool isLight() const { 243 | return (static_cast(file()) + static_cast(rank())) % 2 == 0; 244 | } 245 | 246 | constexpr bool isDark() const { 247 | return !isLight(); 248 | } 249 | 250 | constexpr inline std::string_view toString() const { 251 | return squareToString[m_sq]; 252 | } 253 | 254 | constexpr inline bool operator==(const Square& rhs) const { 255 | return m_sq == rhs.m_sq; 256 | } 257 | 258 | constexpr inline bool operator!=(const Square& rhs) const { 259 | return m_sq != rhs.m_sq; 260 | } 261 | 262 | constexpr inline operator int() const { 263 | return sq(); 264 | } 265 | 266 | constexpr Square operator+(Direction d) const { 267 | return m_sq + static_cast(d); 268 | } 269 | 270 | constexpr Square operator-(Direction d) const { 271 | return m_sq - static_cast(d); 272 | } 273 | 274 | template 275 | static constexpr inline Rank relativeRank(Rank r) { 276 | if constexpr (c == Color::WHITE) { 277 | return r; 278 | } else { 279 | return static_cast(static_cast(r) ^ 7); 280 | } 281 | } 282 | 283 | template 284 | static constexpr inline File relativeFile(File f) { 285 | if constexpr (c == Color::WHITE) { 286 | return f; 287 | } else { 288 | return static_cast(static_cast(f) ^ 7); 289 | } 290 | } 291 | 292 | template 293 | static constexpr inline Rank relativeRank() { 294 | if constexpr (c == Color::WHITE) { 295 | return r; 296 | } else { 297 | return static_cast(static_cast(r) ^ 7); 298 | } 299 | } 300 | 301 | static constexpr inline Rank relativeRank(Color c, Rank r) { 302 | return static_cast(static_cast(r) ^ (static_cast(c) * 7)); 303 | } 304 | 305 | template 306 | static constexpr inline File relativeFile() { 307 | if constexpr (c == Color::WHITE) { 308 | return f; 309 | } else { 310 | return static_cast(static_cast(f) ^ 7); 311 | } 312 | } 313 | 314 | static constexpr inline int squareDistance(Square x, Square y) { 315 | return squareDistanceMap[x][y]; 316 | } 317 | 318 | static constexpr inline Square relativeSquare(Square sq, Color c) { 319 | return static_cast(int(sq) ^ (static_cast(c) * 56)); 320 | } 321 | 322 | template 323 | static constexpr inline Square relativeSquare(Square sq) { 324 | return static_cast(int(sq) ^ (static_cast(c) * 56)); 325 | } 326 | 327 | template 328 | static constexpr inline Square relativeSquare() { 329 | if constexpr (c == Color::WHITE) { 330 | return static_cast(sq); 331 | } else { 332 | return static_cast(sq ^ 56); 333 | } 334 | } 335 | 336 | static constexpr inline bool isOurBackRank(Square sq, Color c) { 337 | return sq.rank() == static_cast(static_cast(c) * 7); 338 | } 339 | 340 | static constexpr inline bool isTheirBackRank(Square sq, Color c) { 341 | return sq.rank() == relativeRank(~c, Rank::RANK_1); 342 | } 343 | }; 344 | 345 | // Used to iterate through all squares 346 | class SquareIterator { 347 | public: 348 | static constexpr std::array SQUARES = { 349 | Square::A1, Square::B1, Square::C1, Square::D1, Square::E1, Square::F1, Square::G1, Square::H1, 350 | Square::A2, Square::B2, Square::C2, Square::D2, Square::E2, Square::F2, Square::G2, Square::H2, 351 | Square::A3, Square::B3, Square::C3, Square::D3, Square::E3, Square::F3, Square::G3, Square::H3, 352 | Square::A4, Square::B4, Square::C4, Square::D4, Square::E4, Square::F4, Square::G4, Square::H4, 353 | Square::A5, Square::B5, Square::C5, Square::D5, Square::E5, Square::F5, Square::G5, Square::H5, 354 | Square::A6, Square::B6, Square::C6, Square::D6, Square::E6, Square::F6, Square::G6, Square::H6, 355 | Square::A7, Square::B7, Square::C7, Square::D7, Square::E7, Square::F7, Square::G7, Square::H7, 356 | Square::A8, Square::B8, Square::C8, Square::D8, Square::E8, Square::F8, Square::G8, Square::H8, 357 | }; 358 | 359 | SquareIterator() = default; 360 | 361 | static constexpr auto begin() { 362 | return std::begin(SQUARES); 363 | } 364 | 365 | static constexpr auto end() { 366 | return std::end(SQUARES); 367 | } 368 | 369 | static constexpr auto size() { 370 | return std::size(SQUARES); 371 | } 372 | }; 373 | 374 | inline std::ostream& operator<<(std::ostream& os, const Square& sq) { 375 | os << sq.toString(); 376 | return os; 377 | } 378 | 379 | } // namespace chess -------------------------------------------------------------------------------- /src/chess/types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace chess { 7 | 8 | using U64 = uint64_t; 9 | 10 | // clang-format off 11 | 12 | enum class PieceType : uint8_t { PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING, NONE }; 13 | 14 | enum class Piece : uint8_t { 15 | WHITEPAWN, 16 | WHITEKNIGHT, 17 | WHITEBISHOP, 18 | WHITEROOK, 19 | WHITEQUEEN, 20 | WHITEKING, 21 | BLACKPAWN, 22 | BLACKKNIGHT, 23 | BLACKBISHOP, 24 | BLACKROOK, 25 | BLACKQUEEN, 26 | BLACKKING, 27 | NONE, 28 | }; 29 | 30 | enum class Color : uint8_t { WHITE, BLACK, NO_COLOR }; 31 | 32 | constexpr Color operator~(Color c) { 33 | return Color(static_cast(c) ^ 1); 34 | } 35 | 36 | constexpr uint8_t NUM_SQUARES = 64; 37 | constexpr uint8_t NUM_COLORS = 2; 38 | constexpr uint8_t NUM_PIECES = 12; 39 | constexpr uint8_t NUM_PIECE_TYPES = 6; 40 | constexpr int MAX_MOVES = 256; 41 | 42 | constexpr Piece charToPiece(char c){ 43 | 44 | if (c == 'P') return Piece::WHITEPAWN; 45 | if (c == 'N') return Piece::WHITEKNIGHT; 46 | if (c == 'B') return Piece::WHITEBISHOP; 47 | if (c == 'R') return Piece::WHITEROOK; 48 | if (c == 'Q') return Piece::WHITEQUEEN; 49 | if (c == 'K') return Piece::WHITEKING; 50 | 51 | if (c == 'p') return Piece::BLACKPAWN; 52 | if (c == 'n') return Piece::BLACKKNIGHT; 53 | if (c == 'b') return Piece::BLACKBISHOP; 54 | if (c == 'r') return Piece::BLACKROOK; 55 | if (c == 'q') return Piece::BLACKQUEEN; 56 | if (c == 'k') return Piece::BLACKKING; 57 | 58 | return Piece::NONE; 59 | } 60 | 61 | constexpr char pieceToChar(Piece p){ 62 | if (p == Piece::WHITEPAWN) return 'P'; 63 | if (p == Piece::WHITEKNIGHT) return 'N'; 64 | if (p == Piece::WHITEBISHOP) return 'B'; 65 | if (p == Piece::WHITEROOK) return 'R'; 66 | if (p == Piece::WHITEQUEEN) return 'Q'; 67 | if (p == Piece::WHITEKING) return 'K'; 68 | 69 | if (p == Piece::BLACKPAWN) return 'p'; 70 | if (p == Piece::BLACKKNIGHT) return 'n'; 71 | if (p == Piece::BLACKBISHOP) return 'b'; 72 | if (p == Piece::BLACKROOK) return 'r'; 73 | if (p == Piece::BLACKQUEEN) return 'q'; 74 | if (p == Piece::BLACKKING) return 'k'; 75 | 76 | return '.'; 77 | } 78 | 79 | constexpr char pieceTypeToChar(PieceType pt){ 80 | if (pt == PieceType::PAWN) return 'P'; 81 | if (pt == PieceType::KNIGHT) return 'N'; 82 | if (pt == PieceType::BISHOP) return 'B'; 83 | if (pt == PieceType::ROOK) return 'R'; 84 | if (pt == PieceType::QUEEN) return 'Q'; 85 | if (pt == PieceType::KING) return 'K'; 86 | 87 | return '.'; 88 | } 89 | 90 | constexpr PieceType charToPieceType(char c){ 91 | if (c == 'P' || c == 'p') return PieceType::PAWN; 92 | if (c == 'N' || c == 'n') return PieceType::KNIGHT; 93 | if (c == 'B' || c == 'b') return PieceType::BISHOP; 94 | if (c == 'R' || c == 'r') return PieceType::ROOK; 95 | if (c == 'Q' || c == 'q') return PieceType::QUEEN; 96 | if (c == 'K' || c == 'k') return PieceType::KING; 97 | 98 | return PieceType::NONE; 99 | } 100 | 101 | constexpr PieceType pieceToPieceType(Piece p) { 102 | return static_cast(static_cast(p) % 6); 103 | } 104 | 105 | constexpr Color pieceToColor(Piece p) { 106 | return static_cast(static_cast(p) / 6); 107 | } 108 | 109 | constexpr Piece makePiece(Color c, PieceType pt) { 110 | return static_cast(static_cast(c) * 6 + static_cast(pt)); 111 | } 112 | 113 | template 114 | constexpr Piece makePiece() { 115 | return static_cast(static_cast(c) * 6 + static_cast(pt)); 116 | } 117 | 118 | // clang-format on 119 | 120 | } // namespace chess -------------------------------------------------------------------------------- /src/chess/zobrist.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "castling.hpp" 4 | #include "types.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace chess { 11 | class Zobrist { 12 | private: 13 | // clang-format off 14 | static constexpr inline std::array RandomArray = { 15 | 0x9D39247E33776D41, 0x2AF7398005AAA5C7, 0x44DB015024623547, 0x9C15F73E62A76AE2, 16 | 0x75834465489C0C89, 0x3290AC3A203001BF, 0x0FBBAD1F61042279, 0xE83A908FF2FB60CA, 17 | 0x0D7E765D58755C10, 0x1A083822CEAFE02D, 0x9605D5F0E25EC3B0, 0xD021FF5CD13A2ED5, 18 | 0x40BDF15D4A672E32, 0x011355146FD56395, 0x5DB4832046F3D9E5, 0x239F8B2D7FF719CC, 19 | 0x05D1A1AE85B49AA1, 0x679F848F6E8FC971, 0x7449BBFF801FED0B, 0x7D11CDB1C3B7ADF0, 20 | 0x82C7709E781EB7CC, 0xF3218F1C9510786C, 0x331478F3AF51BBE6, 0x4BB38DE5E7219443, 21 | 0xAA649C6EBCFD50FC, 0x8DBD98A352AFD40B, 0x87D2074B81D79217, 0x19F3C751D3E92AE1, 22 | 0xB4AB30F062B19ABF, 0x7B0500AC42047AC4, 0xC9452CA81A09D85D, 0x24AA6C514DA27500, 23 | 0x4C9F34427501B447, 0x14A68FD73C910841, 0xA71B9B83461CBD93, 0x03488B95B0F1850F, 24 | 0x637B2B34FF93C040, 0x09D1BC9A3DD90A94, 0x3575668334A1DD3B, 0x735E2B97A4C45A23, 25 | 0x18727070F1BD400B, 0x1FCBACD259BF02E7, 0xD310A7C2CE9B6555, 0xBF983FE0FE5D8244, 26 | 0x9F74D14F7454A824, 0x51EBDC4AB9BA3035, 0x5C82C505DB9AB0FA, 0xFCF7FE8A3430B241, 27 | 0x3253A729B9BA3DDE, 0x8C74C368081B3075, 0xB9BC6C87167C33E7, 0x7EF48F2B83024E20, 28 | 0x11D505D4C351BD7F, 0x6568FCA92C76A243, 0x4DE0B0F40F32A7B8, 0x96D693460CC37E5D, 29 | 0x42E240CB63689F2F, 0x6D2BDCDAE2919661, 0x42880B0236E4D951, 0x5F0F4A5898171BB6, 30 | 0x39F890F579F92F88, 0x93C5B5F47356388B, 0x63DC359D8D231B78, 0xEC16CA8AEA98AD76, 31 | 0x5355F900C2A82DC7, 0x07FB9F855A997142, 0x5093417AA8A7ED5E, 0x7BCBC38DA25A7F3C, 32 | 0x19FC8A768CF4B6D4, 0x637A7780DECFC0D9, 0x8249A47AEE0E41F7, 0x79AD695501E7D1E8, 33 | 0x14ACBAF4777D5776, 0xF145B6BECCDEA195, 0xDABF2AC8201752FC, 0x24C3C94DF9C8D3F6, 34 | 0xBB6E2924F03912EA, 0x0CE26C0B95C980D9, 0xA49CD132BFBF7CC4, 0xE99D662AF4243939, 35 | 0x27E6AD7891165C3F, 0x8535F040B9744FF1, 0x54B3F4FA5F40D873, 0x72B12C32127FED2B, 36 | 0xEE954D3C7B411F47, 0x9A85AC909A24EAA1, 0x70AC4CD9F04F21F5, 0xF9B89D3E99A075C2, 37 | 0x87B3E2B2B5C907B1, 0xA366E5B8C54F48B8, 0xAE4A9346CC3F7CF2, 0x1920C04D47267BBD, 38 | 0x87BF02C6B49E2AE9, 0x092237AC237F3859, 0xFF07F64EF8ED14D0, 0x8DE8DCA9F03CC54E, 39 | 0x9C1633264DB49C89, 0xB3F22C3D0B0B38ED, 0x390E5FB44D01144B, 0x5BFEA5B4712768E9, 40 | 0x1E1032911FA78984, 0x9A74ACB964E78CB3, 0x4F80F7A035DAFB04, 0x6304D09A0B3738C4, 41 | 0x2171E64683023A08, 0x5B9B63EB9CEFF80C, 0x506AACF489889342, 0x1881AFC9A3A701D6, 42 | 0x6503080440750644, 0xDFD395339CDBF4A7, 0xEF927DBCF00C20F2, 0x7B32F7D1E03680EC, 43 | 0xB9FD7620E7316243, 0x05A7E8A57DB91B77, 0xB5889C6E15630A75, 0x4A750A09CE9573F7, 44 | 0xCF464CEC899A2F8A, 0xF538639CE705B824, 0x3C79A0FF5580EF7F, 0xEDE6C87F8477609D, 45 | 0x799E81F05BC93F31, 0x86536B8CF3428A8C, 0x97D7374C60087B73, 0xA246637CFF328532, 46 | 0x043FCAE60CC0EBA0, 0x920E449535DD359E, 0x70EB093B15B290CC, 0x73A1921916591CBD, 47 | 0x56436C9FE1A1AA8D, 0xEFAC4B70633B8F81, 0xBB215798D45DF7AF, 0x45F20042F24F1768, 48 | 0x930F80F4E8EB7462, 0xFF6712FFCFD75EA1, 0xAE623FD67468AA70, 0xDD2C5BC84BC8D8FC, 49 | 0x7EED120D54CF2DD9, 0x22FE545401165F1C, 0xC91800E98FB99929, 0x808BD68E6AC10365, 50 | 0xDEC468145B7605F6, 0x1BEDE3A3AEF53302, 0x43539603D6C55602, 0xAA969B5C691CCB7A, 51 | 0xA87832D392EFEE56, 0x65942C7B3C7E11AE, 0xDED2D633CAD004F6, 0x21F08570F420E565, 52 | 0xB415938D7DA94E3C, 0x91B859E59ECB6350, 0x10CFF333E0ED804A, 0x28AED140BE0BB7DD, 53 | 0xC5CC1D89724FA456, 0x5648F680F11A2741, 0x2D255069F0B7DAB3, 0x9BC5A38EF729ABD4, 54 | 0xEF2F054308F6A2BC, 0xAF2042F5CC5C2858, 0x480412BAB7F5BE2A, 0xAEF3AF4A563DFE43, 55 | 0x19AFE59AE451497F, 0x52593803DFF1E840, 0xF4F076E65F2CE6F0, 0x11379625747D5AF3, 56 | 0xBCE5D2248682C115, 0x9DA4243DE836994F, 0x066F70B33FE09017, 0x4DC4DE189B671A1C, 57 | 0x51039AB7712457C3, 0xC07A3F80C31FB4B4, 0xB46EE9C5E64A6E7C, 0xB3819A42ABE61C87, 58 | 0x21A007933A522A20, 0x2DF16F761598AA4F, 0x763C4A1371B368FD, 0xF793C46702E086A0, 59 | 0xD7288E012AEB8D31, 0xDE336A2A4BC1C44B, 0x0BF692B38D079F23, 0x2C604A7A177326B3, 60 | 0x4850E73E03EB6064, 0xCFC447F1E53C8E1B, 0xB05CA3F564268D99, 0x9AE182C8BC9474E8, 61 | 0xA4FC4BD4FC5558CA, 0xE755178D58FC4E76, 0x69B97DB1A4C03DFE, 0xF9B5B7C4ACC67C96, 62 | 0xFC6A82D64B8655FB, 0x9C684CB6C4D24417, 0x8EC97D2917456ED0, 0x6703DF9D2924E97E, 63 | 0xC547F57E42A7444E, 0x78E37644E7CAD29E, 0xFE9A44E9362F05FA, 0x08BD35CC38336615, 64 | 0x9315E5EB3A129ACE, 0x94061B871E04DF75, 0xDF1D9F9D784BA010, 0x3BBA57B68871B59D, 65 | 0xD2B7ADEEDED1F73F, 0xF7A255D83BC373F8, 0xD7F4F2448C0CEB81, 0xD95BE88CD210FFA7, 66 | 0x336F52F8FF4728E7, 0xA74049DAC312AC71, 0xA2F61BB6E437FDB5, 0x4F2A5CB07F6A35B3, 67 | 0x87D380BDA5BF7859, 0x16B9F7E06C453A21, 0x7BA2484C8A0FD54E, 0xF3A678CAD9A2E38C, 68 | 0x39B0BF7DDE437BA2, 0xFCAF55C1BF8A4424, 0x18FCF680573FA594, 0x4C0563B89F495AC3, 69 | 0x40E087931A00930D, 0x8CFFA9412EB642C1, 0x68CA39053261169F, 0x7A1EE967D27579E2, 70 | 0x9D1D60E5076F5B6F, 0x3810E399B6F65BA2, 0x32095B6D4AB5F9B1, 0x35CAB62109DD038A, 71 | 0xA90B24499FCFAFB1, 0x77A225A07CC2C6BD, 0x513E5E634C70E331, 0x4361C0CA3F692F12, 72 | 0xD941ACA44B20A45B, 0x528F7C8602C5807B, 0x52AB92BEB9613989, 0x9D1DFA2EFC557F73, 73 | 0x722FF175F572C348, 0x1D1260A51107FE97, 0x7A249A57EC0C9BA2, 0x04208FE9E8F7F2D6, 74 | 0x5A110C6058B920A0, 0x0CD9A497658A5698, 0x56FD23C8F9715A4C, 0x284C847B9D887AAE, 75 | 0x04FEABFBBDB619CB, 0x742E1E651C60BA83, 0x9A9632E65904AD3C, 0x881B82A13B51B9E2, 76 | 0x506E6744CD974924, 0xB0183DB56FFC6A79, 0x0ED9B915C66ED37E, 0x5E11E86D5873D484, 77 | 0xF678647E3519AC6E, 0x1B85D488D0F20CC5, 0xDAB9FE6525D89021, 0x0D151D86ADB73615, 78 | 0xA865A54EDCC0F019, 0x93C42566AEF98FFB, 0x99E7AFEABE000731, 0x48CBFF086DDF285A, 79 | 0x7F9B6AF1EBF78BAF, 0x58627E1A149BBA21, 0x2CD16E2ABD791E33, 0xD363EFF5F0977996, 80 | 0x0CE2A38C344A6EED, 0x1A804AADB9CFA741, 0x907F30421D78C5DE, 0x501F65EDB3034D07, 81 | 0x37624AE5A48FA6E9, 0x957BAF61700CFF4E, 0x3A6C27934E31188A, 0xD49503536ABCA345, 82 | 0x088E049589C432E0, 0xF943AEE7FEBF21B8, 0x6C3B8E3E336139D3, 0x364F6FFA464EE52E, 83 | 0xD60F6DCEDC314222, 0x56963B0DCA418FC0, 0x16F50EDF91E513AF, 0xEF1955914B609F93, 84 | 0x565601C0364E3228, 0xECB53939887E8175, 0xBAC7A9A18531294B, 0xB344C470397BBA52, 85 | 0x65D34954DAF3CEBD, 0xB4B81B3FA97511E2, 0xB422061193D6F6A7, 0x071582401C38434D, 86 | 0x7A13F18BBEDC4FF5, 0xBC4097B116C524D2, 0x59B97885E2F2EA28, 0x99170A5DC3115544, 87 | 0x6F423357E7C6A9F9, 0x325928EE6E6F8794, 0xD0E4366228B03343, 0x565C31F7DE89EA27, 88 | 0x30F5611484119414, 0xD873DB391292ED4F, 0x7BD94E1D8E17DEBC, 0xC7D9F16864A76E94, 89 | 0x947AE053EE56E63C, 0xC8C93882F9475F5F, 0x3A9BF55BA91F81CA, 0xD9A11FBB3D9808E4, 90 | 0x0FD22063EDC29FCA, 0xB3F256D8ACA0B0B9, 0xB03031A8B4516E84, 0x35DD37D5871448AF, 91 | 0xE9F6082B05542E4E, 0xEBFAFA33D7254B59, 0x9255ABB50D532280, 0xB9AB4CE57F2D34F3, 92 | 0x693501D628297551, 0xC62C58F97DD949BF, 0xCD454F8F19C5126A, 0xBBE83F4ECC2BDECB, 93 | 0xDC842B7E2819E230, 0xBA89142E007503B8, 0xA3BC941D0A5061CB, 0xE9F6760E32CD8021, 94 | 0x09C7E552BC76492F, 0x852F54934DA55CC9, 0x8107FCCF064FCF56, 0x098954D51FFF6580, 95 | 0x23B70EDB1955C4BF, 0xC330DE426430F69D, 0x4715ED43E8A45C0A, 0xA8D7E4DAB780A08D, 96 | 0x0572B974F03CE0BB, 0xB57D2E985E1419C7, 0xE8D9ECBE2CF3D73F, 0x2FE4B17170E59750, 97 | 0x11317BA87905E790, 0x7FBF21EC8A1F45EC, 0x1725CABFCB045B00, 0x964E915CD5E2B207, 98 | 0x3E2B8BCBF016D66D, 0xBE7444E39328A0AC, 0xF85B2B4FBCDE44B7, 0x49353FEA39BA63B1, 99 | 0x1DD01AAFCD53486A, 0x1FCA8A92FD719F85, 0xFC7C95D827357AFA, 0x18A6A990C8B35EBD, 100 | 0xCCCB7005C6B9C28D, 0x3BDBB92C43B17F26, 0xAA70B5B4F89695A2, 0xE94C39A54A98307F, 101 | 0xB7A0B174CFF6F36E, 0xD4DBA84729AF48AD, 0x2E18BC1AD9704A68, 0x2DE0966DAF2F8B1C, 102 | 0xB9C11D5B1E43A07E, 0x64972D68DEE33360, 0x94628D38D0C20584, 0xDBC0D2B6AB90A559, 103 | 0xD2733C4335C6A72F, 0x7E75D99D94A70F4D, 0x6CED1983376FA72B, 0x97FCAACBF030BC24, 104 | 0x7B77497B32503B12, 0x8547EDDFB81CCB94, 0x79999CDFF70902CB, 0xCFFE1939438E9B24, 105 | 0x829626E3892D95D7, 0x92FAE24291F2B3F1, 0x63E22C147B9C3403, 0xC678B6D860284A1C, 106 | 0x5873888850659AE7, 0x0981DCD296A8736D, 0x9F65789A6509A440, 0x9FF38FED72E9052F, 107 | 0xE479EE5B9930578C, 0xE7F28ECD2D49EECD, 0x56C074A581EA17FE, 0x5544F7D774B14AEF, 108 | 0x7B3F0195FC6F290F, 0x12153635B2C0CF57, 0x7F5126DBBA5E0CA7, 0x7A76956C3EAFB413, 109 | 0x3D5774A11D31AB39, 0x8A1B083821F40CB4, 0x7B4A38E32537DF62, 0x950113646D1D6E03, 110 | 0x4DA8979A0041E8A9, 0x3BC36E078F7515D7, 0x5D0A12F27AD310D1, 0x7F9D1A2E1EBE1327, 111 | 0xDA3A361B1C5157B1, 0xDCDD7D20903D0C25, 0x36833336D068F707, 0xCE68341F79893389, 112 | 0xAB9090168DD05F34, 0x43954B3252DC25E5, 0xB438C2B67F98E5E9, 0x10DCD78E3851A492, 113 | 0xDBC27AB5447822BF, 0x9B3CDB65F82CA382, 0xB67B7896167B4C84, 0xBFCED1B0048EAC50, 114 | 0xA9119B60369FFEBD, 0x1FFF7AC80904BF45, 0xAC12FB171817EEE7, 0xAF08DA9177DDA93D, 115 | 0x1B0CAB936E65C744, 0xB559EB1D04E5E932, 0xC37B45B3F8D6F2BA, 0xC3A9DC228CAAC9E9, 116 | 0xF3B8B6675A6507FF, 0x9FC477DE4ED681DA, 0x67378D8ECCEF96CB, 0x6DD856D94D259236, 117 | 0xA319CE15B0B4DB31, 0x073973751F12DD5E, 0x8A8E849EB32781A5, 0xE1925C71285279F5, 118 | 0x74C04BF1790C0EFE, 0x4DDA48153C94938A, 0x9D266D6A1CC0542C, 0x7440FB816508C4FE, 119 | 0x13328503DF48229F, 0xD6BF7BAEE43CAC40, 0x4838D65F6EF6748F, 0x1E152328F3318DEA, 120 | 0x8F8419A348F296BF, 0x72C8834A5957B511, 0xD7A023A73260B45C, 0x94EBC8ABCFB56DAE, 121 | 0x9FC10D0F989993E0, 0xDE68A2355B93CAE6, 0xA44CFE79AE538BBE, 0x9D1D84FCCE371425, 122 | 0x51D2B1AB2DDFB636, 0x2FD7E4B9E72CD38C, 0x65CA5B96B7552210, 0xDD69A0D8AB3B546D, 123 | 0x604D51B25FBF70E2, 0x73AA8A564FB7AC9E, 0x1A8C1E992B941148, 0xAAC40A2703D9BEA0, 124 | 0x764DBEAE7FA4F3A6, 0x1E99B96E70A9BE8B, 0x2C5E9DEB57EF4743, 0x3A938FEE32D29981, 125 | 0x26E6DB8FFDF5ADFE, 0x469356C504EC9F9D, 0xC8763C5B08D1908C, 0x3F6C6AF859D80055, 126 | 0x7F7CC39420A3A545, 0x9BFB227EBDF4C5CE, 0x89039D79D6FC5C5C, 0x8FE88B57305E2AB6, 127 | 0xA09E8C8C35AB96DE, 0xFA7E393983325753, 0xD6B6D0ECC617C699, 0xDFEA21EA9E7557E3, 128 | 0xB67C1FA481680AF8, 0xCA1E3785A9E724E5, 0x1CFC8BED0D681639, 0xD18D8549D140CAEA, 129 | 0x4ED0FE7E9DC91335, 0xE4DBF0634473F5D2, 0x1761F93A44D5AEFE, 0x53898E4C3910DA55, 130 | 0x734DE8181F6EC39A, 0x2680B122BAA28D97, 0x298AF231C85BAFAB, 0x7983EED3740847D5, 131 | 0x66C1A2A1A60CD889, 0x9E17E49642A3E4C1, 0xEDB454E7BADC0805, 0x50B704CAB602C329, 132 | 0x4CC317FB9CDDD023, 0x66B4835D9EAFEA22, 0x219B97E26FFC81BD, 0x261E4E4C0A333A9D, 133 | 0x1FE2CCA76517DB90, 0xD7504DFA8816EDBB, 0xB9571FA04DC089C8, 0x1DDC0325259B27DE, 134 | 0xCF3F4688801EB9AA, 0xF4F5D05C10CAB243, 0x38B6525C21A42B0E, 0x36F60E2BA4FA6800, 135 | 0xEB3593803173E0CE, 0x9C4CD6257C5A3603, 0xAF0C317D32ADAA8A, 0x258E5A80C7204C4B, 136 | 0x8B889D624D44885D, 0xF4D14597E660F855, 0xD4347F66EC8941C3, 0xE699ED85B0DFB40D, 137 | 0x2472F6207C2D0484, 0xC2A1E7B5B459AEB5, 0xAB4F6451CC1D45EC, 0x63767572AE3D6174, 138 | 0xA59E0BD101731A28, 0x116D0016CB948F09, 0x2CF9C8CA052F6E9F, 0x0B090A7560A968E3, 139 | 0xABEEDDB2DDE06FF1, 0x58EFC10B06A2068D, 0xC6E57A78FBD986E0, 0x2EAB8CA63CE802D7, 140 | 0x14A195640116F336, 0x7C0828DD624EC390, 0xD74BBE77E6116AC7, 0x804456AF10F5FB53, 141 | 0xEBE9EA2ADF4321C7, 0x03219A39EE587A30, 0x49787FEF17AF9924, 0xA1E9300CD8520548, 142 | 0x5B45E522E4B1B4EF, 0xB49C3B3995091A36, 0xD4490AD526F14431, 0x12A8F216AF9418C2, 143 | 0x001F837CC7350524, 0x1877B51E57A764D5, 0xA2853B80F17F58EE, 0x993E1DE72D36D310, 144 | 0xB3598080CE64A656, 0x252F59CF0D9F04BB, 0xD23C8E176D113600, 0x1BDA0492E7E4586E, 145 | 0x21E0BD5026C619BF, 0x3B097ADAF088F94E, 0x8D14DEDB30BE846E, 0xF95CFFA23AF5F6F4, 146 | 0x3871700761B3F743, 0xCA672B91E9E4FA16, 0x64C8E531BFF53B55, 0x241260ED4AD1E87D, 147 | 0x106C09B972D2E822, 0x7FBA195410E5CA30, 0x7884D9BC6CB569D8, 0x0647DFEDCD894A29, 148 | 0x63573FF03E224774, 0x4FC8E9560F91B123, 0x1DB956E450275779, 0xB8D91274B9E9D4FB, 149 | 0xA2EBEE47E2FBFCE1, 0xD9F1F30CCD97FB09, 0xEFED53D75FD64E6B, 0x2E6D02C36017F67F, 150 | 0xA9AA4D20DB084E9B, 0xB64BE8D8B25396C1, 0x70CB6AF7C2D5BCF0, 0x98F076A4F7A2322E, 151 | 0xBF84470805E69B5F, 0x94C3251F06F90CF3, 0x3E003E616A6591E9, 0xB925A6CD0421AFF3, 152 | 0x61BDD1307C66E300, 0xBF8D5108E27E0D48, 0x240AB57A8B888B20, 0xFC87614BAF287E07, 153 | 0xEF02CDD06FFDB432, 0xA1082C0466DF6C0A, 0x8215E577001332C8, 0xD39BB9C3A48DB6CF, 154 | 0x2738259634305C14, 0x61CF4F94C97DF93D, 0x1B6BACA2AE4E125B, 0x758F450C88572E0B, 155 | 0x959F587D507A8359, 0xB063E962E045F54D, 0x60E8ED72C0DFF5D1, 0x7B64978555326F9F, 156 | 0xFD080D236DA814BA, 0x8C90FD9B083F4558, 0x106F72FE81E2C590, 0x7976033A39F7D952, 157 | 0xA4EC0132764CA04B, 0x733EA705FAE4FA77, 0xB4D8F77BC3E56167, 0x9E21F4F903B33FD9, 158 | 0x9D765E419FB69F6D, 0xD30C088BA61EA5EF, 0x5D94337FBFAF7F5B, 0x1A4E4822EB4D7A59, 159 | 0x6FFE73E81B637FB3, 0xDDF957BC36D8B9CA, 0x64D0E29EEA8838B3, 0x08DD9BDFD96B9F63, 160 | 0x087E79E5A57D1D13, 0xE328E230E3E2B3FB, 0x1C2559E30F0946BE, 0x720BF5F26F4D2EAA, 161 | 0xB0774D261CC609DB, 0x443F64EC5A371195, 0x4112CF68649A260E, 0xD813F2FAB7F5C5CA, 162 | 0x660D3257380841EE, 0x59AC2C7873F910A3, 0xE846963877671A17, 0x93B633ABFA3469F8, 163 | 0xC0C0F5A60EF4CDCF, 0xCAF21ECD4377B28C, 0x57277707199B8175, 0x506C11B9D90E8B1D, 164 | 0xD83CC2687A19255F, 0x4A29C6465A314CD1, 0xED2DF21216235097, 0xB5635C95FF7296E2, 165 | 0x22AF003AB672E811, 0x52E762596BF68235, 0x9AEBA33AC6ECC6B0, 0x944F6DE09134DFB6, 166 | 0x6C47BEC883A7DE39, 0x6AD047C430A12104, 0xA5B1CFDBA0AB4067, 0x7C45D833AFF07862, 167 | 0x5092EF950A16DA0B, 0x9338E69C052B8E7B, 0x455A4B4CFE30E3F5, 0x6B02E63195AD0CF8, 168 | 0x6B17B224BAD6BF27, 0xD1E0CCD25BB9C169, 0xDE0C89A556B9AE70, 0x50065E535A213CF6, 169 | 0x9C1169FA2777B874, 0x78EDEFD694AF1EED, 0x6DC93D9526A50E68, 0xEE97F453F06791ED, 170 | 0x32AB0EDB696703D3, 0x3A6853C7E70757A7, 0x31865CED6120F37D, 0x67FEF95D92607890, 171 | 0x1F2B1D1F15F6DC9C, 0xB69E38A8965C6B65, 0xAA9119FF184CCCF4, 0xF43C732873F24C13, 172 | 0xFB4A3D794A9A80D2, 0x3550C2321FD6109C, 0x371F77E76BB8417E, 0x6BFA9AAE5EC05779, 173 | 0xCD04F3FF001A4778, 0xE3273522064480CA, 0x9F91508BFFCFC14A, 0x049A7F41061A9E60, 174 | 0xFCB6BE43A9F2FE9B, 0x08DE8A1C7797DA9B, 0x8F9887E6078735A1, 0xB5B4071DBFC73A66, 175 | 0x230E343DFBA08D33, 0x43ED7F5A0FAE657D, 0x3A88A0FBBCB05C63, 0x21874B8B4D2DBC4F, 176 | 0x1BDEA12E35F6A8C9, 0x53C065C6C8E63528, 0xE34A1D250E7A8D6B, 0xD6B04D3B7651DD7E, 177 | 0x5E90277E7CB39E2D, 0x2C046F22062DC67D, 0xB10BB459132D0A26, 0x3FA9DDFB67E2F199, 178 | 0x0E09B88E1914F7AF, 0x10E8B35AF3EEAB37, 0x9EEDECA8E272B933, 0xD4C718BC4AE8AE5F, 179 | 0x81536D601170FC20, 0x91B534F885818A06, 0xEC8177F83F900978, 0x190E714FADA5156E, 180 | 0xB592BF39B0364963, 0x89C350C893AE7DC1, 0xAC042E70F8B383F2, 0xB49B52E587A1EE60, 181 | 0xFB152FE3FF26DA89, 0x3E666E6F69AE2C15, 0x3B544EBE544C19F9, 0xE805A1E290CF2456, 182 | 0x24B33C9D7ED25117, 0xE74733427B72F0C1, 0x0A804D18B7097475, 0x57E3306D881EDB4F, 183 | 0x4AE7D6A36EB5DBCB, 0x2D8D5432157064C8, 0xD1E649DE1E7F268B, 0x8A328A1CEDFE552C, 184 | 0x07A3AEC79624C7DA, 0x84547DDC3E203C94, 0x990A98FD5071D263, 0x1A4FF12616EEFC89, 185 | 0xF6F7FD1431714200, 0x30C05B1BA332F41C, 0x8D2636B81555A786, 0x46C9FEB55D120902, 186 | 0xCCEC0A73B49C9921, 0x4E9D2827355FC492, 0x19EBB029435DCB0F, 0x4659D2B743848A2C, 187 | 0x963EF2C96B33BE31, 0x74F85198B05A2E7D, 0x5A0F544DD2B1FB18, 0x03727073C2E134B1, 188 | 0xC7F6AA2DE59AEA61, 0x352787BAA0D7C22F, 0x9853EAB63B5E0B35, 0xABBDCDD7ED5C0860, 189 | 0xCF05DAF5AC8D77B0, 0x49CAD48CEBF4A71E, 0x7A4C10EC2158C4A6, 0xD9E92AA246BF719E, 190 | 0x13AE978D09FE5557, 0x730499AF921549FF, 0x4E4B705B92903BA4, 0xFF577222C14F0A3A, 191 | 0x55B6344CF97AAFAE, 0xB862225B055B6960, 0xCAC09AFBDDD2CDB4, 0xDAF8E9829FE96B5F, 192 | 0xB5FDFC5D3132C498, 0x310CB380DB6F7503, 0xE87FBB46217A360E, 0x2102AE466EBB1148, 193 | 0xF8549E1A3AA5E00D, 0x07A69AFDCC42261A, 0xC4C118BFE78FEAAE, 0xF9F4892ED96BD438, 194 | 0x1AF3DBE25D8F45DA, 0xF5B4B0B0D2DEEEB4, 0x962ACEEFA82E1C84, 0x046E3ECAAF453CE9, 195 | 0xF05D129681949A4C, 0x964781CE734B3C84, 0x9C2ED44081CE5FBD, 0x522E23F3925E319E, 196 | 0x177E00F9FC32F791, 0x2BC60A63A6F3B3F2, 0x222BBFAE61725606, 0x486289DDCC3D6780, 197 | 0x7DC7785B8EFDFC80, 0x8AF38731C02BA980, 0x1FAB64EA29A2DDF7, 0xE4D9429322CD065A, 198 | 0x9DA058C67844F20C, 0x24C0E332B70019B0, 0x233003B5A6CFE6AD, 0xD586BD01C5C217F6, 199 | 0x5E5637885F29BC2B, 0x7EBA726D8C94094B, 0x0A56A5F0BFE39272, 0xD79476A84EE20D06, 200 | 0x9E4C1269BAA4BF37, 0x17EFEE45B0DEE640, 0x1D95B0A5FCF90BC6, 0x93CBE0B699C2585D, 201 | 0x65FA4F227A2B6D79, 0xD5F9E858292504D5, 0xC2B5A03F71471A6F, 0x59300222B4561E00, 202 | 0xCE2F8642CA0712DC, 0x7CA9723FBB2E8988, 0x2785338347F2BA08, 0xC61BB3A141E50E8C, 203 | 0x150F361DAB9DEC26, 0x9F6A419D382595F4, 0x64A53DC924FE7AC9, 0x142DE49FFF7A7C3D, 204 | 0x0C335248857FA9E7, 0x0A9C32D5EAE45305, 0xE6C42178C4BBB92E, 0x71F1CE2490D20B07, 205 | 0xF1BCC3D275AFE51A, 0xE728E8C83C334074, 0x96FBF83A12884624, 0x81A1549FD6573DA5, 206 | 0x5FA7867CAF35E149, 0x56986E2EF3ED091B, 0x917F1DD5F8886C61, 0xD20D8C88C8FFE65F, 207 | 0x31D71DCE64B2C310, 0xF165B587DF898190, 0xA57E6339DD2CF3A0, 0x1EF6E6DBB1961EC9, 208 | 0x70CC73D90BC26E24, 0xE21A6B35DF0C3AD7, 0x003A93D8B2806962, 0x1C99DED33CB890A1, 209 | 0xCF3145DE0ADD4289, 0xD0E4427A5514FB72, 0x77C621CC9FB3A483, 0x67A34DAC4356550B, 210 | 0xF8D626AAAF278509 211 | }; 212 | 213 | static inline constexpr std::array castlingKeys = { 214 | 0, 215 | RandomArray[768], 216 | RandomArray[768 + 1], 217 | RandomArray[768] ^ RandomArray[768 + 1], 218 | RandomArray[768 + 2], 219 | RandomArray[768] ^ RandomArray[768 + 2], 220 | RandomArray[768 + 1] ^ RandomArray[768 + 2], 221 | RandomArray[768] ^ RandomArray[768 + 1] ^ RandomArray[768 + 2], 222 | RandomArray[768 + 3], 223 | RandomArray[768] ^ RandomArray[768 + 3], 224 | RandomArray[768 + 1] ^ RandomArray[768 + 3], 225 | RandomArray[768] ^ RandomArray[768 + 1] ^ RandomArray[768 + 3], 226 | RandomArray[768 + 3] ^ RandomArray[768 + 2], 227 | RandomArray[768 + 3] ^ RandomArray[768 + 2] ^ RandomArray[768], 228 | RandomArray[768 + 1] ^ RandomArray[768 + 2] ^ RandomArray[768 + 3], 229 | RandomArray[768 + 1] ^ RandomArray[768 + 2] ^ RandomArray[768 + 3] ^ RandomArray[768] 230 | }; 231 | // clang-format on 232 | 233 | static constexpr int PieceMap[12] = {1, 3, 5, 7, 9, 11, 0, 2, 4, 6, 8, 10}; 234 | 235 | public: 236 | static inline constexpr U64 pieceKey(Piece piece, Square sq) { 237 | return RandomArray[PieceMap[static_cast(piece)] * 64 + sq]; 238 | } 239 | 240 | static inline constexpr U64 enpassantKey(File file) { 241 | return RandomArray[772 + static_cast(file)]; 242 | } 243 | 244 | static inline constexpr U64 castlingKey(int castling) { 245 | return castlingKeys[castling]; 246 | } 247 | 248 | static inline constexpr U64 castlingIndex(int index) { 249 | return RandomArray[768 + index]; 250 | } 251 | 252 | static inline constexpr U64 sideKey() { 253 | return RandomArray[780]; 254 | } 255 | }; 256 | } // namespace chess -------------------------------------------------------------------------------- /src/evaluate.cpp: -------------------------------------------------------------------------------- 1 | #include "evaluation/evaluate.hpp" 2 | #include "evaluation/material.hpp" 3 | #include "evaluation/psqt.hpp" 4 | #include 5 | 6 | using chess::Bitboard; 7 | using chess::Board; 8 | using chess::Color; 9 | using chess::PieceType; 10 | using jet::search::SearchThread; 11 | using jet::types::Value; 12 | 13 | namespace jet { 14 | namespace evaluation { 15 | 16 | Value evaluate(Board& board) { 17 | Value sum = 0; 18 | 19 | if (board.sideToMove() == Color::WHITE) { 20 | sum += evaluateMaterial(board); 21 | sum += evaluateMaterial(board); 22 | 23 | sum += evaluatePSQT(board); 24 | sum += evaluatePSQT(board); 25 | } else { 26 | sum -= evaluateMaterial(board); 27 | sum -= evaluateMaterial(board); 28 | 29 | sum -= evaluatePSQT(board); 30 | sum -= evaluatePSQT(board); 31 | } 32 | 33 | return sum; 34 | } 35 | 36 | Value evaluate(search::SearchThread& st) { 37 | return st.eval(); 38 | } 39 | 40 | } // namespace evaluation 41 | } // namespace jet 42 | -------------------------------------------------------------------------------- /src/evaluation/evaluate.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../search/searchthread.hpp" 4 | #include "../search/types.hpp" 5 | 6 | namespace jet { 7 | namespace evaluation { 8 | types::Value evaluate(chess::Board&); 9 | types::Value evaluate(search::SearchThread&); 10 | } // namespace evaluation 11 | } // namespace jet -------------------------------------------------------------------------------- /src/evaluation/material.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../chess/board.hpp" 4 | #include "../chess/types.hpp" 5 | #include "../search/types.hpp" 6 | 7 | namespace jet { 8 | namespace evaluation { 9 | using chess::Bitboard; 10 | using chess::Board; 11 | using chess::Color; 12 | using chess::PieceType; 13 | 14 | constexpr std::array material = {100, 300, 300, 500, 900, 0}; 15 | 16 | template 17 | types::Value evaluateMaterial(Board& board) { 18 | Bitboard pieceBB = board.bitboard(); 19 | return pieceBB.popcount() * material[static_cast(pt)]; 20 | } 21 | 22 | template 23 | types::Value evaluateMaterial(Board& board) { 24 | types::Value sum = 0; 25 | 26 | constexpr int sign = (c == chess::Color::WHITE) ? 1 : -1; 27 | 28 | sum += evaluateMaterial(board); 29 | sum += evaluateMaterial(board); 30 | sum += evaluateMaterial(board); 31 | sum += evaluateMaterial(board); 32 | sum += evaluateMaterial(board); 33 | 34 | return sum * sign; 35 | } 36 | } // namespace evaluation 37 | } // namespace jet -------------------------------------------------------------------------------- /src/evaluation/psqt.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../chess/board.hpp" 4 | #include "../chess/types.hpp" 5 | #include "../search/types.hpp" 6 | 7 | namespace jet { 8 | 9 | namespace evaluation { 10 | // clang-format off 11 | constexpr types::Value PSQTs[6][64] = { 12 | { 0, 0, 0, 0, 0, 0, 0, 0, 13 | 50, 50, 50, 50, 50, 50, 50, 50, 14 | 10, 10, 20, 30, 30, 20, 10, 10, 15 | 5, 5, 10, 25, 25, 10, 5, 5, 16 | 0, 0, 0, 20, 20, 0, 0, 0, 17 | 5, -5,-10, 0, 0,-10, -5, 5, 18 | 5, 10, 10,-20,-20, 10, 10, 5, 19 | 0, 0, 0, 0, 0, 0, 0, 0}, 20 | {-50,-40,-30,-30,-30,-30,-40,-50, 21 | -40,-20, 0, 0, 0, 0,-20,-40, 22 | -30, 0, 10, 15, 15, 10, 0,-30, 23 | -30, 5, 15, 20, 20, 15, 5,-30, 24 | -30, 0, 15, 20, 20, 15, 0,-30, 25 | -30, 5, 10, 15, 15, 10, 5,-30, 26 | -40,-20, 0, 5, 5, 0,-20,-40, 27 | -50,-40,-30,-30,-30,-30,-40,-50}, 28 | {-20,-10,-10,-10,-10,-10,-10,-20, 29 | -10, 0, 0, 0, 0, 0, 0,-10, 30 | -10, 0, 5, 10, 10, 5, 0,-10, 31 | -10, 5, 5, 10, 10, 5, 5,-10, 32 | -10, 0, 10, 10, 10, 10, 0,-10, 33 | -10, 10, 10, 10, 10, 10, 10,-10, 34 | -10, 5, 0, 0, 0, 0, 5,-10, 35 | -20,-10,-10,-10,-10,-10,-10,-20,}, 36 | {0, 0, 0, 0, 0, 0, 0, 0, 37 | 5, 10, 10, 10, 10, 10, 10, 5, 38 | -5, 0, 0, 0, 0, 0, 0, -5, 39 | -5, 0, 0, 0, 0, 0, 0, -5, 40 | -5, 0, 0, 0, 0, 0, 0, -5, 41 | -5, 0, 0, 0, 0, 0, 0, -5, 42 | -5, 0, 0, 0, 0, 0, 0, -5, 43 | 0, 0, 0, 5, 5, 0, 0, 0}, 44 | {-20,-10,-10, -5, -5,-10,-10,-20, 45 | -10, 0, 0, 0, 0, 0, 0,-10, 46 | -10, 0, 5, 5, 5, 5, 0,-10, 47 | -5, 0, 5, 5, 5, 5, 0, -5, 48 | 0, 0, 5, 5, 5, 5, 0, -5, 49 | -10, 5, 5, 5, 5, 5, 0,-10, 50 | -10, 0, 5, 0, 0, 0, 0,-10, 51 | -20,-10,-10, -5, -5,-10,-10,-20}, 52 | {-30,-40,-40,-50,-50,-40,-40,-30, 53 | -30,-40,-40,-50,-50,-40,-40,-30, 54 | -30,-40,-40,-50,-50,-40,-40,-30, 55 | -30,-40,-40,-50,-50,-40,-40,-30, 56 | -20,-30,-30,-40,-40,-30,-30,-20, 57 | -10,-20,-20,-20,-20,-20,-20,-10, 58 | 20, 20, 0, 0, 0, 0, 20, 20, 59 | 20, 30, 10, 0, 0, 10, 30, 20}, 60 | }; 61 | 62 | // clang-format on 63 | 64 | template 65 | types::Value evaluatePSQT(const chess::Board& board) { 66 | chess::Bitboard bb = board.bitboard(); 67 | 68 | types::Value sum = 0; 69 | 70 | BitboardIterator(bb) { 71 | chess::Square sq = chess::Square::relativeSquare<~c>(bb.poplsb()); 72 | 73 | sum += PSQTs[static_cast(pt)][sq]; 74 | } 75 | 76 | return sum; 77 | } 78 | 79 | template 80 | types::Value evaluatePSQT(const chess::Board& board) { 81 | types::Value sum = 0; 82 | 83 | constexpr int sign = (c == chess::Color::WHITE) ? 1 : -1; 84 | 85 | sum += evaluatePSQT(board); 86 | sum += evaluatePSQT(board); 87 | sum += evaluatePSQT(board); 88 | sum += evaluatePSQT(board); 89 | sum += evaluatePSQT(board); 90 | sum += evaluatePSQT(board); 91 | 92 | return sum * sign; 93 | } 94 | } // namespace evaluation 95 | 96 | } // namespace jet 97 | -------------------------------------------------------------------------------- /src/hexadecane_512_v2.net: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafid-dev/jet/ca93a57c0007d1eb749d3f4dedf5b42dd3f26f53/src/hexadecane_512_v2.net -------------------------------------------------------------------------------- /src/incbin/incbin.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file incbin.h 4 | * @author Dale Weiler 5 | * @brief Utility for including binary files 6 | * 7 | * Facilities for including binary files into the current translation unit and 8 | * making use from them externally in other translation units. 9 | */ 10 | #ifndef INCBIN_HDR 11 | #define INCBIN_HDR 12 | #include 13 | #if defined(__AVX512BW__) || defined(__AVX512CD__) || defined(__AVX512DQ__) || \ 14 | defined(__AVX512ER__) || defined(__AVX512PF__) || defined(__AVX512VL__) || \ 15 | defined(__AVX512F__) 16 | #define INCBIN_ALIGNMENT_INDEX 6 17 | #elif defined(__AVX__) || defined(__AVX2__) 18 | #define INCBIN_ALIGNMENT_INDEX 5 19 | #elif defined(__SSE__) || defined(__SSE2__) || defined(__SSE3__) || \ 20 | defined(__SSSE3__) || defined(__SSE4_1__) || defined(__SSE4_2__) || \ 21 | defined(__neon__) 22 | #define INCBIN_ALIGNMENT_INDEX 4 23 | #elif ULONG_MAX != 0xffffffffu 24 | #define INCBIN_ALIGNMENT_INDEX 3 25 | #else 26 | #define INCBIN_ALIGNMENT_INDEX 2 27 | #endif 28 | 29 | /* Lookup table of (1 << n) where `n' is `INCBIN_ALIGNMENT_INDEX' */ 30 | #define INCBIN_ALIGN_SHIFT_0 1 31 | #define INCBIN_ALIGN_SHIFT_1 2 32 | #define INCBIN_ALIGN_SHIFT_2 4 33 | #define INCBIN_ALIGN_SHIFT_3 8 34 | #define INCBIN_ALIGN_SHIFT_4 16 35 | #define INCBIN_ALIGN_SHIFT_5 32 36 | #define INCBIN_ALIGN_SHIFT_6 64 37 | 38 | /* Actual alignment value */ 39 | #define INCBIN_ALIGNMENT \ 40 | INCBIN_CONCATENATE(INCBIN_CONCATENATE(INCBIN_ALIGN_SHIFT, _), \ 41 | INCBIN_ALIGNMENT_INDEX) 42 | 43 | /* Stringize */ 44 | #define INCBIN_STR(X) #X 45 | #define INCBIN_STRINGIZE(X) INCBIN_STR(X) 46 | /* Concatenate */ 47 | #define INCBIN_CAT(X, Y) X##Y 48 | #define INCBIN_CONCATENATE(X, Y) INCBIN_CAT(X, Y) 49 | /* Deferred macro expansion */ 50 | #define INCBIN_EVAL(X) X 51 | #define INCBIN_INVOKE(N, ...) INCBIN_EVAL(N(__VA_ARGS__)) 52 | 53 | /* Green Hills uses a different directive for including binary data */ 54 | #if defined(__ghs__) 55 | #if (__ghs_asm == 2) 56 | #define INCBIN_MACRO ".file" 57 | /* Or consider the ".myrawdata" entry in the ld file */ 58 | #else 59 | #define INCBIN_MACRO "\tINCBIN" 60 | #endif 61 | #else 62 | #define INCBIN_MACRO ".incbin" 63 | #endif 64 | 65 | #define INCBIN_ALIGN __attribute__((aligned(INCBIN_ALIGNMENT))) 66 | 67 | #if defined(__arm__) || /* GNU C and RealView */ \ 68 | defined(__arm) || /* Diab */ \ 69 | defined(_ARM) /* ImageCraft */ 70 | #define INCBIN_ARM 71 | #endif 72 | 73 | #ifdef __GNUC__ 74 | /* Utilize .balign where supported */ 75 | #define INCBIN_ALIGN_HOST ".balign " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n" 76 | #define INCBIN_ALIGN_BYTE ".balign 1\n" 77 | #elif defined(INCBIN_ARM) 78 | /* 79 | * On arm assemblers, the alignment value is calculated as (1 << n) where `n' is 80 | * the shift count. This is the value passed to `.align' 81 | */ 82 | #define INCBIN_ALIGN_HOST \ 83 | ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT_INDEX) "\n" 84 | #define INCBIN_ALIGN_BYTE ".align 0\n" 85 | #else 86 | /* We assume other inline assembler's treat `.align' as `.balign' */ 87 | #define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n" 88 | #define INCBIN_ALIGN_BYTE ".align 1\n" 89 | #endif 90 | 91 | /* INCBIN_CONST is used by incbin.c generated files */ 92 | #if defined(__cplusplus) 93 | #define INCBIN_EXTERNAL extern "C" 94 | #define INCBIN_CONST extern const 95 | #else 96 | #define INCBIN_EXTERNAL extern 97 | #define INCBIN_CONST const 98 | #endif 99 | 100 | /** 101 | * @brief Optionally override the linker section into which data is emitted. 102 | * 103 | * @warning If you use this facility, you'll have to deal with platform-specific 104 | * linker output section naming on your own 105 | * 106 | * Overriding the default linker output section, e.g for esp8266/Arduino: 107 | * @code 108 | * #define INCBIN_OUTPUT_SECTION ".irom.text" 109 | * #include "incbin.h" 110 | * INCBIN(Foo, "foo.txt"); 111 | * // Data is emitted into program memory that never gets copied to RAM 112 | * @endcode 113 | */ 114 | #if !defined(INCBIN_OUTPUT_SECTION) 115 | #if defined(__APPLE__) 116 | #define INCBIN_OUTPUT_SECTION ".const_data" 117 | #else 118 | #define INCBIN_OUTPUT_SECTION ".rodata" 119 | #endif 120 | #endif 121 | 122 | #if defined(__APPLE__) 123 | /* The directives are different for Apple branded compilers */ 124 | #define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n" 125 | #define INCBIN_GLOBAL(NAME) \ 126 | ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n" 127 | #define INCBIN_INT ".long " 128 | #define INCBIN_MANGLE "_" 129 | #define INCBIN_BYTE ".byte " 130 | #define INCBIN_TYPE(...) 131 | #else 132 | #define INCBIN_SECTION ".section " INCBIN_OUTPUT_SECTION "\n" 133 | #define INCBIN_GLOBAL(NAME) \ 134 | ".global " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n" 135 | #if defined(__ghs__) 136 | #define INCBIN_INT ".word " 137 | #else 138 | #define INCBIN_INT ".int " 139 | #endif 140 | #if defined(__USER_LABEL_PREFIX__) 141 | #define INCBIN_MANGLE INCBIN_STRINGIZE(__USER_LABEL_PREFIX__) 142 | #else 143 | #define INCBIN_MANGLE "" 144 | #endif 145 | #if defined(INCBIN_ARM) 146 | /* On arm assemblers, `@' is used as a line comment token */ 147 | #define INCBIN_TYPE(NAME) \ 148 | ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", %object\n" 149 | #elif defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER) 150 | /* Mingw doesn't support this directive either */ 151 | #define INCBIN_TYPE(NAME) 152 | #else 153 | /* It's safe to use `@' on other architectures */ 154 | #define INCBIN_TYPE(NAME) \ 155 | ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", @object\n" 156 | #endif 157 | #define INCBIN_BYTE ".byte " 158 | #endif 159 | 160 | /* List of style types used for symbol names */ 161 | #define INCBIN_STYLE_CAMEL 0 162 | #define INCBIN_STYLE_SNAKE 1 163 | 164 | /** 165 | * @brief Specify the prefix to use for symbol names. 166 | * 167 | * By default this is `g', producing symbols of the form: 168 | * @code 169 | * #include "incbin.h" 170 | * INCBIN(Foo, "foo.txt"); 171 | * 172 | * // Now you have the following symbols: 173 | * // const unsigned char gFooData[]; 174 | * // const unsigned char *const gFooEnd; 175 | * // const unsigned int gFooSize; 176 | * @endcode 177 | * 178 | * If however you specify a prefix before including: e.g: 179 | * @code 180 | * #define INCBIN_PREFIX incbin 181 | * #include "incbin.h" 182 | * INCBIN(Foo, "foo.txt"); 183 | * 184 | * // Now you have the following symbols instead: 185 | * // const unsigned char incbinFooData[]; 186 | * // const unsigned char *const incbinFooEnd; 187 | * // const unsigned int incbinFooSize; 188 | * @endcode 189 | */ 190 | #if !defined(INCBIN_PREFIX) 191 | #define INCBIN_PREFIX g 192 | #endif 193 | 194 | /** 195 | * @brief Specify the style used for symbol names. 196 | * 197 | * Possible options are 198 | * - INCBIN_STYLE_CAMEL "CamelCase" 199 | * - INCBIN_STYLE_SNAKE "snake_case" 200 | * 201 | * Default option is *INCBIN_STYLE_CAMEL* producing symbols of the form: 202 | * @code 203 | * #include "incbin.h" 204 | * INCBIN(Foo, "foo.txt"); 205 | * 206 | * // Now you have the following symbols: 207 | * // const unsigned char FooData[]; 208 | * // const unsigned char *const FooEnd; 209 | * // const unsigned int FooSize; 210 | * @endcode 211 | * 212 | * If however you specify a style before including: e.g: 213 | * @code 214 | * #define INCBIN_STYLE INCBIN_STYLE_SNAKE 215 | * #include "incbin.h" 216 | * INCBIN(foo, "foo.txt"); 217 | * 218 | * // Now you have the following symbols: 219 | * // const unsigned char foo_data[]; 220 | * // const unsigned char *const foo_end; 221 | * // const unsigned int foo_size; 222 | * @endcode 223 | */ 224 | #if !defined(INCBIN_STYLE) 225 | #define INCBIN_STYLE INCBIN_STYLE_CAMEL 226 | #endif 227 | 228 | /* Style lookup tables */ 229 | #define INCBIN_STYLE_0_DATA Data 230 | #define INCBIN_STYLE_0_END End 231 | #define INCBIN_STYLE_0_SIZE Size 232 | #define INCBIN_STYLE_1_DATA _data 233 | #define INCBIN_STYLE_1_END _end 234 | #define INCBIN_STYLE_1_SIZE _size 235 | 236 | /* Style lookup: returning identifier */ 237 | #define INCBIN_STYLE_IDENT(TYPE) \ 238 | INCBIN_CONCATENATE(INCBIN_STYLE_, \ 239 | INCBIN_CONCATENATE(INCBIN_EVAL(INCBIN_STYLE), \ 240 | INCBIN_CONCATENATE(_, TYPE))) 241 | 242 | /* Style lookup: returning string literal */ 243 | #define INCBIN_STYLE_STRING(TYPE) INCBIN_STRINGIZE(INCBIN_STYLE_IDENT(TYPE)) 244 | 245 | /* Generate the global labels by indirectly invoking the macro with our style 246 | * type and concatenating the name against them. */ 247 | #define INCBIN_GLOBAL_LABELS(NAME, TYPE) \ 248 | INCBIN_INVOKE( \ 249 | INCBIN_GLOBAL, \ 250 | INCBIN_CONCATENATE(NAME, INCBIN_INVOKE(INCBIN_STYLE_IDENT, TYPE))) \ 251 | INCBIN_INVOKE( \ 252 | INCBIN_TYPE, \ 253 | INCBIN_CONCATENATE(NAME, INCBIN_INVOKE(INCBIN_STYLE_IDENT, TYPE))) 254 | 255 | /** 256 | * @brief Externally reference binary data included in another translation unit. 257 | * 258 | * Produces three external symbols that reference the binary data included in 259 | * another translation unit. 260 | * 261 | * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with 262 | * "Data", as well as "End" and "Size" after. An example is provided below. 263 | * 264 | * @param NAME The name given for the binary data 265 | * 266 | * @code 267 | * INCBIN_EXTERN(Foo); 268 | * 269 | * // Now you have the following symbols: 270 | * // extern const unsigned char FooData[]; 271 | * // extern const unsigned char *const FooEnd; 272 | * // extern const unsigned int FooSize; 273 | * @endcode 274 | */ 275 | #define INCBIN_EXTERN(NAME) \ 276 | INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char INCBIN_CONCATENATE( \ 277 | INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), INCBIN_STYLE_IDENT(DATA))[]; \ 278 | INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char *const INCBIN_CONCATENATE( \ 279 | INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), INCBIN_STYLE_IDENT(END)); \ 280 | INCBIN_EXTERNAL const unsigned int INCBIN_CONCATENATE( \ 281 | INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), INCBIN_STYLE_IDENT(SIZE)) 282 | 283 | /** 284 | * @brief Include a binary file into the current translation unit. 285 | * 286 | * Includes a binary file into the current translation unit, producing three 287 | * symbols for objects that encode the data and size respectively. 288 | * 289 | * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with 290 | * "Data", as well as "End" and "Size" after. An example is provided below. 291 | * 292 | * @param NAME The name to associate with this binary data (as an identifier.) 293 | * @param FILENAME The file to include (as a string literal.) 294 | * 295 | * @code 296 | * INCBIN(Icon, "icon.png"); 297 | * 298 | * // Now you have the following symbols: 299 | * // const unsigned char IconData[]; 300 | * // const unsigned char *const IconEnd; 301 | * // const unsigned int IconSize; 302 | * @endcode 303 | * 304 | * @warning This must be used in global scope 305 | * @warning The identifiers may be different if INCBIN_STYLE is not default 306 | * 307 | * To externally reference the data included by this in another translation unit 308 | * please @see INCBIN_EXTERN. 309 | */ 310 | #define INCBIN(NAME, FILENAME) \ 311 | __asm__( \ 312 | INCBIN_SECTION INCBIN_GLOBAL_LABELS( \ 313 | NAME, DATA) INCBIN_ALIGN_HOST INCBIN_MANGLE \ 314 | INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING( \ 315 | DATA) ":\n" INCBIN_MACRO " \"" FILENAME \ 316 | "\"\n" INCBIN_GLOBAL_LABELS( \ 317 | NAME, END) INCBIN_ALIGN_BYTE INCBIN_MANGLE \ 318 | INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING( \ 319 | END) ":\n" INCBIN_BYTE "1\n" INCBIN_GLOBAL_LABELS(NAME, \ 320 | SIZE) \ 321 | INCBIN_ALIGN_HOST INCBIN_MANGLE INCBIN_STRINGIZE( \ 322 | INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(SIZE) ":\n" INCBIN_INT \ 323 | INCBIN_MANGLE INCBIN_STRINGIZE( \ 324 | INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) " - " INCBIN_MANGLE \ 325 | INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING( \ 326 | DATA) "\n" INCBIN_ALIGN_HOST \ 327 | ".text\n"); \ 328 | INCBIN_EXTERN(NAME) 329 | 330 | #endif -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "bench.hpp" 2 | #include "chess/movegen.hpp" 3 | #include "chess/zobrist.hpp" 4 | #include "evaluation/evaluate.hpp" 5 | #include "nnue/nnue.hpp" 6 | #include "perfsuite.hpp" 7 | #include "search/search.hpp" 8 | #include "search/searchinfo.hpp" 9 | #include "search/searchthread.hpp" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace chess; 18 | using namespace jet; 19 | using namespace jet::search; 20 | 21 | static constexpr std::string_view NAME = "Jet"; 22 | static constexpr std::string_view VERSION = "1.2"; 23 | static constexpr std::string_view AUTHOR = "Rafid Ahsan"; 24 | 25 | template 26 | void set_option(std::istream &is, std::string &token, const std::string& name, T &value) { 27 | if (token == name) { 28 | is >> std::skipws >> token; 29 | is >> std::skipws >> token; 30 | 31 | if constexpr (std::is_floating_point_v) { 32 | value = std::stof(token); 33 | } else { 34 | value = std::stoi(token); 35 | } 36 | } 37 | } 38 | 39 | template 40 | struct TypeName { 41 | static std::string get() { 42 | return typeid(T).name(); 43 | } 44 | }; 45 | 46 | template 47 | void print_parameter_inputs(const std::string& name, 48 | T current_val, float min_val, float max_val, 49 | float start_lr, float end_lr) { 50 | if constexpr(uci){ 51 | std::cout << "option name " << name << " type string default " << current_val << std::endl; 52 | }else{ 53 | std::cout << name << ", " << TypeName::get() << ", " << current_val << ", " << min_val << ", " << max_val << ", " 54 | << start_lr << ", " << end_lr << std::endl; 55 | } 56 | } 57 | 58 | #define TUNING_OPTION(param) set_option(iss, token, #param, param) 59 | #define PARAM_INPUT(param, min, max, start, end) if (!uci) { print_parameter_inputs(#param, param, min, max, start, end); } else { print_parameter_inputs(#param, param, min, max, start, end); } 60 | 61 | void print_parameter_inputs(bool uci) { 62 | using namespace search::search_params; 63 | 64 | PARAM_INPUT(lmr_base, 0.5, 3.0, 0.1, 0.002); 65 | PARAM_INPUT(lmr_division, 0.5, 3.0, 0.1, 0.002); 66 | PARAM_INPUT(lmr_see_margin, -1000, 0, 2.25, 0.002); 67 | PARAM_INPUT(qs_see_ordering_threshold, -1000, 0, 2.25, 0.002); 68 | 69 | PARAM_INPUT(nmp_base, 3, 5, 1, 0.002); 70 | PARAM_INPUT(nmp_depth_divisor, 1, 5, 1, 0.002); 71 | PARAM_INPUT(nmp_max_scaled_depth, 2, 5, 1, 0.002); 72 | PARAM_INPUT(nmp_divisor, 100, 300, 2, 0.002); 73 | 74 | PARAM_INPUT(rfp_margin, 10, 100, 1, 0.002); 75 | PARAM_INPUT(rfp_depth, 6, 9, 1, 0.002); 76 | 77 | PARAM_INPUT(lmp_depth, 5, 9, 1, 0.002); 78 | PARAM_INPUT(lmp_base, 2, 5, 1, 0.002); 79 | PARAM_INPUT(lmp_scalar, 1, 5, 1, 0.002); 80 | 81 | PARAM_INPUT(se_depth, 5, 10, 1, 0.002); 82 | PARAM_INPUT(se_depth_offset, 1, 4, 1, 0.002); 83 | PARAM_INPUT(singular_scalar, 1, 10, 1, 0.002); 84 | PARAM_INPUT(singular_divisor, 1, 10, 1, 0.002); 85 | PARAM_INPUT(singular_depth_divisor, 1, 3, 1, 0.002); 86 | 87 | PARAM_INPUT(asp_delta, 5, 15, 1, 0.002); 88 | } 89 | 90 | int main(int argc, char** argv) { 91 | Attacks::init(); 92 | nnue::init(); 93 | search::init(); 94 | 95 | std::cout << NAME << " " << VERSION << std::endl; 96 | std::cout << "Copyright (C) 2023 " << AUTHOR << std::endl; 97 | search::TranspositionTable.initialize(16); 98 | 99 | auto heapSt = std::make_unique(); 100 | auto& st = *heapSt; 101 | Board& board = st.board(); 102 | 103 | std::string line; 104 | std::string token; 105 | 106 | std::vector moves; 107 | 108 | jet::search::SearchInfo info; 109 | 110 | if (argc > 1 && std::string(argv[1]) == "bench") { 111 | StartBenchmark(st); 112 | return 0; 113 | } 114 | 115 | print_parameter_inputs(true); 116 | 117 | while (std::getline(std::cin, line)) { 118 | token.clear(); 119 | 120 | std::istringstream iss(line); 121 | 122 | iss >> token; 123 | 124 | if (token == "export"){ 125 | iss >> token; 126 | if (token == "searchparams"){ 127 | print_parameter_inputs(false); 128 | } else { 129 | std::cout << "Unknown export option: " << token << std::endl; 130 | std::cout << "Did you mean: export searchparams" << std::endl; 131 | } 132 | } 133 | else if (token == "uci") { 134 | std::cout << "id name " << NAME << " " << VERSION << std::endl; 135 | std::cout << "id author " << AUTHOR << std::endl; 136 | std::cout << "option name Hash type spin default 8 min 8 max 32768" << std::endl; 137 | std::cout << "option name Threads type spin default 1 min 1 max 1" << std::endl; 138 | std::cout << "uciok" << std::endl; 139 | } else if (token == "isready") { 140 | std::cout << "readyok\n"; 141 | } else if (token == "ucinewgame") { 142 | // Re initialize the transposition table upon a new game 143 | search::TranspositionTable.initialize(16); 144 | st.setFen(FENS::STARTPOS); 145 | } else if (token == "movegen") { 146 | Movelist list; 147 | MoveGen::legalmoves(board, list); 148 | 149 | std::cout << list << std::endl; 150 | 151 | list.clear(); 152 | MoveGen::legalmoves(board, list); 153 | std::cout << "Quiet moves: " << list.size() << '\n'; 154 | std::cout << list << std::endl; 155 | 156 | list.clear(); 157 | MoveGen::legalmoves(board, list); 158 | std::cout << "Capture moves: " << list.size() << '\n'; 159 | std::cout << list << std::endl; 160 | 161 | } else if (token == "bench"){ 162 | StartBenchmark(st); 163 | exit(0); 164 | } else if (token == "position") { 165 | iss >> token; 166 | 167 | if (token == "startpos") { 168 | st.setFen(FENS::STARTPOS); 169 | iss >> token; 170 | 171 | } else if (token == "kiwipete") { 172 | st.setFen(FENS::KIWIPETE); 173 | iss >> token; 174 | 175 | } else if (token == "fen") { 176 | std::string fen; 177 | 178 | while (iss >> token && token != "moves") { 179 | fen += token + ' '; 180 | } 181 | 182 | st.setFen(fen); 183 | } 184 | 185 | if (token == "moves") { 186 | while (iss >> token) { 187 | Move move = board.uciToMove(token); 188 | // std::cout << "Parsed move: " << move << '\n'; 189 | board.makeMove(move); 190 | } 191 | } 192 | 193 | continue; 194 | } else if (token == "makemove" || token == "make") { 195 | while (iss >> token) { 196 | Move move = board.uciToMove(token); 197 | board.makeMove(move); 198 | moves.push_back(move); 199 | } 200 | 201 | std::cout << board << std::endl; 202 | } else if (token == "unmakemove" || token == "unmake") { 203 | board.unmakeMove(moves.back()); 204 | moves.pop_back(); 205 | 206 | std::cout << board << std::endl; 207 | 208 | } else if (token == "perftsuite") { 209 | iss >> token; 210 | 211 | perft::bulkSuite(token, 1000); 212 | } else if (token == "perft") { 213 | iss >> token; 214 | 215 | int depth = 6; 216 | if (token == "depth") { 217 | iss >> token; 218 | depth = std::stoi(token); 219 | } 220 | 221 | iss >> token; 222 | if (token == "speed") { 223 | perft::bulkSpeedTest(board, depth); 224 | } else { 225 | perft::startBulk(board, depth); 226 | } 227 | 228 | // Search things 229 | } else if (token == "go") { 230 | jet::search::TimeManager& tm = st.timeManager(); 231 | 232 | tm.setNodes(0); 233 | tm.setMTG(0); 234 | 235 | while (iss >> token) { 236 | if (token == "depth") { 237 | iss >> token; 238 | info.setDepth(std::stoi(token)); 239 | } else if (token == "wtime") { 240 | iss >> token; 241 | tm.setTime(std::stod(token)); 242 | } else if (token == "btime") { 243 | iss >> token; 244 | tm.setTime(std::stod(token)); 245 | } else if (token == "winc") { 246 | iss >> token; 247 | tm.setTime(std::stod(token)); 248 | } else if (token == "binc") { 249 | iss >> token; 250 | tm.setTime(std::stod(token)); 251 | } else if (token == "movetime") { 252 | iss >> token; 253 | tm.setTime(std::stod(token)); 254 | } else if (token == "movestogo") { 255 | iss >> token; 256 | tm.setMTG(std::stoi(token)); 257 | } else if (token == "nodes") { 258 | iss >> token; 259 | tm.setNodes(std::stoull(token)); 260 | } 261 | } 262 | 263 | jet::search::search(st, info); 264 | } else if (token == "setoption") { 265 | iss >> token; 266 | iss >> token; 267 | 268 | using namespace search::search_params; 269 | 270 | TUNING_OPTION(lmr_base); 271 | TUNING_OPTION(lmr_division); 272 | TUNING_OPTION(lmr_see_margin); 273 | 274 | TUNING_OPTION(qs_see_ordering_threshold); 275 | 276 | TUNING_OPTION(nmp_base); 277 | TUNING_OPTION(nmp_depth_divisor); 278 | TUNING_OPTION(nmp_max_scaled_depth); 279 | TUNING_OPTION(nmp_divisor); 280 | 281 | TUNING_OPTION(rfp_margin); 282 | TUNING_OPTION(rfp_depth); 283 | 284 | TUNING_OPTION(lmp_depth); 285 | TUNING_OPTION(lmp_base); 286 | TUNING_OPTION(lmp_scalar); 287 | 288 | TUNING_OPTION(se_depth); 289 | TUNING_OPTION(se_depth_offset); 290 | TUNING_OPTION(singular_scalar); 291 | TUNING_OPTION(singular_divisor); 292 | TUNING_OPTION(singular_depth_divisor); 293 | TUNING_OPTION(singular_depth_intercept); 294 | 295 | TUNING_OPTION(asp_delta); 296 | 297 | if (token == "Hash") { 298 | iss >> token; 299 | iss >> token; 300 | search::TranspositionTable.initialize(std::clamp(std::stoi(token), 8, 32768)); 301 | } 302 | 303 | search::init(); 304 | } else if (token == "print") { 305 | std::cout << board << std::endl; 306 | st.refresh(); 307 | std::cout << "Eval: " << jet::evaluation::evaluate(st) << std::endl; 308 | } else if (token == "quit" || token == "exit") { 309 | break; 310 | } else if (token == "\n") { 311 | continue; 312 | } else { 313 | std::cout << "Unknown command: " << token << '\n'; 314 | } 315 | } 316 | return 0; 317 | } -------------------------------------------------------------------------------- /src/misc/argparse.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace misc { 9 | class ArgumentParser { 10 | private: 11 | std::unordered_map argumentDescriptions; 12 | std::unordered_map arguments; 13 | std::unordered_map optionalArguments; 14 | std::string programName; 15 | 16 | bool validateArguments() const { 17 | for (const auto& desc : argumentDescriptions) { 18 | if (!optionalArguments.at(desc.first) && arguments.count(desc.first) == 0) { 19 | std::cerr << "Error: Argument " << desc.first << " is missing.\n"; 20 | return false; 21 | } 22 | } 23 | return true; 24 | } 25 | 26 | public: 27 | ArgumentParser() { 28 | } 29 | 30 | void addArgument(const std::string& name, const std::string& description, bool isOptional = false) { 31 | argumentDescriptions[name] = description; 32 | optionalArguments[name] = isOptional; 33 | } 34 | 35 | bool parse(int argc, char* argv[]) { 36 | for (int i = 1; i < argc; i += 2) { 37 | std::string arg = argv[i]; 38 | if (i + 1 < argc) { 39 | arguments[arg] = argv[i + 1]; 40 | } 41 | } 42 | 43 | return validateArguments(); 44 | } 45 | 46 | std::string getArgumentValue(const std::string& name) const { 47 | if (arguments.count(name) > 0) { 48 | return arguments.at(name); 49 | } 50 | return ""; 51 | } 52 | 53 | bool argumentExists(const std::string& name) const { 54 | return arguments.count(name) > 0; 55 | } 56 | 57 | void printHelp() const { 58 | std::cout << "Usage: " << programName << " [options]\n"; 59 | 60 | // Calculate the maximum argument name length for alignment 61 | size_t maxNameLength = 0; 62 | for (const auto& desc : argumentDescriptions) { 63 | maxNameLength = std::max(maxNameLength, desc.first.length()); 64 | } 65 | 66 | // Print required argument descriptions 67 | for (const auto& desc : argumentDescriptions) { 68 | if (!optionalArguments.at(desc.first)) { 69 | std::cout << " " << std::left << std::setw(maxNameLength + 5) << desc.first << desc.second 70 | << std::endl; 71 | } 72 | } 73 | 74 | // Print optional argument descriptions 75 | for (const auto& desc : argumentDescriptions) { 76 | if (optionalArguments.at(desc.first)) { 77 | std::cout << " " << std::left << std::setw(maxNameLength + 5) << desc.first << desc.second 78 | << " (Optional)" << std::endl; 79 | } 80 | } 81 | } 82 | 83 | void setProgramName(const std::string& name) { 84 | programName = name; 85 | } 86 | }; 87 | } -------------------------------------------------------------------------------- /src/misc/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace misc { 8 | 9 | static inline std::vector splitString(std::string_view str, char delimiter) { 10 | std::vector result; 11 | size_t start = 0; 12 | size_t end = str.find(delimiter); 13 | 14 | while (end != std::string_view::npos) { 15 | result.push_back(str.substr(start, end - start)); 16 | start = end + 1; 17 | end = str.find(delimiter, start); 18 | } 19 | 20 | result.push_back(str.substr(start)); 21 | 22 | return result; 23 | } 24 | 25 | // Returns the current time in Duration 26 | template 27 | inline double tick() { 28 | return (double) std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); 29 | } 30 | 31 | } // namespace misc 32 | -------------------------------------------------------------------------------- /src/nnue.cpp: -------------------------------------------------------------------------------- 1 | #include "nnue/nnue.hpp" 2 | 3 | #include 4 | 5 | #define INCBIN_STYLE INCBIN_STYLE_CAMEL 6 | #include "incbin/incbin.h" 7 | INCBIN(EVAL, NNFILE); 8 | 9 | namespace jet { 10 | namespace nnue { 11 | 12 | std::array inputWeights; 13 | std::array inputBias; 14 | std::array hiddenWeights; 15 | std::array hiddenBias; 16 | 17 | template 18 | void copyDataFromMemory(std::array& dataArray, uint64_t& memoryIndex) { 19 | constexpr auto size = sizeof(T) * SIZE; 20 | std::memcpy(dataArray.data(), &gEVALData[memoryIndex], size); 21 | memoryIndex += size; 22 | } 23 | 24 | void init() { 25 | uint64_t memoryIndex = 0; 26 | 27 | copyDataFromMemory(inputWeights, memoryIndex); 28 | copyDataFromMemory(inputBias, memoryIndex); 29 | copyDataFromMemory(hiddenWeights, memoryIndex); 30 | copyDataFromMemory(hiddenBias, memoryIndex); 31 | 32 | #ifdef DEBUG 33 | std::cout << "Memory index: " << memoryIndex << std::endl; 34 | std::cout << "Size: " << gEVALSize << std::endl; 35 | #endif 36 | } 37 | 38 | } // namespace nnue 39 | } // namespace jet -------------------------------------------------------------------------------- /src/nnue/accumulator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "types.hpp" 7 | 8 | namespace jet { 9 | 10 | namespace nnue { 11 | 12 | enum class AccumulatorOP : uint8_t { 13 | ADD, 14 | SUB, 15 | ADD_SUB, 16 | }; 17 | 18 | template 19 | class AccumulatorBase { 20 | private: 21 | using Weights = std::array; 22 | 23 | // 0 = white POV, 1 = black POV 24 | std::array weights; 25 | 26 | public: 27 | AccumulatorBase() = default; 28 | 29 | void load(const Weights& bias) { 30 | std::memcpy(weights[0].data(), bias.data(), sizeof(Weights)); 31 | std::memcpy(weights[1].data(), bias.data(), sizeof(Weights)); 32 | } 33 | 34 | template 35 | void add(const T* input) { 36 | for (int i = 0; i < constants::HIDDEN_SIZE; ++i) { 37 | weights[static_cast(c)][i] += input[i]; 38 | } 39 | } 40 | 41 | template 42 | void sub(const T* input) { 43 | for (int i = 0; i < constants::HIDDEN_SIZE; ++i) { 44 | weights[static_cast(c)][i] -= input[i]; 45 | } 46 | } 47 | 48 | template 49 | void addSub(const T* inputAdd, const T* inputSub) { 50 | for (int i = 0; i < constants::HIDDEN_SIZE; ++i) { 51 | weights[static_cast(c)][i] += inputAdd[i] - inputSub[i]; 52 | } 53 | } 54 | 55 | template 56 | const auto& data() const { 57 | return weights[static_cast(c)]; 58 | } 59 | 60 | void copy(const AccumulatorBase& other) { 61 | std::memcpy(weights[0].data(), other.weights[0].data(), sizeof(Weights)); 62 | std::memcpy(weights[1].data(), other.weights[1].data(), sizeof(Weights)); 63 | } 64 | }; 65 | 66 | using Accumulator = AccumulatorBase; 67 | 68 | } // namespace nnue 69 | 70 | } // namespace jet 71 | -------------------------------------------------------------------------------- /src/nnue/constants.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace jet { 4 | namespace nnue { 5 | 6 | namespace constants { 7 | 8 | constexpr int BUCKETS = 16; 9 | constexpr int INPUT_SIZE = 64 * 6 * 2 * BUCKETS; 10 | constexpr int HIDDEN_SIZE = 512; 11 | constexpr int OUTPUT_SIZE = 1; 12 | 13 | constexpr int INPUT_LAYER_SIZE = INPUT_SIZE * HIDDEN_SIZE; 14 | constexpr int HIDDEN_LAYER_SIZE = HIDDEN_SIZE * 2; 15 | constexpr int OUTPUT_LAYER_SIZE = OUTPUT_SIZE; 16 | 17 | constexpr int INPUT_QUANTIZATION = 32; 18 | constexpr int HIDDEN_QUANTIZATON = 128; 19 | 20 | // clang-format off 21 | constexpr std::array KING_BUCKET { 22 | 0, 1, 2, 3, 3, 2, 1, 0, 23 | 4, 5, 6, 7, 7, 6, 5, 4, 24 | 8, 9, 10, 11, 11, 10, 9, 8, 25 | 8, 9, 10, 11, 11, 10, 9, 8, 26 | 12, 12, 13, 13, 13, 13, 12, 12, 27 | 12, 12, 13, 13, 13, 13, 12, 12, 28 | 14, 14, 15, 15, 15, 15, 14, 14, 29 | 14, 14, 15, 15, 15, 15, 14, 14, 30 | }; 31 | // clang-format on 32 | 33 | } // namespace constants 34 | 35 | } // namespace nnue 36 | 37 | } // namespace jet 38 | -------------------------------------------------------------------------------- /src/nnue/nnue.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "accumulator.hpp" 7 | #include "types.hpp" 8 | 9 | namespace jet { 10 | 11 | namespace nnue { 12 | 13 | void init(); 14 | 15 | extern std::array inputWeights; 16 | extern std::array inputBias; 17 | extern std::array hiddenWeights; 18 | extern std::array hiddenBias; 19 | 20 | class Network { 21 | private: 22 | std::array accumulatorStack; 23 | 24 | int currentAccumulator = 0; 25 | 26 | static int16_t ReLU(int16_t x) { 27 | return std::max(x, static_cast(0)); 28 | } 29 | 30 | public: 31 | Network() = default; 32 | 33 | void resetCurrentAccumulator() { 34 | accumulatorStack[currentAccumulator].load(inputBias); 35 | } 36 | 37 | void push() { 38 | accumulatorStack[currentAccumulator + 1].copy(accumulatorStack[currentAccumulator]); 39 | currentAccumulator++; 40 | } 41 | 42 | void pull() { 43 | currentAccumulator--; 44 | } 45 | 46 | void reset() { 47 | currentAccumulator = 0; 48 | } 49 | 50 | template 51 | int32_t eval() const { 52 | const auto& accumulator = accumulatorStack[currentAccumulator]; 53 | 54 | int32_t output = hiddenBias[0]; 55 | 56 | for (int i = 0; i < constants::HIDDEN_SIZE; ++i) { 57 | output += ReLU(accumulator.data()[i]) * hiddenWeights[i]; 58 | } 59 | 60 | for (int i = 0; i < constants::HIDDEN_SIZE; ++i) { 61 | output += ReLU(accumulator.data<~side>()[i]) * hiddenWeights[constants::HIDDEN_SIZE + i]; 62 | } 63 | 64 | return output / constants::INPUT_QUANTIZATION / constants::HIDDEN_QUANTIZATON; 65 | } 66 | 67 | template 68 | void updateAccumulator(chess::PieceType pieceType, chess::Color pieceColor, chess::Square square, 69 | chess::Square kingSq) { 70 | static_assert(operation != AccumulatorOP::ADD_SUB, 71 | "This overloaded function isn't for add sub. Only add or sub."); 72 | 73 | auto& accumulator = accumulatorStack[currentAccumulator]; 74 | const int inputs = index(pieceType, pieceColor, square, kingSq); 75 | 76 | if constexpr (operation == AccumulatorOP::ADD) { 77 | accumulator.add(inputWeights.data() + inputs * constants::HIDDEN_SIZE); 78 | } else if constexpr (operation == AccumulatorOP::SUB) { 79 | accumulator.sub(inputWeights.data() + inputs * constants::HIDDEN_SIZE); 80 | } 81 | } 82 | 83 | template 84 | void updateAccumulator(chess::PieceType pieceType, chess::Color pieceColor, chess::Square from, chess::Square to, 85 | chess::Square kingSq) { 86 | static_assert(operation == AccumulatorOP::ADD_SUB, 87 | "This overloaded function isn't for add sub. Only add or sub."); 88 | 89 | auto& accumulator = accumulatorStack[currentAccumulator]; 90 | 91 | const int inputsAdd = index(pieceType, pieceColor, to, kingSq); 92 | const int inputsSub = index(pieceType, pieceColor, from, kingSq); 93 | 94 | accumulator.addSub(inputWeights.data() + inputsAdd * constants::HIDDEN_SIZE, 95 | inputWeights.data() + inputsSub * constants::HIDDEN_SIZE); 96 | } 97 | 98 | template 99 | void updateAccumulator(chess::PieceType pieceType, chess::Color pieceColor, chess::Square square, 100 | chess::Square kingSqWhite, chess::Square kingSqBlack) { 101 | static_assert(operation != AccumulatorOP::ADD_SUB, 102 | "This overloaded function isn't for subbing or adding. Only add sub."); 103 | 104 | if constexpr (operation == AccumulatorOP::ADD) { 105 | updateAccumulator(pieceType, pieceColor, square, kingSqWhite); 106 | updateAccumulator(pieceType, pieceColor, square, kingSqBlack); 107 | } else if constexpr (operation == AccumulatorOP::SUB) { 108 | updateAccumulator(pieceType, pieceColor, square, kingSqWhite); 109 | updateAccumulator(pieceType, pieceColor, square, kingSqBlack); 110 | } 111 | } 112 | 113 | template 114 | void updateAccumulator(chess::PieceType pieceType, chess::Color pieceColor, chess::Square from, chess::Square to, 115 | chess::Square kingSqWhite, chess::Square kingSqBlack) { 116 | static_assert(operation == AccumulatorOP::ADD_SUB, 117 | "This overloaded function isn't for subbing or adding. Only add sub."); 118 | 119 | updateAccumulator(pieceType, pieceColor, from, to, kingSqWhite); 120 | updateAccumulator(pieceType, pieceColor, from, to, kingSqBlack); 121 | } 122 | }; 123 | 124 | } // namespace nnue 125 | 126 | } // namespace jet 127 | -------------------------------------------------------------------------------- /src/nnue/types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../chess/square.hpp" 8 | #include "../chess/types.hpp" 9 | 10 | #include "constants.hpp" 11 | 12 | namespace jet { 13 | 14 | namespace nnue { 15 | 16 | inline int kingSquareIndex(chess::Square kingSq, chess::Color view) { 17 | kingSq = chess::Square::relativeSquare(kingSq, view); 18 | 19 | if constexpr (constants::BUCKETS != 0) { 20 | return constants::KING_BUCKET[kingSq]; 21 | } else { 22 | return 0; 23 | } 24 | } 25 | 26 | template 27 | inline int index(chess::PieceType pieceType, chess::Color pieceColor, chess::Square sq, chess::Square kingSq) { 28 | const int kingIndex = kingSquareIndex(kingSq, view); 29 | 30 | sq = chess::Square::relativeSquare(sq, view); 31 | sq = chess::Square(sq ^ (7 * !!(kingSq & 0x4))); 32 | 33 | // clang-format off 34 | return static_cast(sq) 35 | + static_cast(pieceType) * 64 36 | + !(static_cast(pieceColor) ^ static_cast(view)) * 64 * 6 + kingIndex * 64 * 6 * 2; 37 | // clang-format on 38 | } 39 | } // namespace nnue 40 | 41 | } // namespace jet 42 | -------------------------------------------------------------------------------- /src/perfsuite.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "chess/fens.hpp" 4 | #include "misc/utils.hpp" 5 | #include 6 | #include 7 | 8 | namespace chess { 9 | class Board; 10 | } 11 | 12 | namespace perft { 13 | class DepthNodes { 14 | private: 15 | uint64_t m_nodes; 16 | uint32_t m_depth; 17 | 18 | public: 19 | constexpr DepthNodes(const uint64_t nodes, const uint32_t depth) : m_nodes(nodes), m_depth(depth) { 20 | } 21 | 22 | constexpr uint64_t nodes() const { 23 | return m_nodes; 24 | } 25 | 26 | constexpr uint32_t depth() const { 27 | return m_depth; 28 | } 29 | }; 30 | class EpdInfo { 31 | private: 32 | std::string m_fen; 33 | std::vector m_info_vec; 34 | 35 | public: 36 | EpdInfo(const std::string_view& epd) { 37 | auto split = misc::splitString(epd, ';'); 38 | 39 | m_fen = split[0]; 40 | 41 | for (size_t i = 1; i < split.size(); i++) { 42 | auto info_split = misc::splitString(split[i], ' '); 43 | 44 | auto depth = std::stoi(std::string{info_split[0][1]}); 45 | auto nodes = std::stoull(std::string{info_split[1]}); 46 | 47 | m_info_vec.emplace_back(nodes, depth); 48 | } 49 | } 50 | 51 | constexpr const std::string& fen() const { 52 | return m_fen; 53 | } 54 | 55 | constexpr const std::vector& fetch() const { 56 | return m_info_vec; 57 | } 58 | }; 59 | 60 | void bulkSuite(const std::string& name, const uint64_t max); 61 | void startBulk(const std::string& fen, const int depth = 1); 62 | void startBulk(const chess::Board& board, const int depth = 1); 63 | void bulkSpeedTest(const std::string_view& fen = chess::FENS::STARTPOS, const int depth = 7); 64 | void bulkSpeedTest(const chess::Board& board, const int depth = 7); 65 | 66 | } // namespace perft -------------------------------------------------------------------------------- /src/perftsuite.cpp: -------------------------------------------------------------------------------- 1 | #include "chess/movegen.hpp" 2 | #include "chess/movegencountonly.hpp" 3 | #include "perfsuite.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace perft { 10 | 11 | template 12 | uint64_t bulkPerft(chess::Board& board, int depth) { 13 | if (depth == 1) { 14 | return chess::MoveGenCountOnly::legalmoves(board); 15 | } 16 | 17 | chess::Movelist moves; 18 | chess::MoveGen::legalmoves(board, moves); 19 | 20 | // if (depth == 1) { 21 | // return moves.size(); 22 | // } 23 | 24 | uint64_t nodes = 0; 25 | 26 | for (const auto& move : moves) { 27 | if constexpr (print) { 28 | board.makeMove(move); 29 | auto child = bulkPerft(board, depth - 1); 30 | board.unmakeMove(move); 31 | std::cout << move << ": " << child << std::endl; 32 | nodes += child; 33 | } else { 34 | board.makeMove(move); 35 | nodes += bulkPerft(board, depth - 1); 36 | board.unmakeMove(move); 37 | } 38 | } 39 | 40 | return nodes; 41 | } 42 | 43 | template 44 | void testPositionBulk(chess::Board& board, int depth, uint64_t& nodes) { 45 | if constexpr (print) { 46 | // if (depth == 1) { 47 | // chess::Movelist moves; 48 | // chess::MoveGen::legalmoves(board, moves); 49 | 50 | // for (const auto& move : moves) { 51 | // std::cout << move << ": 1" << std::endl; 52 | // } 53 | 54 | // nodes = moves.size(); 55 | // } else { 56 | nodes = bulkPerft(board, depth); 57 | // } 58 | } else { 59 | nodes = bulkPerft(board, depth); 60 | } 61 | } 62 | 63 | void bulkSuite(const std::string& name, const uint64_t max) { 64 | std::cout << "(BULK) Starting perft suite: " << name << std::endl; 65 | 66 | std::ifstream file(name, std::ios::in); 67 | 68 | if (!file.is_open()) { 69 | std::cout << "Failed to open file: " << name << std::endl; 70 | return; 71 | } 72 | 73 | std::string line; 74 | 75 | uint64_t count = 0; 76 | 77 | uint64_t fails = 0; 78 | uint64_t passes = 0; 79 | uint64_t totalTime = 0; 80 | 81 | uint64_t totalNodes = 0; 82 | 83 | while (std::getline(file, line)) { 84 | if (count > max) { 85 | break; 86 | } 87 | 88 | EpdInfo info(line); 89 | 90 | for (const auto& depthnodes : info.fetch()) { 91 | std::cout << "\033[0m" << std::endl; 92 | std::cout << "testing: " << info.fen(); 93 | count++; 94 | 95 | chess::Board board(info.fen()); 96 | 97 | uint64_t nodes = 0; 98 | 99 | auto depth = depthnodes.depth(); 100 | auto required = depthnodes.nodes(); 101 | 102 | auto start = misc::tick(); 103 | testPositionBulk(board, depth, nodes); 104 | 105 | auto time_elapsed = misc::tick() - start; 106 | uint64_t speed = static_cast(1000.0f * nodes / (time_elapsed + 1)); 107 | 108 | std::string color = (nodes == required) ? "\033[32m" : "\033[31m"; 109 | if (nodes != required) { 110 | std::cout << "\r" << color << "#" << count << " D" << depth << " Failed: [" << info.fen() 111 | << "] Expected: " << required << " Got: " << nodes << " Speed: " << speed 112 | << " NPS" << std::endl; 113 | 114 | testPositionBulk(board, depth, nodes); 115 | std::cout << board << std::endl; 116 | 117 | fails++; 118 | } else { 119 | std::cout << "\r" << color << "#" << count << " D" << depth << " Passed: [" << info.fen() 120 | << "] Expected: " << required << " Got: " << nodes << " Speed: " << speed 121 | << " NPS" << std::endl; 122 | passes++; 123 | } 124 | 125 | totalTime += time_elapsed; 126 | totalNodes += nodes; 127 | 128 | std::cout << "\033[0m" << std::endl; 129 | } 130 | } 131 | 132 | std::cout << "Finished perft suite: " << name << std::endl; 133 | std::cout << "Total tests: " << count << std::endl; 134 | std::cout << "Total passes: " << passes << std::endl; 135 | std::cout << "Total fails: " << fails << std::endl; 136 | std::cout << "Total time: " << totalTime << "ms" << std::endl; 137 | std::cout << "Average speed: " << static_cast(1000.0f * totalNodes / (totalTime + 1)) 138 | << " NPS" << std::endl; 139 | } 140 | 141 | void startBulk(const std::string& fen, const int depth) { 142 | chess::Board board(fen); 143 | 144 | uint64_t nodes = 0; 145 | 146 | testPositionBulk(board, depth, nodes); 147 | 148 | std::cout << "Nodes: " << nodes << std::endl; 149 | } 150 | 151 | void startBulk(const chess::Board& brd, const int depth) { 152 | chess::Board board = brd; 153 | 154 | uint64_t nodes = 0; 155 | 156 | testPositionBulk(board, depth, nodes); 157 | 158 | std::cout << "Nodes: " << nodes << std::endl; 159 | } 160 | 161 | void bulkSpeedTest(const std::string_view& fen, const int depth) { 162 | std::cout << "(BULK) Starting speed test: " << fen << " D" << depth << std::endl; 163 | chess::Board board(fen); 164 | 165 | uint64_t nodes = 0; 166 | 167 | auto start_time = misc::tick(); 168 | 169 | testPositionBulk(board, depth, nodes); 170 | 171 | auto time_elapsed = misc::tick() - start_time; 172 | 173 | std::cout << "Nodes: " << nodes << std::endl; 174 | std::cout << "Time: " << time_elapsed << "ms" << std::endl; 175 | std::cout << "Speed: " << static_cast(1000.0f * nodes / (time_elapsed + 1)) << " NPS" 176 | << std::endl; 177 | } 178 | 179 | void bulkSpeedTest(const chess::Board& brd, const int depth) { 180 | std::cout << "(BULK) Starting speed test: " 181 | << " D" << depth << std::endl; 182 | chess::Board board = brd; 183 | 184 | uint64_t nodes = 0; 185 | 186 | auto start_time = misc::tick(); 187 | 188 | testPositionBulk(board, depth, nodes); 189 | 190 | auto time_elapsed = misc::tick() - start_time; 191 | 192 | std::cout << "Nodes: " << nodes << std::endl; 193 | std::cout << "Time: " << time_elapsed << "ms" << std::endl; 194 | std::cout << "Speed: " << static_cast(1000.0f * nodes / (time_elapsed + 1)) << " NPS" 195 | << std::endl; 196 | } 197 | 198 | } // namespace perft -------------------------------------------------------------------------------- /src/search.cpp: -------------------------------------------------------------------------------- 1 | #include "search/constants.hpp" 2 | 3 | #include "search/moveorder.hpp" 4 | #include "search/search.hpp" 5 | #include "search/searchinfo.hpp" 6 | #include "search/searchstack.hpp" 7 | #include "search/searchthread.hpp" 8 | 9 | #include "evaluation/evaluate.hpp" 10 | 11 | #include "chess/movegen.hpp" 12 | 13 | using namespace chess; 14 | using jet::types::Depth; 15 | using jet::types::Value; 16 | 17 | namespace jet { 18 | namespace search { 19 | 20 | namespace search_params { 21 | float lmr_base = 0.6422965; 22 | float lmr_division = 2.2050718; 23 | int lmr_see_margin = -104; 24 | 25 | int qs_see_ordering_threshold = -88; 26 | 27 | int nmp_base = 5; 28 | int nmp_depth_divisor = 5; 29 | int nmp_max_scaled_depth = 2; 30 | int nmp_divisor = 175; 31 | 32 | int rfp_margin = 68; 33 | int rfp_depth = 7; 34 | 35 | int lmp_depth = 5; 36 | int lmp_base = 2; 37 | int lmp_scalar = 3; 38 | 39 | int se_depth = 5; 40 | int se_depth_offset = 1; 41 | int singular_scalar = 3; 42 | int singular_divisor = 3; 43 | int singular_depth_divisor = 3; 44 | int singular_depth_intercept = 0; 45 | 46 | int asp_delta = 12; 47 | } // namespace search_params 48 | 49 | using namespace search_params; 50 | 51 | TT::Table TranspositionTable; 52 | 53 | std::array, 64> LmrTable; 54 | 55 | void init() { 56 | for (int depth = 1; depth < 64; depth++) { 57 | for (int played = 1; played < 64; played++) { 58 | LmrTable[depth][played] = lmr_base + std::log(depth) * std::log(played) / lmr_division; 59 | } 60 | } 61 | } 62 | 63 | template Value negamax(Value, Value, Depth, SearchThread&, SearchStack*); 64 | template Value negamax(Value, Value, Depth, SearchThread&, SearchStack*); 65 | template Value negamax(Value, Value, Depth, SearchThread&, SearchStack*); 66 | 67 | template 68 | Value qsearch(Value alpha, Value beta, SearchThread& st) { 69 | st.checkup(); 70 | 71 | Value bestscore = evaluation::evaluate(st); 72 | 73 | if (bestscore >= beta) { 74 | return bestscore; 75 | } 76 | 77 | if (bestscore > alpha) { 78 | alpha = bestscore; 79 | } 80 | 81 | Board& board = st.board(); 82 | Movelist movelist; 83 | MoveGen::legalmoves(board, movelist); 84 | MoveOrdering::capturesWithSee(board, movelist, qs_see_ordering_threshold); 85 | 86 | Value score = -constants::VALUE_INFINITY; 87 | 88 | for (int i = 0; i < movelist.size(); i++) { 89 | movelist.nextmove(i); 90 | 91 | const auto& move = movelist[i]; 92 | 93 | // SEE pruning 94 | if (move.score() < MoveOrdering::SEE_SCORE) { 95 | break; 96 | } 97 | 98 | st.makeMove(move); 99 | st.nodes++; 100 | 101 | score = -qsearch(-beta, -alpha, st); 102 | 103 | st.unmakeMove(move); 104 | 105 | if (st.stop()) { 106 | return 0; 107 | } 108 | 109 | if (score > bestscore) { 110 | bestscore = score; 111 | 112 | if (score > alpha) { 113 | alpha = score; 114 | 115 | if (score >= beta) { 116 | break; 117 | } 118 | } 119 | } 120 | } 121 | 122 | return bestscore; 123 | } 124 | 125 | template 126 | Value negamax(Value alpha, Value beta, Depth depth, SearchThread& st, SearchStack* ss) { 127 | if constexpr (nt == NodeType::PV || nt == NodeType::ROOT) { 128 | ss->pv_length = ss->ply; 129 | } 130 | 131 | st.checkup(); // check for time/nodes 132 | 133 | if (depth <= 0) { 134 | return qsearch(alpha, beta, st); 135 | } 136 | 137 | Board& board = st.board(); 138 | if constexpr (nt != NodeType::ROOT) { 139 | if (board.isRepetition()) { 140 | return 0; 141 | } 142 | 143 | if (ss->ply >= constants::PLY_MAX) { 144 | return evaluation::evaluate(st); 145 | } 146 | } 147 | 148 | constexpr bool isPvNode = (nt == NodeType::PV || nt == NodeType::ROOT); 149 | 150 | bool ttHit = false; 151 | const auto& entry = TranspositionTable.probe(board.hash(), ttHit); 152 | 153 | if (ss->excluded.isValid()) { 154 | ttHit = false; 155 | } 156 | 157 | if constexpr (!isPvNode) { 158 | if (ttHit && entry.depth() >= depth) { 159 | if ((entry.flag() == TT::Flag::UPPER && entry.score() <= alpha) || 160 | (entry.flag() == TT::Flag::LOWER && entry.score() >= beta) || (entry.flag() == TT::Flag::EXACT)) { 161 | return entry.score(); 162 | } 163 | } 164 | } 165 | 166 | const bool inCheck = board.isCheck(); 167 | const bool improving = !inCheck && !ss->excluded.isValid() && ss->static_eval > (ss - 2)->static_eval; 168 | 169 | depth += inCheck; 170 | if (inCheck || ss->excluded.isValid()) { 171 | ss->static_eval = 0; 172 | } else { 173 | ss->static_eval = evaluation::evaluate(st); 174 | } 175 | 176 | Value eval = ss->static_eval; 177 | 178 | if constexpr (!isPvNode) { 179 | if (ttHit) { 180 | eval = entry.score(); 181 | } 182 | 183 | if (!inCheck && !ss->excluded.isValid()) { 184 | // Reverse futility pruning 185 | if (depth <= rfp_depth && eval >= beta && eval - ((depth - improving) * rfp_margin) >= beta) { 186 | return eval; 187 | } 188 | 189 | // Null move pruning 190 | if (depth >= 3 && (ss - 1)->move != Move::nullmove() && board.hasNonPawnMat() && ss->static_eval >= beta && 191 | (!ttHit || entry.flag() != TT::Flag::UPPER || eval >= beta)) { 192 | Depth reduction = 193 | nmp_base + depth / nmp_depth_divisor + std::min(nmp_max_scaled_depth, (eval - beta) / nmp_divisor); 194 | 195 | board.makeNullMove(); 196 | ss->move = Move::nullmove(); 197 | (ss + 1)->ply = ss->ply + 1; 198 | 199 | Value score = -negamax(-beta, -beta + 1, depth - reduction, st, ss + 1); 200 | 201 | board.unmakeNullMove(); 202 | 203 | if (st.stop()) { 204 | return 0; 205 | } 206 | 207 | if (score >= beta) { 208 | if (score > constants::IS_MATE) { 209 | score = beta; 210 | } 211 | 212 | return score; 213 | } 214 | } 215 | } 216 | } 217 | 218 | Value oldAlpha = alpha; 219 | Value score = 0; 220 | Value bestscore = -constants::VALUE_INFINITY; 221 | 222 | Movelist movelist; 223 | MoveGen::legalmoves(board, movelist); 224 | MoveOrdering::all(movelist, st, ss, entry.move()); 225 | 226 | Movelist quietlist; 227 | 228 | Move bestmove = Move::none(); 229 | int movecount = 0; 230 | Depth extension = 0; 231 | 232 | bool hasNonPawnMat = board.hasNonPawnMat(); 233 | 234 | for (int i = 0; i < movelist.size(); i++) { 235 | movelist.nextmove(i); 236 | 237 | const auto& move = movelist[i]; 238 | const bool isQuiet = board.isQuiet(move); 239 | 240 | if (move == ss->excluded) { 241 | continue; 242 | } 243 | 244 | if constexpr (nt != NodeType::ROOT) { 245 | // Singular extensions 246 | if (depth >= se_depth && move == entry.move() && entry.flag() == TT::Flag::LOWER && 247 | std::abs(entry.score()) < constants::IS_MATE && entry.depth() >= depth - se_depth_offset && 248 | !ss->excluded.isValid()) { 249 | Value singularBeta = entry.score() - singular_scalar * depth / singular_divisor; 250 | Depth singularDepth = depth / singular_depth_divisor + singular_depth_intercept; 251 | 252 | ss->excluded = move; 253 | Value singularScore = negamax(singularBeta - 1, singularBeta, singularDepth, st, ss); 254 | ss->excluded = Move::none(); 255 | 256 | if (singularScore < singularBeta) { 257 | extension = 1; 258 | } 259 | } 260 | 261 | // Late move pruning 262 | if (isQuiet && bestscore > -constants::IS_MATE && hasNonPawnMat) { 263 | if (!inCheck && !isPvNode && depth <= lmp_depth && 264 | quietlist.size() >= (lmp_base + depth * lmp_scalar)) { 265 | break; 266 | } 267 | } 268 | } 269 | 270 | Depth newDepth = depth + extension; 271 | 272 | st.makeMove(move); 273 | (ss + 1)->ply = ss->ply + 1; 274 | ss->move = move; 275 | st.nodes++; 276 | movecount++; 277 | 278 | if (isQuiet) { 279 | quietlist.add(move); 280 | } 281 | 282 | if constexpr (nt == NodeType::ROOT) { 283 | if (movecount == 1 && depth == 1) { 284 | ss->updatePV(move, ss + 1); 285 | } 286 | } 287 | 288 | bool do_fullsearch = !isPvNode || movecount > 1; 289 | 290 | // Late move reductions 291 | if (!inCheck && movecount > 2 + 2 * isPvNode && depth >= 3) { 292 | Depth reduction = LmrTable[std::min(63, depth)][std::min(63, movecount)]; 293 | 294 | if (!isQuiet) { 295 | reduction -= MoveOrdering::see(board, move, lmr_see_margin * depth); 296 | } 297 | 298 | reduction += !isPvNode; 299 | reduction += !improving; 300 | 301 | reduction = std::clamp(reduction, 0, depth - 1); 302 | 303 | score = -negamax(-alpha - 1, -alpha, newDepth - reduction, st, ss + 1); 304 | 305 | do_fullsearch = score > alpha && reduction; 306 | } 307 | 308 | if (do_fullsearch) { 309 | score = -negamax(-alpha - 1, -alpha, newDepth - 1, st, ss + 1); 310 | } 311 | 312 | // Principal variation search 313 | if constexpr (isPvNode) { 314 | if (movecount == 1 || (score > alpha && score < beta)) { 315 | score = -negamax(-beta, -alpha, newDepth - 1, st, ss + 1); 316 | } 317 | } 318 | 319 | st.unmakeMove(move); 320 | 321 | // if we stop, return 0 322 | if (st.stop()) { 323 | return 0; 324 | } 325 | 326 | if (score > bestscore) { 327 | bestscore = score; 328 | 329 | if (score > alpha) { 330 | bestmove = move; 331 | alpha = score; 332 | 333 | if constexpr (isPvNode) { 334 | ss->updatePV(move, ss + 1); 335 | } 336 | 337 | if (score >= beta) { 338 | if (isQuiet) { 339 | ss->updateKiller(move); 340 | } 341 | 342 | break; 343 | } 344 | } 345 | } 346 | } 347 | 348 | if (alpha != oldAlpha) { 349 | if (bestscore >= beta && board.isQuiet(bestmove)) { 350 | MoveOrdering::updateHistory(st, quietlist, bestmove, depth); 351 | } 352 | } 353 | 354 | if (!movecount) { 355 | bestscore = inCheck ? -constants::IS_MATE + ss->ply : 0; 356 | } 357 | 358 | if (!ss->excluded.isValid()) { 359 | TT::Flag flag = bestscore >= beta ? TT::Flag::LOWER : (alpha != oldAlpha) ? TT::Flag::EXACT : TT::Flag::UPPER; 360 | TranspositionTable.store(board.hash(), bestmove, depth, bestscore, flag); 361 | } 362 | 363 | return bestscore; 364 | } 365 | 366 | Value aspiration_window(SearchThread& st, SearchStack* ss, Depth depth, Value prevEval) { 367 | Value score = 0; 368 | 369 | Value delta = asp_delta; 370 | 371 | Value alpha = -constants::VALUE_INFINITY; 372 | Value beta = constants::VALUE_INFINITY; 373 | 374 | Depth initial_depth = depth; 375 | 376 | if (depth > 3) { 377 | alpha = std::max(prevEval - delta, -constants::VALUE_INFINITY); 378 | beta = std::min(prevEval + delta, constants::VALUE_INFINITY); 379 | } 380 | 381 | while (true) { 382 | score = negamax(alpha, beta, depth, st, ss); 383 | 384 | if (st.stop()) { 385 | break; 386 | } 387 | 388 | if (score <= alpha) { 389 | beta = (alpha + beta) / 2; 390 | alpha = std::max(score - delta, -constants::VALUE_INFINITY); 391 | 392 | depth = initial_depth; 393 | } else if (score >= beta) { 394 | beta = std::min(score + delta, constants::VALUE_INFINITY); 395 | 396 | if (std::abs(beta) <= constants::IS_MATE / 2 && depth > 1) { 397 | depth--; 398 | } 399 | 400 | ss->updatePV(ss->pv[0], ss + 1); 401 | 402 | } else { 403 | break; 404 | } 405 | 406 | delta += delta / 2; 407 | } 408 | 409 | return score; 410 | } 411 | 412 | void search(SearchThread& st, SearchInfo& info) { 413 | st.start(); 414 | 415 | SearchStack::List stack; 416 | SearchStack* ss = SearchStack::init(stack); 417 | 418 | auto start = misc::tick(); 419 | 420 | Value score = 0; 421 | 422 | for (Depth d = 1; d <= info.depth(); d++) { 423 | score = aspiration_window(st, ss, d, score); 424 | 425 | if (st.stop()) { 426 | break; 427 | } 428 | 429 | if (info.shouldPrintInfo()) { 430 | auto time_elapsed = misc::tick() - start; 431 | 432 | std::cout << "info depth " << d << " score cp " << score; 433 | std::cout << " time " << time_elapsed; 434 | std::cout << " nodes " << st.nodes; 435 | std::cout << " nps " << static_cast(1000.0f * st.nodes / (time_elapsed + 1)); 436 | std::cout << " pv"; 437 | SearchStack::printPVs(ss); 438 | 439 | std::cout << std::endl; 440 | } 441 | } 442 | 443 | if (info.shouldPrintInfo()) { 444 | std::cout << "bestmove " << ss->pv[0] << std::endl; 445 | } 446 | } 447 | 448 | void search(SearchThread& st, Depth depth) { 449 | st.start(); 450 | 451 | SearchInfo info; 452 | info.setDepth(depth); 453 | info.setPrintInfo(false); 454 | 455 | search(st, info); 456 | } 457 | 458 | } // namespace search 459 | 460 | } // namespace jet 461 | -------------------------------------------------------------------------------- /src/search/constants.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.hpp" 4 | 5 | namespace jet { 6 | 7 | namespace constants { 8 | static inline constexpr types::Depth DEPTH_MAX = 63; 9 | static inline constexpr types::Depth PLY_MAX = 63; 10 | 11 | static inline constexpr types::Value VALUE_INFINITY = 32001; 12 | static inline constexpr types::Value VALUE_NONE = 32002; 13 | 14 | static inline constexpr types::Value IS_MATE = 30000; 15 | static inline constexpr types::Value IS_MATED = -IS_MATE; 16 | 17 | static inline constexpr types::Value IS_MATE_IN_PLY_MAX = (IS_MATE - PLY_MAX); 18 | static inline constexpr types::Value IS_MATED_IN_PLY_MAX = -IS_MATE_IN_PLY_MAX; 19 | 20 | static inline constexpr types::Depth SEARCH_STACK_SIZE = PLY_MAX + 10; 21 | 22 | } // namespace constants 23 | 24 | } // namespace jet 25 | -------------------------------------------------------------------------------- /src/search/history.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../chess/board.hpp" 4 | #include "../chess/moves.hpp" 5 | 6 | namespace jet { 7 | 8 | namespace search { 9 | 10 | class History { 11 | private: 12 | std::array, 64> m_table; 13 | 14 | public: 15 | static constexpr inline int16_t MAX_HISTORY = 2 << 13; 16 | 17 | auto& index(const chess::Board& board, const chess::Move& move) { 18 | const auto piece = board.at(move.from()); 19 | return m_table[static_cast(piece)][move.to()]; 20 | } 21 | 22 | auto& index(const chess::Board& board, const chess::Move& move) const { 23 | const auto piece = board.at(move.from()); 24 | return m_table[static_cast(piece)][move.to()]; 25 | } 26 | 27 | void clear() { 28 | std::memset(m_table.data(), 0, sizeof(m_table)); 29 | } 30 | 31 | static auto bonus(int depth) { 32 | return 32 * depth; 33 | } 34 | 35 | void update(const chess::Board& board, const chess::Move& move, int bonus) { 36 | auto& historyScore = index(board, move); 37 | 38 | historyScore += bonus - historyScore * std::abs(bonus) / MAX_HISTORY; 39 | } 40 | }; 41 | 42 | } // namespace search 43 | 44 | } // namespace jet 45 | -------------------------------------------------------------------------------- /src/search/moveorder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../chess/board.hpp" 4 | #include "../chess/moves.hpp" 5 | #include "searchstack.hpp" 6 | #include "searchthread.hpp" 7 | 8 | namespace jet { 9 | 10 | namespace search { 11 | 12 | class MoveOrdering { 13 | private: 14 | using Bitboard = chess::Bitboard; 15 | using Color = chess::Color; 16 | using PieceType = chess::PieceType; 17 | 18 | static int16_t _mvvlva(PieceType target, PieceType attacker) { 19 | return static_cast(target) * static_cast(10) - static_cast(attacker); 20 | } 21 | 22 | static int16_t _updateHistoryScore(int16_t& history, int16_t depth) { 23 | return history + depth * depth; 24 | } 25 | 26 | public: 27 | static constexpr inline int TT_MOVE_SCORE = 300000; 28 | static constexpr inline int SEE_SCORE = 100000; 29 | static constexpr inline int KILLER_1_SCORE = 80000; 30 | static constexpr inline int KILLER_2_SCORE = 70000; 31 | 32 | static void updateHistory(SearchThread& st, chess::Movelist& quiets, const chess::Move& move, int depth) { 33 | st.history.update(st.board(), move, History::bonus(depth)); 34 | 35 | for (auto& quiet : quiets) { 36 | if (quiet != move) { 37 | st.history.update(st.board(), quiet, -History::bonus(depth)); 38 | } 39 | } 40 | } 41 | 42 | static void captures(const chess::Board& board, chess::Movelist& movelist) { 43 | for (auto& move : movelist) { 44 | const auto attacker = board.pieceTypeAt(move.from()); 45 | const auto target = board.pieceTypeAt(move.to()); 46 | 47 | if (target != PieceType::NONE) { 48 | move.setScore(_mvvlva(target, attacker)); 49 | } 50 | } 51 | } 52 | 53 | static void capturesWithSee(const chess::Board& board, chess::Movelist& movelist, int threshold = 0) { 54 | for (auto& move : movelist) { 55 | const auto attacker = board.pieceTypeAt(move.from()); 56 | const auto target = board.pieceTypeAt(move.to()); 57 | 58 | if (target != PieceType::NONE) { 59 | move.setScore(see(board, move, threshold) * SEE_SCORE + _mvvlva(target, attacker)); 60 | } 61 | } 62 | } 63 | 64 | static void all(chess::Movelist& movelist, const SearchThread& st, const SearchStack* ss, 65 | const chess::Move& ttMove) { 66 | const auto& board = st.board(); 67 | for (auto& move : movelist) { 68 | const auto attacker = board.pieceTypeAt(move.from()); 69 | const auto target = board.pieceTypeAt(move.to()); 70 | 71 | if (move == ttMove) { 72 | move.setScore(TT_MOVE_SCORE); 73 | } else if (target != PieceType::NONE) { 74 | move.setScore(see(board, move, 0) * SEE_SCORE + _mvvlva(target, attacker)); 75 | } else if (move == ss->killers[0]) { 76 | move.setScore(KILLER_1_SCORE); 77 | } else if (move == ss->killers[1]) { 78 | move.setScore(KILLER_2_SCORE); 79 | } else { 80 | move.setScore(st.history.index(board, move)); 81 | } 82 | } 83 | } 84 | 85 | static bool see(const chess::Board& board, const chess::Move& move, const int threshold) { 86 | constexpr std::array values = {100, 320, 330, 500, 900, 20000, 0}; 87 | 88 | const auto from = move.from(); 89 | const auto to = move.to(); 90 | 91 | const auto target = board.pieceTypeAt(to); 92 | 93 | int score = values[static_cast(target)] - threshold; 94 | 95 | if (score < 0) { 96 | return false; 97 | } 98 | 99 | const auto attacker = board.pieceTypeAt(from); 100 | 101 | score -= values[static_cast(attacker)]; 102 | 103 | if (score >= threshold) { 104 | return true; 105 | } 106 | 107 | Bitboard occupied = board.occupied() ^ Bitboard(from) | Bitboard(to); 108 | Bitboard attackers = board.attackers(to, occupied) & occupied; 109 | 110 | Bitboard queens = board.bitboard(); 111 | Bitboard rooks = board.bitboard() | queens; 112 | Bitboard bishops = board.bitboard() | queens; 113 | 114 | Color st = ~board.colorOf(from); 115 | 116 | while (true) { 117 | attackers &= occupied; 118 | 119 | Bitboard ourAttackers = attackers & board.us(st); 120 | 121 | if (ourAttackers.empty()) { 122 | break; 123 | } 124 | 125 | int pt; 126 | for (pt = 0; pt < 6; pt++) { 127 | if (ourAttackers & board.bitboard(static_cast(pt))) { 128 | break; 129 | } 130 | } 131 | 132 | st = ~st; 133 | 134 | score = -score - 1 - values[pt]; 135 | 136 | if (score >= 0) { 137 | if (static_cast(pt) == PieceType::KING && (attackers & board.us(st))) { 138 | st = ~st; 139 | break; 140 | } 141 | } 142 | 143 | PieceType _pt = static_cast(pt); 144 | 145 | occupied ^= Bitboard((ourAttackers & board.bitboard(_pt)).lsb()); 146 | 147 | if (_pt == PieceType::PAWN || _pt == PieceType::BISHOP || _pt == PieceType::QUEEN) { 148 | attackers |= chess::Attacks::bishopAttacks(to, occupied) & bishops; 149 | } 150 | 151 | if (_pt == PieceType::ROOK || _pt == PieceType::QUEEN) { 152 | attackers |= chess::Attacks::rookAttacks(to, occupied) & rooks; 153 | } 154 | } 155 | 156 | return st != board.colorOf(from); 157 | } 158 | }; 159 | 160 | } // namespace search 161 | 162 | } // namespace jet 163 | -------------------------------------------------------------------------------- /src/search/search.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "tt.hpp" 4 | #include "types.hpp" 5 | 6 | namespace jet { 7 | namespace search { 8 | namespace search_params { 9 | extern float lmr_base; 10 | extern float lmr_division; 11 | extern int lmr_see_margin; 12 | 13 | extern int qs_see_ordering_threshold; 14 | 15 | extern int nmp_base; 16 | extern int nmp_depth_divisor; 17 | extern int nmp_max_scaled_depth; 18 | extern int nmp_divisor; 19 | 20 | extern int rfp_margin; 21 | extern int rfp_depth; 22 | 23 | extern int lmp_depth; 24 | extern int lmp_base; 25 | extern int lmp_scalar; 26 | 27 | extern int se_depth; 28 | extern int se_depth_offset; 29 | extern int singular_scalar; 30 | extern int singular_divisor; 31 | extern int singular_depth_divisor; 32 | extern int singular_depth_intercept; 33 | 34 | extern int asp_delta; 35 | } // namespace search_params 36 | 37 | extern TT::Table TranspositionTable; 38 | 39 | class SearchThread; 40 | class SearchInfo; 41 | struct SearchStack; 42 | 43 | enum class NodeType : uint8_t { ROOT, PV, NONPV }; 44 | 45 | void init(); 46 | void search(SearchThread&, SearchInfo&); 47 | void search(SearchThread&, types::Depth); 48 | 49 | template 50 | types::Value negamax(types::Value a, types::Value b, types::Depth, SearchThread&, SearchStack*); 51 | } // namespace search 52 | } // namespace jet -------------------------------------------------------------------------------- /src/search/searchinfo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "constants.hpp" 4 | #include "types.hpp" 5 | 6 | namespace jet { 7 | namespace search { 8 | class SearchInfo { 9 | public: 10 | inline constexpr types::Depth depth() const { 11 | return m_depth; 12 | } 13 | 14 | inline void setDepth(types::Depth d) { 15 | m_depth = d; 16 | } 17 | 18 | inline void setPrintInfo(bool b) { 19 | printInfo = b; 20 | } 21 | 22 | inline bool shouldPrintInfo() const { 23 | return printInfo; 24 | } 25 | 26 | private: 27 | types::Depth m_depth = constants::DEPTH_MAX; 28 | 29 | bool printInfo = true; 30 | }; 31 | } // namespace search 32 | } // namespace jet -------------------------------------------------------------------------------- /src/search/searchstack.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../chess/moves.hpp" 4 | #include "constants.hpp" 5 | #include 6 | 7 | namespace jet { 8 | 9 | namespace search { 10 | using PvTable = std::array; 11 | struct SearchStack { 12 | using List = std::array; 13 | using pointer = SearchStack*; 14 | 15 | types::Depth ply = 0; 16 | int pv_length = 0; 17 | types::Value static_eval = 0; 18 | chess::Move move = chess::Move::none(); 19 | chess::Move killers[2] = {chess::Move::none(), chess::Move::none()}; 20 | chess::Move excluded = chess::Move::none(); 21 | PvTable pv; 22 | 23 | void updateKiller(chess::Move move) { 24 | killers[1] = killers[0]; 25 | killers[0] = move; 26 | } 27 | 28 | void updatePV(chess::Move bestmove, SearchStack* next) { 29 | pv[ply] = bestmove; 30 | 31 | for (int i = ply + 1; i < next->pv_length; ++i) { 32 | pv[i] = next->pv[i]; 33 | } 34 | 35 | pv_length = next->pv_length; 36 | }; 37 | 38 | static pointer init(List& list) { 39 | for (auto& ss : list) { 40 | std::fill(ss.pv.begin(), ss.pv.end(), chess::Move::none()); 41 | } 42 | 43 | return list.data() + 7; 44 | } 45 | 46 | static void printPVs(pointer ss) { 47 | for (int i = 0; i < ss->pv_length; i++) { 48 | std::cout << " " << ss->pv[i]; 49 | } 50 | } 51 | }; 52 | 53 | } // namespace search 54 | 55 | } // namespace jet 56 | -------------------------------------------------------------------------------- /src/search/searchthread.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../chess/board.hpp" 4 | #include "../nnue/nnue.hpp" 5 | #include "constants.hpp" 6 | #include "history.hpp" 7 | #include "searchinfo.hpp" 8 | #include "timeman.hpp" 9 | 10 | namespace jet { 11 | namespace search { 12 | 13 | class SearchThread { 14 | public: 15 | uint64_t nodes = 0; 16 | History history; 17 | 18 | SearchThread() = default; 19 | SearchThread(const chess::Board& b) : m_board{b} { 20 | } 21 | 22 | auto& board() { 23 | return m_board; 24 | } 25 | 26 | const auto& board() const { 27 | return m_board; 28 | } 29 | 30 | void setFen(std::string_view fen) { 31 | m_board.setFen(fen); 32 | network.reset(); 33 | refresh(); 34 | } 35 | 36 | void start() { 37 | refresh(); 38 | stop_flag = false; 39 | nodes = 0; 40 | timeman.start(m_board.sideToMove()); 41 | 42 | history.clear(); 43 | } 44 | 45 | TimeManager& timeManager() { 46 | return timeman; 47 | } 48 | 49 | bool stop() const { 50 | return stop_flag && (timeman.timeset || timeman.nodeset()); 51 | } 52 | 53 | void checkup() { 54 | if (timeman.nodeset()) { 55 | stop_flag = timeman.checkNodes(nodes); 56 | return; 57 | } 58 | if ((nodes & 2047) == 0) { 59 | stop_flag = timeman.shouldStop(); 60 | } 61 | } 62 | 63 | template 64 | void makeMove(const chess::Move& move) { 65 | if constexpr (!updateNNUE) { 66 | m_board.makeMove(move); 67 | return; 68 | } 69 | 70 | const auto from = move.from(); 71 | const auto to = move.to(); 72 | const auto pieceType = m_board.pieceTypeAt(from); 73 | const bool is_capture = m_board.isCapture(move) && move.type() != chess::MoveType::CASTLING; 74 | const auto capturedType = m_board.pieceTypeAt(to); 75 | 76 | const auto side = m_board.sideToMove(); 77 | 78 | const auto kingSqWhite = m_board.kingSq(); 79 | const auto kingSqBlack = m_board.kingSq(); 80 | 81 | network.push(); 82 | 83 | if (pieceType == chess::PieceType::KING && 84 | (nnue::constants::KING_BUCKET[from ^ (static_cast(side) * 56)] != nnue::constants::KING_BUCKET[to ^ (static_cast(side) * 56)] || 85 | static_cast(from.file()) + static_cast(to.file()) == 7)) { 86 | m_board.makeMove(move); 87 | refresh(); 88 | 89 | return; 90 | } 91 | 92 | if (is_capture) { 93 | network.updateAccumulator(capturedType, ~side, to, kingSqWhite, kingSqBlack); 94 | } 95 | 96 | if (move.type() == chess::MoveType::CASTLING) { 97 | const auto castleSide = chess::CastlingRights::getCastlingSide(move.to(), move.from()); 98 | 99 | const auto rookTo = chess::CastlingRights::rookTo(side, castleSide); 100 | const auto kingTo = chess::CastlingRights::kingTo(side, castleSide); 101 | 102 | if (nnue::constants::KING_BUCKET[from ^ (static_cast(side) * 56)] != nnue::constants::KING_BUCKET[kingTo ^ (static_cast(side) * 56)] || 103 | static_cast(from.file()) + static_cast(kingTo.file()) == 7) { 104 | m_board.makeMove(move); 105 | refresh(); 106 | return; 107 | } 108 | 109 | network.updateAccumulator(pieceType, side, from, kingSqWhite, kingSqBlack); 110 | network.updateAccumulator(capturedType, side, to, kingSqWhite, kingSqBlack); 111 | network.updateAccumulator(pieceType, side, kingTo, kingSqWhite, kingSqBlack); 112 | network.updateAccumulator(capturedType, side, rookTo, kingSqWhite, kingSqBlack); 113 | 114 | m_board.makeMove(move); 115 | return; 116 | } 117 | 118 | if (move.type() == chess::MoveType::ENPASSANT) { 119 | network.updateAccumulator( 120 | chess::PieceType::PAWN, ~side, chess::Square(int(move.to()) ^ 8), kingSqWhite, kingSqBlack); 121 | } 122 | 123 | if (move.type() == chess::MoveType::PROMOTION) { 124 | network.updateAccumulator(chess::PieceType::PAWN, side, from, kingSqWhite, 125 | kingSqBlack); 126 | network.updateAccumulator(move.promoted(), side, to, kingSqWhite, kingSqBlack); 127 | 128 | m_board.makeMove(move); 129 | return; 130 | } 131 | 132 | network.updateAccumulator(pieceType, side, from, to, kingSqWhite, kingSqBlack); 133 | 134 | m_board.makeMove(move); 135 | } 136 | 137 | template 138 | void unmakeMove(const chess::Move& move) { 139 | m_board.unmakeMove(move); 140 | 141 | if constexpr (updateNNUE) { 142 | network.pull(); 143 | } 144 | } 145 | 146 | int32_t eval() { 147 | if (m_board.sideToMove() == chess::Color::WHITE) { 148 | return network.eval(); 149 | } else { 150 | return network.eval(); 151 | } 152 | } 153 | 154 | void refresh() { 155 | network.resetCurrentAccumulator(); 156 | 157 | chess::Bitboard pieces = m_board.all(); 158 | 159 | const chess::Square kingSqWhite = m_board.kingSq(); 160 | const chess::Square kingSqBlack = m_board.kingSq(); 161 | 162 | while (pieces.nonEmpty()) { 163 | const chess::Square sq = pieces.poplsb(); 164 | 165 | const chess::PieceType pieceType = m_board.pieceTypeAt(sq); 166 | const chess::Color side = m_board.colorOf(sq); 167 | 168 | network.updateAccumulator(pieceType, side, sq, kingSqWhite, kingSqBlack); 169 | } 170 | } 171 | 172 | private: 173 | chess::Board m_board; 174 | TimeManager timeman; 175 | bool stop_flag = false; 176 | 177 | nnue::Network network; 178 | }; 179 | } // namespace search 180 | } // namespace jet 181 | -------------------------------------------------------------------------------- /src/search/timeman.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../misc/utils.hpp" 4 | #include "types.hpp" 5 | 6 | namespace jet { 7 | namespace search { 8 | enum class TimeType : uint8_t { 9 | WTIME, 10 | BTIME, 11 | WINC, 12 | BINC, 13 | MOVETIME, 14 | }; 15 | class TimeManager { 16 | public: 17 | using Time = types::Time; 18 | 19 | static constexpr inline Time OVERHEAD = 50; 20 | 21 | TimeManager() = default; 22 | 23 | void start(chess::Color c) { 24 | m_start = misc::tick(); 25 | 26 | Time t = (c == chess::Color::WHITE) ? wtime : btime; 27 | Time inc = (c == chess::Color::WHITE) ? winc : binc; 28 | 29 | t -= OVERHEAD; 30 | 31 | if (movestogo) { 32 | stoptime = t / static_cast