From 5c7a688c943fd6b50db798462df890190ae5d06f Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Tue, 24 Feb 2026 16:54:50 +0700 Subject: [PATCH 01/76] Delete .gitmodules --- .gitmodules | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index f0e51e3..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "deps/doctest"] - path = deps/doctest - url = https://github.com/doctest/doctest.git From 2c24054c7b386a3c15574a565a6f7c061e06a4ee Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Tue, 24 Feb 2026 17:01:03 +0700 Subject: [PATCH 02/76] Add square mirror and flip functions, replace ASSUME with assert --- types.h | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/types.h b/types.h index e5e2ee1..d3085f7 100644 --- a/types.h +++ b/types.h @@ -75,23 +75,33 @@ enum Square : int8_t { SQUARE_ZERO = 0, SQUARE_NB = 64 + + operator int() const { + return static_cast(value); + } + Square operator()(int value) const { + assert(0<=s && s<64); + return static_cast(value); + } }; enum File : int8_t { FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB }; enum Rank : int8_t { RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB }; -constexpr bool is_valid(const Rank r, const File f) { return 0 <= r && r <= 7 && 0 <= f && f <= 7; } // unsigned already fix signedness +constexpr Square square_mirror(Square sq){return (Square)((int)sq^56);} +constexpr Square flip_sq(Square sq){return square_mirror(sq);} +constexpr bool is_valid(const Rank r, const File f) { return 0 <= r && r <= 7 && 0 <= f && f <= 7; } constexpr bool is_valid(const Square s) { return 0 <= s && s < 64; } constexpr File file_of(Square s) { - ASSUME(0 <= s && s < 64); + assert(0 <= s && s < 64); return File(s & 7); } constexpr Rank rank_of(Square s) { - ASSUME(0 <= s && s < 64); + assert(0 <= s && s < 64); return Rank(s >> 3); } constexpr Square make_sq(Rank r, File f) { - ASSUME(0 <= r && r <= 7 && 0 <= f && f <= 7); + assert(0 <= r && r <= 7 && 0 <= f && f <= 7); return static_cast(static_cast(r * 8 + f)); } constexpr Square make_sq(File f, Rank r) { From edb8d35eeb140bdb98aee5cd45d05309a500cc5c Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Tue, 24 Feb 2026 17:12:55 +0700 Subject: [PATCH 03/76] Replace HeapAllocatedValueList with preallocated std::vector --- position.h | 98 +++--------------------------------------------------- 1 file changed, 4 insertions(+), 94 deletions(-) diff --git a/position.h b/position.h index 841d8a2..65c362f 100644 --- a/position.h +++ b/position.h @@ -38,97 +38,6 @@ template struct alignas(64) HistoryEntry { // implementation-specific implementations goes here }; -template class HeapAllocatedValueList { - private: - constexpr static int ALIGNMENT = 64; - - public: - using size_type = std::size_t; - inline HeapAllocatedValueList() { - values_ = reinterpret_cast(::operator new(MaxSize * sizeof(T), std::align_val_t{ ALIGNMENT })); - assert(values_); - } - inline ~HeapAllocatedValueList() { ::operator delete(values_, std::align_val_t{ ALIGNMENT }); } - - HeapAllocatedValueList(const HeapAllocatedValueList &other) : size_(other.size_) { - if (values_) - ::operator delete(values_, std::align_val_t{ ALIGNMENT }); - values_ = reinterpret_cast(::operator new(MaxSize * sizeof(T), std::align_val_t{ ALIGNMENT })); - assert(values_); - std::copy(other.values_, other.values_ + size_, values_); - } - - HeapAllocatedValueList &operator=(const HeapAllocatedValueList &other) { - if (this != &other) { - ::operator delete(values_, std::align_val_t{ ALIGNMENT }); - // Allocate new memory and copy - T *new_values = reinterpret_cast(::operator new(MaxSize * sizeof(T), std::align_val_t{ ALIGNMENT })); - assert(new_values); - std::copy(other.values_, other.values_ + other.size_, new_values); - - // Free old memory - free(values_); - - // Assign new data - values_ = new_values; - size_ = other.size_; - } - return *this; - } - - HeapAllocatedValueList(HeapAllocatedValueList &&other) noexcept : size_(other.size_) { - ::operator delete(values_, std::align_val_t{ ALIGNMENT }); - values_ = reinterpret_cast(::operator new(MaxSize * sizeof(T), std::align_val_t{ ALIGNMENT })); - assert(values_); - std::copy(other.values_, other.values_ + size_, values_); - } - - HeapAllocatedValueList &operator=(HeapAllocatedValueList &&other) noexcept { - if (this != &other) { - ::operator delete(values_, std::align_val_t{ ALIGNMENT }); - values_ = reinterpret_cast(::operator new(MaxSize * sizeof(T), std::align_val_t{ ALIGNMENT })); - assert(values_); - std::copy(other.values_, other.values_ + size_, values_); - } - return *this; - } - inline size_type size() const { return size_; } - - inline void push_back(const T &value) { - assert(size_ < MaxSize); - - values_[size_++] = value; - } - inline void clear() { size_ = 0; } - inline const T &pop() { - assert(size_ > 0); - return values_[--size_]; // always safe due to mask - } - - inline void pop_back() { size_ -= (size_ > 0) ? 1 : 0; } - - inline const T &front() const { return (size_ > 0) ? values_[0] : T{}; } - - inline T &operator[](int index) { - assert(index < MaxSize); // relax the conditions, it's BRANCHLESS so forgive - size_type i = static_cast(index); - return values_[i]; - } - - inline T &operator[](int index) const { - assert(index < MaxSize); // relax the conditions, it's BRANCHLESS so forgive - size_type i = static_cast(index); - return values_[i]; - } - inline const T *begin() const { return values_; } - inline T *data() { return values_; } - inline const T *end() const { return values_ + size_; } - size_type size_ = 0; - - private: - T *values_ = nullptr; -}; - enum class CheckType { NO_CHECK, DIRECT_CHECK, DISCOVERY_CHECK }; enum class MoveGenType : uint16_t { NONE = 0, @@ -163,7 +72,7 @@ template current_state; // Move history stack - HeapAllocatedValueList, 6144> history; + std::vector> history; Bitboard _rook_pin; Bitboard _bishop_pin; Bitboard _checkers; @@ -488,6 +397,7 @@ template 99 || is_repetition(ply); } // Tests whether there has been at least one repetition // of positions since the last capture or pawn move. @@ -652,7 +562,7 @@ template Date: Tue, 24 Feb 2026 17:24:36 +0700 Subject: [PATCH 04/76] Update position.h --- position.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/position.h b/position.h index 65c362f..e48dd59 100644 --- a/position.h +++ b/position.h @@ -421,6 +421,8 @@ template ) return piece_of(piece_at(sq)); + else if constexpr (std::is_same_v) + return type_of(piece_at(sq)); else return piece_at(sq); } @@ -445,8 +447,8 @@ template Date: Tue, 24 Feb 2026 17:24:47 +0700 Subject: [PATCH 05/76] Delete repetition.h --- repetition.h | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 repetition.h diff --git a/repetition.h b/repetition.h deleted file mode 100644 index 479a59c..0000000 --- a/repetition.h +++ /dev/null @@ -1,5 +0,0 @@ -#include "types.h" -namespace chess { -extern const std::array cuckoo; -extern const std::array cuckooMove; -} // namespace chess \ No newline at end of file From 9679bc64be356aa7ee675013acd2d44fdbd2f031 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Tue, 24 Feb 2026 17:25:23 +0700 Subject: [PATCH 06/76] Remove coverage reporting configuration Removed coverage reporting options and related logic from CMakeLists.txt. --- CMakeLists.txt | 108 +------------------------------------------------ 1 file changed, 1 insertion(+), 107 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d5124e9..a825409 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(chesslib LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -option(ENABLE_COVERAGE "Enable coverage reporting" OFF) + # --- Compiler tuning --- if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") @@ -32,10 +32,6 @@ elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") add_compile_options(/constexpr:steps2000000000 /constexpr:depth1024 ${ARCH_FLAG}) endif() -if(ENABLE_COVERAGE) - set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE) - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF) -endif() add_compile_definitions(GENERATE_AT_RUNTIME) if(CMAKE_BUILD_TYPE MATCHES "Debug") add_compile_definitions(_DEBUG) @@ -69,105 +65,3 @@ target_link_libraries(NonImportantTests PRIVATE chesslib) target_include_directories(NonImportantTests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${doctest_SOURCE_DIR}) add_test(NAME test_core COMMAND test_chess) add_test(NAME api_tests COMMAND NonImportantTests) -if(ENABLE_COVERAGE) - - if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - foreach(t chesslib test_chess NonImportantTests) - target_compile_options(${t} PRIVATE - --coverage - -O0 - ) - target_link_options(${t} PRIVATE - --coverage - ) - endforeach() - - find_program(GCOVR gcovr REQUIRED) - - add_custom_target(coverage - COMMAND ${CMAKE_COMMAND} -E make_directory coverage - COMMAND ${CMAKE_CTEST_COMMAND} - COMMAND ${GCOVR} - -r ${CMAKE_SOURCE_DIR} - --html - --html-details - -o coverage/index.html - --exclude-directories tests - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMENT "Generating GCC coverage report" - ) - elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - foreach(t chesslib test_chess NonImportantTests) - target_compile_options(${t} PRIVATE - -fprofile-instr-generate - -fcoverage-mapping - -O0 - ) - target_link_options(${t} PRIVATE - -fprofile-instr-generate - ) - endforeach() - - if(WIN32) - - find_program(LLVM_PROFDATA llvm-profdata REQUIRED) - find_program(LLVM_COV llvm-cov REQUIRED) - - add_custom_target(coverage - COMMAND ${CMAKE_COMMAND} -E make_directory coverage - - COMMAND ${CMAKE_COMMAND} -E env - LLVM_PROFILE_FILE=coverage/coverage.profraw - CTEST_PARALLEL_LEVEL=1 - ${CMAKE_CTEST_COMMAND} - - COMMAND ${LLVM_PROFDATA} merge - -sparse - coverage/coverage.profraw - -o coverage/coverage.profdata - - COMMAND ${LLVM_COV} show - $ - $ - -instr-profile=coverage/coverage.profdata - -format=html - -output-dir=coverage - -ignore-filename-regex="tests|doctest" - -Xdemangler=llvm-cxxfilt - - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMENT "Generating Clang coverage report (Windows)" - ) - else() - - find_program(LLVM_PROFDATA llvm-profdata REQUIRED) - find_program(LLVM_COV llvm-cov REQUIRED) - - add_custom_target(coverage - COMMAND ${CMAKE_COMMAND} -E make_directory coverage - - COMMAND ${CMAKE_COMMAND} -E env - LLVM_PROFILE_FILE=coverage/%m.profraw - ${CMAKE_CTEST_COMMAND} - - COMMAND ${LLVM_PROFDATA} merge - -sparse - coverage - -o coverage/coverage.profdata - - COMMAND ${LLVM_COV} show - $ - $ - -instr-profile=coverage/coverage.profdata - -format=html - -output-dir=coverage - -ignore-filename-regex="tests|doctest" - -Xdemangler=c++filt - - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMENT "Generating Clang coverage report" - ) - endif() - endif() - -endif() \ No newline at end of file From 51dae1f1faf06cb236e84aaca0a66a8d055e4579 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Tue, 24 Feb 2026 17:44:18 +0700 Subject: [PATCH 07/76] Update types.h --- types.h | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/types.h b/types.h index d3085f7..077dfc5 100644 --- a/types.h +++ b/types.h @@ -62,7 +62,7 @@ namespace chess { using Bitboard = uint64_t; using Key = uint64_t; // clang-format off -enum Square : int8_t { +enum Square { SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1, SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2, SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3, @@ -75,14 +75,6 @@ enum Square : int8_t { SQUARE_ZERO = 0, SQUARE_NB = 64 - - operator int() const { - return static_cast(value); - } - Square operator()(int value) const { - assert(0<=s && s<64); - return static_cast(value); - } }; enum File : int8_t { FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB }; From 9731a316da00be7f89b189e05f6ab88f2694aa3f Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Tue, 24 Feb 2026 17:44:28 +0700 Subject: [PATCH 08/76] Update fwd_decl.h --- fwd_decl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fwd_decl.h b/fwd_decl.h index 7fbf593..aff3def 100644 --- a/fwd_decl.h +++ b/fwd_decl.h @@ -9,7 +9,7 @@ template struct is_piece_enum : std::false_type {} template struct is_piece_enum> : std::true_type {}; enum CastlingRights : int8_t; -enum Square : int8_t; +enum Square; enum Direction : int8_t; enum MoveType : uint16_t; enum File : int8_t; @@ -25,4 +25,4 @@ using Movelist = ValueList; enum class PolyglotPiece : uint8_t; enum class EnginePiece : uint8_t; enum class ContiguousMappingPiece : uint8_t; -} // namespace chess \ No newline at end of file +} // namespace chess From ad19011f92678ce6fb7f97f574bb7d40c382cb23 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Tue, 24 Feb 2026 17:44:50 +0700 Subject: [PATCH 09/76] Update position.h --- position.h | 1 + 1 file changed, 1 insertion(+) diff --git a/position.h b/position.h index e48dd59..4946a88 100644 --- a/position.h +++ b/position.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace chess { template struct alignas(64) HistoryEntry { From d4624ff382f85f7e503a970f1a277ffa737cc8ae Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Tue, 24 Feb 2026 17:47:15 +0700 Subject: [PATCH 10/76] Refactor chess namespace and include forward declarations --- moves_io.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/moves_io.h b/moves_io.h index e0b2435..e7acaf3 100644 --- a/moves_io.h +++ b/moves_io.h @@ -3,11 +3,8 @@ #include #include #include -namespace chess { -class Move; -enum Square : int8_t; -template class _Position; -namespace uci { +#include "fwd_decl.h" +namespace chess::uci{ std::string moveToUci(Move move, bool chess960 = false); std::string squareToString(Square sq); @@ -32,5 +29,4 @@ template Move parseSan(const _Position &pos, std::string_view uci, bool remove_illegals = false); template std::string moveToSan(const _Position &pos, Move move, bool long_ = false, bool suffix = true); -} // namespace uci -} // namespace chess +} // namespace chess::uci From 36f74bb025a0b338e89f55dcbb06919de02e031b Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Tue, 24 Feb 2026 17:48:41 +0700 Subject: [PATCH 11/76] Refactor is_zeroing and at functions in position.h --- position.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/position.h b/position.h index 4946a88..ca32b88 100644 --- a/position.h +++ b/position.h @@ -405,7 +405,7 @@ template (mv.from_sq()) == PAWN; } std::string fen() const; inline uint8_t halfmoveClock() const { return current_state.halfMoveClock; } inline uint16_t fullmoveNumber() const { return current_state.fullMoveNumber; } @@ -418,12 +418,12 @@ template &state() const { return current_state; } uint64_t zobrist() const; inline PieceC piece_at(Square sq) const { return piece_on(sq); } - template inline PieceC at(Square sq) const { + template inline T at(Square sq) const { assert(chess::is_valid(sq)); if constexpr (std::is_same_v) return piece_of(piece_at(sq)); else if constexpr (std::is_same_v) - return type_of(piece_at(sq)); + return color_of(piece_at(sq)); else return piece_at(sq); } From f63701ddbed4b1dcf1ef14728f64f3120f336745 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Tue, 24 Feb 2026 17:49:47 +0700 Subject: [PATCH 12/76] Update position.cpp --- position.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/position.cpp b/position.cpp index eccf198..75cd91f 100644 --- a/position.cpp +++ b/position.cpp @@ -179,7 +179,7 @@ template template void _Position= 4) { - auto *stp = &history[history.size_ - 1]; + auto *stp = &history[history.size() - 1]; stp -= 1; for (int i = 4; i <= end; i += 2) { stp -= 2; From f772fe686a425b2d8f4a57e13f74e5bad8fe20d0 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Tue, 24 Feb 2026 17:51:20 +0700 Subject: [PATCH 13/76] fix unassignable array --- position.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/position.h b/position.h index ca32b88..d4df649 100644 --- a/position.h +++ b/position.h @@ -565,7 +565,7 @@ template Date: Wed, 25 Feb 2026 11:33:24 +0700 Subject: [PATCH 14/76] Update types.h --- types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types.h b/types.h index 077dfc5..1cc991d 100644 --- a/types.h +++ b/types.h @@ -62,7 +62,7 @@ namespace chess { using Bitboard = uint64_t; using Key = uint64_t; // clang-format off -enum Square { +enum Square : int8_t { SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1, SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2, SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3, From d1c489ff7fe9f0d19446f0de030eaca4521893b7 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 11:33:51 +0700 Subject: [PATCH 15/76] prevent MSVC extension --- fwd_decl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fwd_decl.h b/fwd_decl.h index aff3def..1d43a7b 100644 --- a/fwd_decl.h +++ b/fwd_decl.h @@ -9,7 +9,7 @@ template struct is_piece_enum : std::false_type {} template struct is_piece_enum> : std::true_type {}; enum CastlingRights : int8_t; -enum Square; +enum Square : int8_t; enum Direction : int8_t; enum MoveType : uint16_t; enum File : int8_t; From c737a763c74338d6f33720fff9541c65ee5bf399 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 11:48:43 +0700 Subject: [PATCH 16/76] Use constexpr START_FEN in _Position constructor --- position.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/position.h b/position.h index d4df649..924366e 100644 --- a/position.h +++ b/position.h @@ -95,6 +95,7 @@ template inline void legals(Movelist &out) const { @@ -397,7 +398,7 @@ template Date: Wed, 25 Feb 2026 11:51:53 +0700 Subject: [PATCH 17/76] fix compile error --- position.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/position.h b/position.h index 924366e..c85bf1a 100644 --- a/position.h +++ b/position.h @@ -95,7 +95,7 @@ template inline void legals(Movelist &out) const { From 3c7b880d5cae0527e731182eef340f6547bda1b1 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 11:53:21 +0700 Subject: [PATCH 18/76] Overload set_fen with default parameter value --- position.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/position.h b/position.h index c85bf1a..3fb6c2f 100644 --- a/position.h +++ b/position.h @@ -431,7 +431,8 @@ template Date: Wed, 25 Feb 2026 11:55:12 +0700 Subject: [PATCH 19/76] Change START_FEN to use auto type deduction --- position.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/position.h b/position.h index 3fb6c2f..8d35332 100644 --- a/position.h +++ b/position.h @@ -95,7 +95,7 @@ template inline void legals(Movelist &out) const { From b02b787d8a5b3c9fbc0ff28f23e5e5e9b237598a Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 12:00:06 +0700 Subject: [PATCH 20/76] Update moves_io.cpp --- moves_io.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/moves_io.cpp b/moves_io.cpp index 6a5cf7f..4d811c4 100644 --- a/moves_io.cpp +++ b/moves_io.cpp @@ -89,9 +89,17 @@ template Move uciToMove(const _Position &pos, std THROW_IF_EXCEPTIONS_ON(IllegalMoveException("source need to be a existing piece, got nothing")); return Move::NO_MOVE; } - if (!pos.chess960() && pt == KING && square_distance(target, source) == 2) { - target = make_sq(target > source ? File::FILE_H : File::FILE_A, rank_of(source)); - return Move::make(source, target); + // castling in chess960 + if (board.chess960() && pt == PieceType::KING && board.at(target) == PieceType::ROOK && + board.at(target) == board.sideToMove()) { + return Move::make(source, target); + } + + // convert to king captures rook + // in chess960 the move should be sent as king captures rook already! + if (!board.chess960() && pt == PieceType::KING && square_distance(target, source) == 2) { + target = make_sq(target > source ? File::FILE_H : File::FILE_A, source.rank()); + return Move::make(source, target); } // en passant if (pt == PAWN && target == pos.enpassantSq()) { From 3c477e5a892b3217f307ff5866da497728fc0034 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 12:50:34 +0700 Subject: [PATCH 21/76] Improve castling move logic for chess960 Refactor castling move handling for chess960 support. --- moves_io.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/moves_io.cpp b/moves_io.cpp index 4d811c4..1939c44 100644 --- a/moves_io.cpp +++ b/moves_io.cpp @@ -35,7 +35,10 @@ std::string moveToUci(Move mv, bool chess960) { // To square, special: castlings switch (mv.type_of()) { case CASTLING: - switch (mv.to_sq()) { + if (chess960) + move += squareToString(mv.to_sq()); + else + switch (mv.to_sq()) { case SQ_H1: move += "g1"; // White kingside castling break; @@ -49,9 +52,6 @@ std::string moveToUci(Move mv, bool chess960) { move += "c8"; // black queenside castling break; default: - if (chess960) - move += squareToString(mv.to_sq()); - else { #if defined(_DEBUG) || !defined(NDEBUG) assert(false && "this isn't chess960"); #else From c2b39afba91f8064a907d3d038807602aa4fbcd2 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 12:58:25 +0700 Subject: [PATCH 22/76] Fix formatting in castling case of move handling --- moves_io.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/moves_io.cpp b/moves_io.cpp index 1939c44..04e3f2f 100644 --- a/moves_io.cpp +++ b/moves_io.cpp @@ -37,7 +37,7 @@ std::string moveToUci(Move mv, bool chess960) { case CASTLING: if (chess960) move += squareToString(mv.to_sq()); - else + else{ switch (mv.to_sq()) { case SQ_H1: move += "g1"; // White kingside castling @@ -60,6 +60,7 @@ std::string moveToUci(Move mv, bool chess960) { #endif } } + } break; case PROMOTION: move += squareToString(mv.to_sq()); From 20e44ddcfa64020a481ec66bb69a36b0427b0ec1 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 12:58:45 +0700 Subject: [PATCH 23/76] Add push event trigger to clang-format workflow --- .github/workflows/clang-format.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 82b3350..f7549fc 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -3,7 +3,7 @@ name: clang-format on: pull_request: branches: [ "main" ] - + push: jobs: format: if: github.actor != 'github-actions[bot]' @@ -30,4 +30,4 @@ jobs: git config user.name "GitHub Actions" git commit -am "Apply clang-format" git push - fi \ No newline at end of file + fi From 0c59bf0b80e08efe7d2e2db51c217d4aaae2b7a2 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 25 Feb 2026 05:59:07 +0000 Subject: [PATCH 24/76] Apply clang-format --- moves_io.cpp | 52 ++++++++++++++++++++++++++-------------------------- moves_io.h | 4 ++-- position.h | 2 +- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/moves_io.cpp b/moves_io.cpp index 04e3f2f..bf06030 100644 --- a/moves_io.cpp +++ b/moves_io.cpp @@ -37,21 +37,21 @@ std::string moveToUci(Move mv, bool chess960) { case CASTLING: if (chess960) move += squareToString(mv.to_sq()); - else{ + else { switch (mv.to_sq()) { - case SQ_H1: - move += "g1"; // White kingside castling - break; - case SQ_A1: - move += "c1"; // white queenside castling - break; - case SQ_H8: - move += "g8"; // black kingside castling - break; - case SQ_A8: - move += "c8"; // black queenside castling - break; - default: + case SQ_H1: + move += "g1"; // White kingside castling + break; + case SQ_A1: + move += "c1"; // white queenside castling + break; + case SQ_H8: + move += "g8"; // black kingside castling + break; + case SQ_A8: + move += "c8"; // black queenside castling + break; + default: #if defined(_DEBUG) || !defined(NDEBUG) assert(false && "this isn't chess960"); #else @@ -60,17 +60,17 @@ std::string moveToUci(Move mv, bool chess960) { #endif } } - } - break; - case PROMOTION: - move += squareToString(mv.to_sq()); - move += PieceTypeChar[mv.promotion_type()]; - break; - default: - move += squareToString(mv.to_sq()); - break; } - return move; + break; +case PROMOTION: + move += squareToString(mv.to_sq()); + move += PieceTypeChar[mv.promotion_type()]; + break; +default: + move += squareToString(mv.to_sq()); + break; +} +return move; } template Move uciToMove(const _Position &pos, std::string_view uci) { if (uci.length() < 4) { @@ -99,8 +99,8 @@ template Move uciToMove(const _Position &pos, std // convert to king captures rook // in chess960 the move should be sent as king captures rook already! if (!board.chess960() && pt == PieceType::KING && square_distance(target, source) == 2) { - target = make_sq(target > source ? File::FILE_H : File::FILE_A, source.rank()); - return Move::make(source, target); + target = make_sq(target > source ? File::FILE_H : File::FILE_A, source.rank()); + return Move::make(source, target); } // en passant if (pt == PAWN && target == pos.enpassantSq()) { diff --git a/moves_io.h b/moves_io.h index e7acaf3..5dccb74 100644 --- a/moves_io.h +++ b/moves_io.h @@ -1,10 +1,10 @@ #pragma once +#include "fwd_decl.h" #include #include #include #include -#include "fwd_decl.h" -namespace chess::uci{ +namespace chess::uci { std::string moveToUci(Move move, bool chess960 = false); std::string squareToString(Square sq); diff --git a/position.h b/position.h index 8d35332..daa76ae 100644 --- a/position.h +++ b/position.h @@ -95,7 +95,7 @@ template inline void legals(Movelist &out) const { From fe6f9f285bbc4aafbefadae994a83881c8940e94 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 13:00:15 +0700 Subject: [PATCH 25/76] fix compile errors --- moves_io.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moves_io.cpp b/moves_io.cpp index bf06030..2771ab0 100644 --- a/moves_io.cpp +++ b/moves_io.cpp @@ -34,7 +34,7 @@ std::string moveToUci(Move mv, bool chess960) { move += squareToString(mv.from_sq()); // To square, special: castlings switch (mv.type_of()) { - case CASTLING: + case CASTLING:{ if (chess960) move += squareToString(mv.to_sq()); else { From 31def464cee48d72e545b19c9475c9912a517cc4 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 25 Feb 2026 06:00:41 +0000 Subject: [PATCH 26/76] Apply clang-format --- moves_io.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/moves_io.cpp b/moves_io.cpp index 2771ab0..db541cc 100644 --- a/moves_io.cpp +++ b/moves_io.cpp @@ -34,7 +34,7 @@ std::string moveToUci(Move mv, bool chess960) { move += squareToString(mv.from_sq()); // To square, special: castlings switch (mv.type_of()) { - case CASTLING:{ + case CASTLING: { if (chess960) move += squareToString(mv.to_sq()); else { @@ -60,17 +60,16 @@ std::string moveToUci(Move mv, bool chess960) { #endif } } + } break; + case PROMOTION: + move += squareToString(mv.to_sq()); + move += PieceTypeChar[mv.promotion_type()]; + break; + default: + move += squareToString(mv.to_sq()); + break; } - break; -case PROMOTION: - move += squareToString(mv.to_sq()); - move += PieceTypeChar[mv.promotion_type()]; - break; -default: - move += squareToString(mv.to_sq()); - break; -} -return move; + return move; } template Move uciToMove(const _Position &pos, std::string_view uci) { if (uci.length() < 4) { From d01601a8e9ee8efe161c4663cb3e5635d520a35d Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 13:02:53 +0700 Subject: [PATCH 27/76] Update moves_io.cpp --- moves_io.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/moves_io.cpp b/moves_io.cpp index db541cc..e819f2c 100644 --- a/moves_io.cpp +++ b/moves_io.cpp @@ -90,15 +90,15 @@ template Move uciToMove(const _Position &pos, std return Move::NO_MOVE; } // castling in chess960 - if (board.chess960() && pt == PieceType::KING && board.at(target) == PieceType::ROOK && - board.at(target) == board.sideToMove()) { + if (pos.chess960() && pt == PieceType::KING && board.at(target) == PieceType::ROOK && + pos.at(target) == board.sideToMove()) { return Move::make(source, target); } // convert to king captures rook // in chess960 the move should be sent as king captures rook already! - if (!board.chess960() && pt == PieceType::KING && square_distance(target, source) == 2) { - target = make_sq(target > source ? File::FILE_H : File::FILE_A, source.rank()); + if (!pos.chess960() && pt == PieceType::KING && square_distance(target, source) == 2) { + target = make_sq(target > source ? File::FILE_H : File::FILE_A, rank_of(source)); return Move::make(source, target); } // en passant @@ -112,9 +112,9 @@ template Move uciToMove(const _Position &pos, std if (promotion == NO_PIECE_TYPE || promotion == KING || promotion == PAWN) { #if defined(_DEBUG) || !defined(NDEBUG) - assert(false && "promotions: NRBQ"); + assert(false && "promotions: [NRBQ]"); #else - THROW_IF_EXCEPTIONS_ON(IllegalMoveException("promotions: NRBQ")); + THROW_IF_EXCEPTIONS_ON(IllegalMoveException("promotions: [NRBQ]")); #endif return Move::NO_MOVE; } From 90536563cc26d95efa646d4c51876c2b6fd31eb3 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 13:04:13 +0700 Subject: [PATCH 28/76] Add GitHub Actions workflow for CMake builds --- .github/workflows/try_compile.yml | 69 +++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 .github/workflows/try_compile.yml diff --git a/.github/workflows/try_compile.yml b/.github/workflows/try_compile.yml new file mode 100644 index 0000000..d9061c8 --- /dev/null +++ b/.github/workflows/try_compile.yml @@ -0,0 +1,69 @@ +name: CMake on multiple platforms + +on: + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + build_type: [Debug, Release] + c_compiler: [gcc, clang, cl] + + include: + # Windows + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + - os: windows-latest + c_compiler: clang + cpp_compiler: clang++ + - os: windows-latest + c_compiler: gcc + cpp_compiler: g++ + + # Linux + - os: ubuntu-latest + c_compiler: gcc + cpp_compiler: g++ + - os: ubuntu-latest + c_compiler: clang + cpp_compiler: clang++ + + exclude: + - os: ubuntu-latest + c_compiler: cl + + steps: + - uses: actions/checkout@v4 + + - name: Set build dir + id: vars + shell: bash + run: echo "dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + + - name: Configure CMake + shell: bash + run: | + CXX_FLAGS="" + LINK_FLAGS="" + if [[ "${{ matrix.os }}" == "ubuntu-latest" && "${{ matrix.build_type }}" == "Debug" ]]; then + CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g" + LINK_FLAGS="-fsanitize=address,undefined" + fi + + cmake -B "${{ steps.vars.outputs.dir }}" \ + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} \ + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} \ + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ + -DCMAKE_CXX_FLAGS_DEBUG="$CXX_FLAGS" \ + -DCMAKE_EXE_LINKER_FLAGS="$LINK_FLAGS" \ + -S "${{ github.workspace }}" + + - name: Build + run: cmake --build ${{ steps.vars.outputs.dir }} --config ${{ matrix.build_type }} From 2f4c12616e7eb7ad25689c70270b3619422536bd Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 13:05:11 +0700 Subject: [PATCH 29/76] Update GitHub Actions to trigger on push and pull_request --- .github/workflows/try_compile.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/try_compile.yml b/.github/workflows/try_compile.yml index d9061c8..ac6bf61 100644 --- a/.github/workflows/try_compile.yml +++ b/.github/workflows/try_compile.yml @@ -1,8 +1,8 @@ name: CMake on multiple platforms on: - pull_request: - branches: [ "main" ] + push: + pull_request: jobs: build: From fb6f4b6e2d8330320d13552fbdfb3650bfadbdec Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 13:08:25 +0700 Subject: [PATCH 30/76] Fix template usage in chess960 castling check --- moves_io.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/moves_io.cpp b/moves_io.cpp index e819f2c..5c09afe 100644 --- a/moves_io.cpp +++ b/moves_io.cpp @@ -90,8 +90,8 @@ template Move uciToMove(const _Position &pos, std return Move::NO_MOVE; } // castling in chess960 - if (pos.chess960() && pt == PieceType::KING && board.at(target) == PieceType::ROOK && - pos.at(target) == board.sideToMove()) { + if (pos.chess960() && pt == PieceType::KING && pos.template at(target) == PieceType::ROOK && + pos.template at(target) == pos.sideToMove()) { return Move::make(source, target); } From a62614e4017e10e9f3cf6bffa01495879f13be26 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 13:08:31 +0700 Subject: [PATCH 31/76] Update try_compile.yml --- .github/workflows/try_compile.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/try_compile.yml b/.github/workflows/try_compile.yml index ac6bf61..ac4ae2b 100644 --- a/.github/workflows/try_compile.yml +++ b/.github/workflows/try_compile.yml @@ -9,10 +9,10 @@ jobs: runs-on: ${{ matrix.os }} strategy: - fail-fast: false + fail-fast: true matrix: os: [ubuntu-latest, windows-latest] - build_type: [Debug, Release] + build_type: [Debug] c_compiler: [gcc, clang, cl] include: @@ -52,10 +52,6 @@ jobs: run: | CXX_FLAGS="" LINK_FLAGS="" - if [[ "${{ matrix.os }}" == "ubuntu-latest" && "${{ matrix.build_type }}" == "Debug" ]]; then - CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g" - LINK_FLAGS="-fsanitize=address,undefined" - fi cmake -B "${{ steps.vars.outputs.dir }}" \ -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} \ From bfbbef8df585099b8479233cac10e0c1a204b14c Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 13:10:03 +0700 Subject: [PATCH 32/76] Update position.h --- position.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/position.h b/position.h index daa76ae..38809cd 100644 --- a/position.h +++ b/position.h @@ -171,7 +171,7 @@ template void doMove(const Move &move); template inline auto undoMove() -> std::conditional_t &, void> { - assert(history.size_ > 0 && "undoMove called with empty history"); + assert(history.size() > 0 && "undoMove called with empty history"); // Restore previous state from history assert(current_state.mv.is_ok() && "Corrupted history entry"); @@ -179,7 +179,8 @@ template Date: Wed, 25 Feb 2026 13:12:15 +0700 Subject: [PATCH 33/76] Update position.h --- position.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/position.h b/position.h index 38809cd..a902949 100644 --- a/position.h +++ b/position.h @@ -180,7 +180,7 @@ template Date: Wed, 25 Feb 2026 17:23:38 +0700 Subject: [PATCH 34/76] Update fwd_decl.h --- fwd_decl.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fwd_decl.h b/fwd_decl.h index 1d43a7b..1b753f6 100644 --- a/fwd_decl.h +++ b/fwd_decl.h @@ -25,4 +25,7 @@ using Movelist = ValueList; enum class PolyglotPiece : uint8_t; enum class EnginePiece : uint8_t; enum class ContiguousMappingPiece : uint8_t; +using Position=_Position; +using Board=Position; + } // namespace chess From ee297129f5d2ceb9c72464766c77a9c5923c83fe Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 25 Feb 2026 10:24:02 +0000 Subject: [PATCH 35/76] Apply clang-format --- fwd_decl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fwd_decl.h b/fwd_decl.h index 1b753f6..6a5033c 100644 --- a/fwd_decl.h +++ b/fwd_decl.h @@ -25,7 +25,7 @@ using Movelist = ValueList; enum class PolyglotPiece : uint8_t; enum class EnginePiece : uint8_t; enum class ContiguousMappingPiece : uint8_t; -using Position=_Position; -using Board=Position; +using Position = _Position; +using Board = Position; } // namespace chess From 10c851d95b8da58e7cedaedc60dbd55aa34164f9 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:24:19 +0700 Subject: [PATCH 36/76] Update fwd_decl.h --- fwd_decl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fwd_decl.h b/fwd_decl.h index 6a5033c..006962a 100644 --- a/fwd_decl.h +++ b/fwd_decl.h @@ -25,7 +25,7 @@ using Movelist = ValueList; enum class PolyglotPiece : uint8_t; enum class EnginePiece : uint8_t; enum class ContiguousMappingPiece : uint8_t; -using Position = _Position; +using Position = _Position; using Board = Position; } // namespace chess From 8f26ec0fbeaa9bbeb192b275b513baf40f0be61b Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 25 Feb 2026 10:25:04 +0000 Subject: [PATCH 37/76] Apply clang-format --- fwd_decl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fwd_decl.h b/fwd_decl.h index 006962a..87e64e2 100644 --- a/fwd_decl.h +++ b/fwd_decl.h @@ -25,7 +25,7 @@ using Movelist = ValueList; enum class PolyglotPiece : uint8_t; enum class EnginePiece : uint8_t; enum class ContiguousMappingPiece : uint8_t; -using Position = _Position; +using Position = _Position; using Board = Position; } // namespace chess From 4763730188b13fbf74be957ebd967ac67ed2d24d Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 19:37:59 +0700 Subject: [PATCH 38/76] Update position.h --- position.h | 58 +++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/position.h b/position.h index a902949..be18184 100644 --- a/position.h +++ b/position.h @@ -39,35 +39,6 @@ template struct alignas(64) HistoryEntry { // implementation-specific implementations goes here }; -enum class CheckType { NO_CHECK, DIRECT_CHECK, DISCOVERY_CHECK }; -enum class MoveGenType : uint16_t { - NONE = 0, - - // piece selectors - PAWN = 1 << 1, - KNIGHT = 1 << 2, - BISHOP = 1 << 3, - ROOK = 1 << 4, - QUEEN = 1 << 5, - KING = 1 << 6, - - PIECE_MASK = PAWN | KNIGHT | BISHOP | ROOK | QUEEN | KING, - - // move-type selectors - CAPTURE = 1 << 7, - QUIET = 1 << 8, - - ALL = PIECE_MASK | CAPTURE | QUIET -}; - -template constexpr MoveGenType operator&(MoveGenType a, MoveGenType b) { - using U = std::underlying_type_t; - return static_cast(static_cast(a) & static_cast(b)); -} -template constexpr MoveGenType operator|(MoveGenType a, MoveGenType b) { - using U = std::underlying_type_t; - return static_cast(static_cast(a) | static_cast(b)); -} template ::value>> class _Position { private: HistoryEntry current_state; @@ -95,6 +66,35 @@ template static constexpr MoveGenType operator&(MoveGenType a, MoveGenType b) { + using U = std::underlying_type_t; + return static_cast(static_cast(a) & static_cast(b)); + } + template static constexpr MoveGenType operator|(MoveGenType a, MoveGenType b) { + using U = std::underlying_type_t; + return static_cast(static_cast(a) | static_cast(b)); + } static constexpr auto START_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; // Legal move generation functions template inline void legals(Movelist &out) const { From 85f9728eb619971e171e7ba82825215128fb6d74 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 25 Feb 2026 12:38:27 +0000 Subject: [PATCH 39/76] Apply clang-format --- position.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/position.h b/position.h index be18184..17fd812 100644 --- a/position.h +++ b/position.h @@ -69,7 +69,7 @@ template static constexpr MoveGenType operator&(MoveGenType a, MoveGenType b) { using U = std::underlying_type_t; return static_cast(static_cast(a) & static_cast(b)); From 0dcf50dd6984c920a198b47a7e3ed455dc8eb4bf Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 19:39:48 +0700 Subject: [PATCH 40/76] revert commit --- position.h | 58 +++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/position.h b/position.h index 17fd812..08e85b8 100644 --- a/position.h +++ b/position.h @@ -39,33 +39,6 @@ template struct alignas(64) HistoryEntry { // implementation-specific implementations goes here }; -template ::value>> class _Position { - private: - HistoryEntry current_state; - - // Move history stack - std::vector> history; - Bitboard _rook_pin; - Bitboard _bishop_pin; - Bitboard _checkers; - Bitboard _check_mask; - Bitboard _pin_mask; - PieceC pieces_list[SQUARE_NB + 1] = { - PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, - PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, - PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, - PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, - PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, - PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, - PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, - PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, - PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, - PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, - PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE - }; - bool _chess960; - - public: enum class CheckType { NO_CHECK, DIRECT_CHECK, DISCOVERY_CHECK }; enum class MoveGenType : uint16_t { NONE = 0, @@ -87,14 +60,41 @@ template static constexpr MoveGenType operator&(MoveGenType a, MoveGenType b) { + template constexpr MoveGenType operator&(MoveGenType a, MoveGenType b) { using U = std::underlying_type_t; return static_cast(static_cast(a) & static_cast(b)); } - template static constexpr MoveGenType operator|(MoveGenType a, MoveGenType b) { + template constexpr MoveGenType operator|(MoveGenType a, MoveGenType b) { using U = std::underlying_type_t; return static_cast(static_cast(a) | static_cast(b)); } +template ::value>> class _Position { + private: + HistoryEntry current_state; + + // Move history stack + std::vector> history; + Bitboard _rook_pin; + Bitboard _bishop_pin; + Bitboard _checkers; + Bitboard _check_mask; + Bitboard _pin_mask; + PieceC pieces_list[SQUARE_NB + 1] = { + PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, + PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, + PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, + PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, + PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, + PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, + PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, + PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, + PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, + PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, + PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE + }; + bool _chess960; + + public: static constexpr auto START_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; // Legal move generation functions template inline void legals(Movelist &out) const { From a6e3844abb86b244cade30f7bb8e1b2cb7a2dc0f Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 25 Feb 2026 12:40:12 +0000 Subject: [PATCH 41/76] Apply clang-format --- position.h | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/position.h b/position.h index 08e85b8..a902949 100644 --- a/position.h +++ b/position.h @@ -39,35 +39,35 @@ template struct alignas(64) HistoryEntry { // implementation-specific implementations goes here }; - enum class CheckType { NO_CHECK, DIRECT_CHECK, DISCOVERY_CHECK }; - enum class MoveGenType : uint16_t { - NONE = 0, +enum class CheckType { NO_CHECK, DIRECT_CHECK, DISCOVERY_CHECK }; +enum class MoveGenType : uint16_t { + NONE = 0, - // piece selectors - PAWN = 1 << 1, - KNIGHT = 1 << 2, - BISHOP = 1 << 3, - ROOK = 1 << 4, - QUEEN = 1 << 5, - KING = 1 << 6, + // piece selectors + PAWN = 1 << 1, + KNIGHT = 1 << 2, + BISHOP = 1 << 3, + ROOK = 1 << 4, + QUEEN = 1 << 5, + KING = 1 << 6, - PIECE_MASK = PAWN | KNIGHT | BISHOP | ROOK | QUEEN | KING, + PIECE_MASK = PAWN | KNIGHT | BISHOP | ROOK | QUEEN | KING, - // move-type selectors - CAPTURE = 1 << 7, - QUIET = 1 << 8, + // move-type selectors + CAPTURE = 1 << 7, + QUIET = 1 << 8, - ALL = PIECE_MASK | CAPTURE | QUIET - }; + ALL = PIECE_MASK | CAPTURE | QUIET +}; - template constexpr MoveGenType operator&(MoveGenType a, MoveGenType b) { - using U = std::underlying_type_t; - return static_cast(static_cast(a) & static_cast(b)); - } - template constexpr MoveGenType operator|(MoveGenType a, MoveGenType b) { - using U = std::underlying_type_t; - return static_cast(static_cast(a) | static_cast(b)); - } +template constexpr MoveGenType operator&(MoveGenType a, MoveGenType b) { + using U = std::underlying_type_t; + return static_cast(static_cast(a) & static_cast(b)); +} +template constexpr MoveGenType operator|(MoveGenType a, MoveGenType b) { + using U = std::underlying_type_t; + return static_cast(static_cast(a) | static_cast(b)); +} template ::value>> class _Position { private: HistoryEntry current_state; From b2f3b921d58b3c8a658423d0ddc2cec7defe966e Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Wed, 25 Feb 2026 22:33:21 +0700 Subject: [PATCH 42/76] Update tests.cpp --- tests.cpp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests.cpp b/tests.cpp index 906b7c3..cba41b1 100644 --- a/tests.cpp +++ b/tests.cpp @@ -229,6 +229,18 @@ void check_perfts(const std::vector> &entries) { auto end_time = high_resolution_clock::now(); elapsed += duration(end_time - start_time).count(); nodes += entry.info.nodes; + if (entry.info.nodes < 5e6) { + _Position pos2=pos; + REQUIRE(pos.fen() == pos2.fen()); + auto start_time = high_resolution_clock::now(); + REQUIRE(perft(pos2, entry.info.depth) == entry.info.nodes); + auto end_time = high_resolution_clock::now(); + elapsed += duration(end_time - start_time).count(); + nodes += entry.info.nodes; + } + else{ + std::cerr << "\n(skipped copying test)\n"; + } } { _Position pos(entry.input, chess960); @@ -237,6 +249,18 @@ void check_perfts(const std::vector> &entries) { auto end_time = high_resolution_clock::now(); elapsed += duration(end_time - start_time).count(); nodes += entry.info.nodes; + if (entry.info.nodes < 5e6) { + _Position pos2=pos; + REQUIRE(pos.fen() == pos2.fen()); + auto start_time = high_resolution_clock::now(); + REQUIRE(perft(pos2, entry.info.depth) == entry.info.nodes); + auto end_time = high_resolution_clock::now(); + elapsed += duration(end_time - start_time).count(); + nodes += entry.info.nodes; + } + else{ + std::cerr << "\n(skipped copying test)\n"; + } } { _Position pos(entry.input, chess960); @@ -245,6 +269,18 @@ void check_perfts(const std::vector> &entries) { auto end_time = high_resolution_clock::now(); elapsed += duration(end_time - start_time).count(); nodes += entry.info.nodes; + if (entry.info.nodes < 5e6) { + _Position pos2=pos; + REQUIRE(pos.fen() == pos2.fen()); + auto start_time = high_resolution_clock::now(); + REQUIRE(perft(pos2, entry.info.depth) == entry.info.nodes); + auto end_time = high_resolution_clock::now(); + elapsed += duration(end_time - start_time).count(); + nodes += entry.info.nodes; + } + else{ + std::cerr << "\n(skipped copying test)\n"; + } } } double mnps = (nodes / elapsed) / 1'000'000.0; From 16cd43a219e892ce1b67345b0a27c501db9f869f Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 25 Feb 2026 15:34:09 +0000 Subject: [PATCH 43/76] Apply clang-format --- tests.cpp | 57 ++++++++++++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/tests.cpp b/tests.cpp index cba41b1..84c1d73 100644 --- a/tests.cpp +++ b/tests.cpp @@ -230,16 +230,15 @@ void check_perfts(const std::vector> &entries) { elapsed += duration(end_time - start_time).count(); nodes += entry.info.nodes; if (entry.info.nodes < 5e6) { - _Position pos2=pos; - REQUIRE(pos.fen() == pos2.fen()); - auto start_time = high_resolution_clock::now(); - REQUIRE(perft(pos2, entry.info.depth) == entry.info.nodes); - auto end_time = high_resolution_clock::now(); - elapsed += duration(end_time - start_time).count(); - nodes += entry.info.nodes; - } - else{ - std::cerr << "\n(skipped copying test)\n"; + _Position pos2 = pos; + REQUIRE(pos.fen() == pos2.fen()); + auto start_time = high_resolution_clock::now(); + REQUIRE(perft(pos2, entry.info.depth) == entry.info.nodes); + auto end_time = high_resolution_clock::now(); + elapsed += duration(end_time - start_time).count(); + nodes += entry.info.nodes; + } else { + std::cerr << "\n(skipped copying test)\n"; } } { @@ -250,16 +249,15 @@ void check_perfts(const std::vector> &entries) { elapsed += duration(end_time - start_time).count(); nodes += entry.info.nodes; if (entry.info.nodes < 5e6) { - _Position pos2=pos; - REQUIRE(pos.fen() == pos2.fen()); - auto start_time = high_resolution_clock::now(); - REQUIRE(perft(pos2, entry.info.depth) == entry.info.nodes); - auto end_time = high_resolution_clock::now(); - elapsed += duration(end_time - start_time).count(); - nodes += entry.info.nodes; - } - else{ - std::cerr << "\n(skipped copying test)\n"; + _Position pos2 = pos; + REQUIRE(pos.fen() == pos2.fen()); + auto start_time = high_resolution_clock::now(); + REQUIRE(perft(pos2, entry.info.depth) == entry.info.nodes); + auto end_time = high_resolution_clock::now(); + elapsed += duration(end_time - start_time).count(); + nodes += entry.info.nodes; + } else { + std::cerr << "\n(skipped copying test)\n"; } } { @@ -270,16 +268,15 @@ void check_perfts(const std::vector> &entries) { elapsed += duration(end_time - start_time).count(); nodes += entry.info.nodes; if (entry.info.nodes < 5e6) { - _Position pos2=pos; - REQUIRE(pos.fen() == pos2.fen()); - auto start_time = high_resolution_clock::now(); - REQUIRE(perft(pos2, entry.info.depth) == entry.info.nodes); - auto end_time = high_resolution_clock::now(); - elapsed += duration(end_time - start_time).count(); - nodes += entry.info.nodes; - } - else{ - std::cerr << "\n(skipped copying test)\n"; + _Position pos2 = pos; + REQUIRE(pos.fen() == pos2.fen()); + auto start_time = high_resolution_clock::now(); + REQUIRE(perft(pos2, entry.info.depth) == entry.info.nodes); + auto end_time = high_resolution_clock::now(); + elapsed += duration(end_time - start_time).count(); + nodes += entry.info.nodes; + } else { + std::cerr << "\n(skipped copying test)\n"; } } } From 2c92281e78c5e50dd1876bee23e63bc7fa346f6d Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Thu, 26 Feb 2026 11:27:15 +0700 Subject: [PATCH 44/76] fix undefined behavior on invalid move --- moves_io.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/moves_io.cpp b/moves_io.cpp index 5c09afe..9481fba 100644 --- a/moves_io.cpp +++ b/moves_io.cpp @@ -84,6 +84,7 @@ template Move uciToMove(const _Position &pos, std THROW_IF_EXCEPTIONS_ON(IllegalMoveException("source !in [a1, h8], target !in [a1, h8]")); return Move::NO_MOVE; } + auto move = (uci.length() == 4) ? Move::make(source, target) : Move::NO_MOVE; auto pt = piece_of(pos.at(source)); if (pt == NO_PIECE_TYPE) { THROW_IF_EXCEPTIONS_ON(IllegalMoveException("source need to be a existing piece, got nothing")); @@ -92,22 +93,22 @@ template Move uciToMove(const _Position &pos, std // castling in chess960 if (pos.chess960() && pt == PieceType::KING && pos.template at(target) == PieceType::ROOK && pos.template at(target) == pos.sideToMove()) { - return Move::make(source, target); + move= Move::make(source, target); } // convert to king captures rook // in chess960 the move should be sent as king captures rook already! - if (!pos.chess960() && pt == PieceType::KING && square_distance(target, source) == 2) { + else if (!pos.chess960() && pt == PieceType::KING && square_distance(target, source) == 2) { target = make_sq(target > source ? File::FILE_H : File::FILE_A, rank_of(source)); - return Move::make(source, target); + move= Move::make(source, target); } // en passant - if (pt == PAWN && target == pos.enpassantSq()) { - return Move::make(source, target); + else if (pt == PAWN && target == pos.enpassantSq()) { + move= Move::make(source, target); } // promotion - if (pt == PAWN && uci.length() == 5 && (rank_of(target) == (pos.sideToMove() == WHITE ? RANK_8 : RANK_1))) { + else if (pt == PAWN && uci.length() == 5 && (rank_of(target) == (pos.sideToMove() == WHITE ? RANK_8 : RANK_1))) { auto promotion = parse_pt(uci[4]); if (promotion == NO_PIECE_TYPE || promotion == KING || promotion == PAWN) { @@ -119,9 +120,8 @@ template Move uciToMove(const _Position &pos, std return Move::NO_MOVE; } - return Move::make(source, target, promotion); + move = Move::make(source, target, promotion); } - auto move = (uci.length() == 4) ? Move::make(source, target) : Move::NO_MOVE; Movelist moves; pos.legals(moves); auto it = std::find(moves.begin(), moves.end(), move); From 8092f6c3b604340ee2212961eb73408cb0a17258 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 26 Feb 2026 04:27:36 +0000 Subject: [PATCH 45/76] Apply clang-format --- moves_io.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/moves_io.cpp b/moves_io.cpp index 9481fba..b5bee47 100644 --- a/moves_io.cpp +++ b/moves_io.cpp @@ -93,18 +93,18 @@ template Move uciToMove(const _Position &pos, std // castling in chess960 if (pos.chess960() && pt == PieceType::KING && pos.template at(target) == PieceType::ROOK && pos.template at(target) == pos.sideToMove()) { - move= Move::make(source, target); + move = Move::make(source, target); } // convert to king captures rook // in chess960 the move should be sent as king captures rook already! else if (!pos.chess960() && pt == PieceType::KING && square_distance(target, source) == 2) { target = make_sq(target > source ? File::FILE_H : File::FILE_A, rank_of(source)); - move= Move::make(source, target); + move = Move::make(source, target); } // en passant else if (pt == PAWN && target == pos.enpassantSq()) { - move= Move::make(source, target); + move = Move::make(source, target); } // promotion From f5baba864bcdb6a4643ccb549c0717b08d024bf1 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Thu, 26 Feb 2026 11:33:54 +0700 Subject: [PATCH 46/76] Update clang-format.yml --- .github/workflows/clang-format.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index f7549fc..3dcc630 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -4,6 +4,8 @@ on: pull_request: branches: [ "main" ] push: + branches-ignore: + - main jobs: format: if: github.actor != 'github-actions[bot]' From 0bcfbdd292c49e3b5a1eaf9fc50dd1f5e9d60072 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Sun, 1 Mar 2026 04:54:09 +0000 Subject: [PATCH 47/76] fix #35 (#36) * fix #35# * Update try_compile.yml * Update tests.cpp * Apply clang-format --------- Co-authored-by: GitHub Actions --- .github/workflows/try_compile.yml | 1 - position.cpp | 1 + tests.cpp | 5 +++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/try_compile.yml b/.github/workflows/try_compile.yml index ac4ae2b..3c811f5 100644 --- a/.github/workflows/try_compile.yml +++ b/.github/workflows/try_compile.yml @@ -1,7 +1,6 @@ name: CMake on multiple platforms on: - push: pull_request: jobs: diff --git a/position.cpp b/position.cpp index 75cd91f..208f58e 100644 --- a/position.cpp +++ b/position.cpp @@ -196,6 +196,7 @@ template void _Position::setFEN(const s current_state = HistoryEntry(); history.clear(); _chess960 = chess960; + std::fill(std::begin(pieces_list), std::end(pieces_list), PieceC::NO_PIECE); std::istringstream ss(str); std::string board_fen, active_color, castling, enpassant; int halfmove = 0, fullmove = 1; diff --git a/tests.cpp b/tests.cpp index 84c1d73..179d80d 100644 --- a/tests.cpp +++ b/tests.cpp @@ -1330,6 +1330,11 @@ TEST_CASE("Experienced bugs in this repo") { REQUIRE(p.getCastlingPath(BLACK, true) == 0x6000000000000000ULL); REQUIRE(p.getCastlingPath(BLACK, false) == 0xe00000000000000ULL); } + { + Position p; + p.setFEN("1nbqkbnr/1ppppppp/r7/8/4P3/8/PPPP1PPP/RNBQK1NR w KQk - 0 3"); + REQUIRE(p.fen() == "1nbqkbnr/1ppppppp/r7/8/4P3/8/PPPP1PPP/RNBQK1NR w KQk - 0 3"); + } } TEST_CASE("Captures only?") { std::vector> tests = { From 41cc66c150f4d77fda0f78eb40f9418fe7a9302a Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Sun, 1 Mar 2026 05:01:11 +0000 Subject: [PATCH 48/76] little bit of small patches (#37) * Update cmake-multi-platform.yml * Update tests.cpp --- .github/workflows/cmake-multi-platform.yml | 2 +- tests.cpp | 118 +++++++++++---------- 2 files changed, 61 insertions(+), 59 deletions(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 0402087..99e8d56 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -9,7 +9,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: - fail-fast: false + fail-fast: true matrix: os: [ubuntu-latest, windows-latest] build_type: [Debug, Release] diff --git a/tests.cpp b/tests.cpp index 179d80d..6463d57 100644 --- a/tests.cpp +++ b/tests.cpp @@ -364,6 +364,65 @@ TEST_CASE("Move making pin update") { REQUIRE(pin_mask == 0x8040201000000ULL); } } + +TEST_CASE("Experienced bugs in this repo") { + { + Position p("rnbqkbnr/pp1p1ppp/8/2pPp3/8/8/PPP1PPPP/RNBQKBNR w KQkq e6 0 3"); + REQUIRE(p.zobrist() == p.hash()); + REQUIRE(p.hash() == 11114434025341711937ULL); + } + { + Position p("rnbqkbnr/pppp1ppp/8/3PpP2/8/8/PPP2PPP/RNBQKBNR w KQkq e6 0 2"); + REQUIRE(p.zobrist() == p.hash()); + REQUIRE(p.hash() == 11926398512926811756ULL); + } + { + Position p("r4rk1/2p1qpp1/p1np1n1p/1pb1p1B1/P1B1P1b1/1PNP1N2/2P1QPPP/R4RK1 w - - 0 12"); + REQUIRE(p.zobrist() == p.hash()); + REQUIRE(p.hash() == 221975837765752100ULL); + } + { + Position p("rnbqkbnr/ppp1p1pp/8/3pPp2/8/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 3"); + REQUIRE(p.zobrist() == p.hash()); + p.push_uci("e5d6"); + REQUIRE(p.zobrist() == p.hash()); + } + { + Position p("rnbqkbnr/ppppp1pp/8/4Pp2/8/8/PPPP1PPP/RNBQKBNR b KQkq - 0 2"); + REQUIRE(p.zobrist() == p.hash()); + p.push_uci("d7d5"); + REQUIRE(p.zobrist() == p.hash()); + } + { + Position p; + p.doMove(Move(SQ_A2, SQ_A3)); + p.doMove(Move(SQ_B8, SQ_A6)); + p.doMove(Move(SQ_F2, SQ_F3)); + p.doMove(Move(SQ_F7, SQ_F5)); + REQUIRE(p.zobrist() == p.hash()); + REQUIRE(p.hash() == 4177524090105507023); + } + { + Position p; + REQUIRE(p.getCastlingPath(WHITE, true) == 0x60); + REQUIRE(p.getCastlingPath(WHITE, false) == 0xe); + REQUIRE(p.getCastlingPath(BLACK, true) == 0x6000000000000000ULL); + REQUIRE(p.getCastlingPath(BLACK, false) == 0xe00000000000000ULL); + } + { + Position p; + p.setFEN("1nbqkbnr/1ppppppp/r7/8/4P3/8/PPPP1PPP/RNBQK1NR w KQk - 0 3"); + REQUIRE(p.fen() == "1nbqkbnr/1ppppppp/r7/8/4P3/8/PPPP1PPP/RNBQK1NR w KQk - 0 3"); + } +} +TEST_CASE("Captures only?") { + std::vector> tests = { + { "rn1qkbnr/ppp1pppp/3p4/6B1/6b1/3P4/PPP1PPPP/RN1QKBNR w KQkq - 2 3", 17, 6732 }, + { "rn1qkbnr/ppp1pppp/3p4/6B1/6b1/3P4/PPP1PPPP/RN1QKBNR w KQkq - 2 3", 30, 360 }, + { "2b5/2p5/8/8/5B1k/q7/2K5/8 w - - 0 1", 1, 1 } + }; + check_perfts(tests); +} TEST_CASE("Perfts") { std::vector> tests = { { "5k2/8/8/8/3K4/8/8/8 w - - 0 1", 1, 8 }, @@ -1286,64 +1345,6 @@ TEST_CASE("Perfts") { }; check_perfts(tests); } -TEST_CASE("Experienced bugs in this repo") { - { - Position p("rnbqkbnr/pp1p1ppp/8/2pPp3/8/8/PPP1PPPP/RNBQKBNR w KQkq e6 0 3"); - REQUIRE(p.zobrist() == p.hash()); - REQUIRE(p.hash() == 11114434025341711937ULL); - } - { - Position p("rnbqkbnr/pppp1ppp/8/3PpP2/8/8/PPP2PPP/RNBQKBNR w KQkq e6 0 2"); - REQUIRE(p.zobrist() == p.hash()); - REQUIRE(p.hash() == 11926398512926811756ULL); - } - { - Position p("r4rk1/2p1qpp1/p1np1n1p/1pb1p1B1/P1B1P1b1/1PNP1N2/2P1QPPP/R4RK1 w - - 0 12"); - REQUIRE(p.zobrist() == p.hash()); - REQUIRE(p.hash() == 221975837765752100ULL); - } - { - Position p("rnbqkbnr/ppp1p1pp/8/3pPp2/8/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 3"); - REQUIRE(p.zobrist() == p.hash()); - p.push_uci("e5d6"); - REQUIRE(p.zobrist() == p.hash()); - } - { - Position p("rnbqkbnr/ppppp1pp/8/4Pp2/8/8/PPPP1PPP/RNBQKBNR b KQkq - 0 2"); - REQUIRE(p.zobrist() == p.hash()); - p.push_uci("d7d5"); - REQUIRE(p.zobrist() == p.hash()); - } - { - Position p; - p.doMove(Move(SQ_A2, SQ_A3)); - p.doMove(Move(SQ_B8, SQ_A6)); - p.doMove(Move(SQ_F2, SQ_F3)); - p.doMove(Move(SQ_F7, SQ_F5)); - REQUIRE(p.zobrist() == p.hash()); - REQUIRE(p.hash() == 4177524090105507023); - } - { - Position p; - REQUIRE(p.getCastlingPath(WHITE, true) == 0x60); - REQUIRE(p.getCastlingPath(WHITE, false) == 0xe); - REQUIRE(p.getCastlingPath(BLACK, true) == 0x6000000000000000ULL); - REQUIRE(p.getCastlingPath(BLACK, false) == 0xe00000000000000ULL); - } - { - Position p; - p.setFEN("1nbqkbnr/1ppppppp/r7/8/4P3/8/PPPP1PPP/RNBQK1NR w KQk - 0 3"); - REQUIRE(p.fen() == "1nbqkbnr/1ppppppp/r7/8/4P3/8/PPPP1PPP/RNBQK1NR w KQk - 0 3"); - } -} -TEST_CASE("Captures only?") { - std::vector> tests = { - { "rn1qkbnr/ppp1pppp/3p4/6B1/6b1/3P4/PPP1PPPP/RN1QKBNR w KQkq - 2 3", 17, 6732 }, - { "rn1qkbnr/ppp1pppp/3p4/6B1/6b1/3P4/PPP1PPPP/RN1QKBNR w KQkq - 2 3", 30, 360 }, - { "2b5/2p5/8/8/5B1k/q7/2K5/8 w - - 0 1", 1, 1 } - }; - check_perfts(tests); -} TEST_CASE("Chess960") { std::vector> tests = { { "bqnb1rkr/pp3ppp/3ppn2/2p5/5P2/P2P4/NPP1P1PP/BQ1BNRKR w HFhf - 2 9", 1, 21 }, @@ -7113,5 +7114,6 @@ int main(int argc, char **argv) { doctest::Context ctx; ctx.setOption("success", true); ctx.setOption("no-breaks", true); + ctx.setOption("abort-after", 1); return ctx.run(); } From 1c499b6ded1bf25b5136c4b45f902ce6e83c4674 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:44:27 +0700 Subject: [PATCH 49/76] Update position.cpp --- position.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/position.cpp b/position.cpp index 208f58e..6ba92c1 100644 --- a/position.cpp +++ b/position.cpp @@ -184,7 +184,7 @@ template template void _Positionhash == hash()) { - current_state.repetition = stp->repetition ? -i : i; + current_state.repetition++; break; } } From f54bc071953123d3bb55624592534dd4c247e23a Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Mon, 2 Mar 2026 18:03:22 +0700 Subject: [PATCH 50/76] Update cmake-multi-platform.yml --- .github/workflows/cmake-multi-platform.yml | 71 ++++++++++++---------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 99e8d56..73d88c9 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -1,11 +1,16 @@ -name: CMake on multiple platforms +name: test-build on: pull_request: branches: [ "main" ] + workflow_run: + workflows: ["Compilation"] + types: + - completed jobs: build: + if: ${{ !endsWith(github.event.pull_request.title, 'no functional change') }} runs-on: ${{ matrix.os }} strategy: @@ -40,40 +45,40 @@ jobs: c_compiler: cl steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Set build dir - id: vars - shell: bash - run: echo "dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + - name: Set build dir + id: vars + shell: bash + run: echo "dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" - - name: Configure CMake - shell: bash - run: | - CXX_FLAGS="" - LINK_FLAGS="" - if [[ "${{ matrix.os }}" == "ubuntu-latest" && "${{ matrix.build_type }}" == "Debug" ]]; then - CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g" - LINK_FLAGS="-fsanitize=address,undefined" - fi + - name: Configure CMake + shell: bash + run: | + CXX_FLAGS="" + LINK_FLAGS="" + if [[ "${{ matrix.os }}" == "ubuntu-latest" && "${{ matrix.build_type }}" == "Debug" ]]; then + CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g" + LINK_FLAGS="-fsanitize=address,undefined" + fi - cmake -B "${{ steps.vars.outputs.dir }}" \ - -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} \ - -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} \ - -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ - -DCMAKE_CXX_FLAGS_DEBUG="$CXX_FLAGS" \ - -DCMAKE_EXE_LINKER_FLAGS="$LINK_FLAGS" \ - -S "${{ github.workspace }}" + cmake -B "${{ steps.vars.outputs.dir }}" \ + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} \ + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} \ + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ + -DCMAKE_CXX_FLAGS_DEBUG="$CXX_FLAGS" \ + -DCMAKE_EXE_LINKER_FLAGS="$LINK_FLAGS" \ + -S "${{ github.workspace }}" - - name: Build - run: cmake --build ${{ steps.vars.outputs.dir }} --config ${{ matrix.build_type }} + - name: Build + run: cmake --build ${{ steps.vars.outputs.dir }} --config ${{ matrix.build_type }} - - name: Test - working-directory: ${{ steps.vars.outputs.dir }} - shell: bash - run: | - if [[ "${{ matrix.os }}" == "windows-latest" ]]; then - ctest --build-config ${{ matrix.build_type }} --verbose -j 4 - else - ctest --verbose -j 4 - fi + - name: Test + working-directory: ${{ steps.vars.outputs.dir }} + shell: bash + run: | + if [[ "${{ matrix.os }}" == "windows-latest" ]]; then + ctest --build-config ${{ matrix.build_type }} --verbose -j 4 + else + ctest --verbose -j 4 + fi From ca684e9d5660a28b1d6259fee2012467a85e0d42 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Mon, 2 Mar 2026 18:03:38 +0700 Subject: [PATCH 51/76] Update try_compile.yml --- .github/workflows/try_compile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/try_compile.yml b/.github/workflows/try_compile.yml index 3c811f5..730a31e 100644 --- a/.github/workflows/try_compile.yml +++ b/.github/workflows/try_compile.yml @@ -1,4 +1,4 @@ -name: CMake on multiple platforms +name: Compilation on: pull_request: From 78c95ecc474e66705da1dcbcdef496f47b341601 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Mon, 2 Mar 2026 18:03:53 +0700 Subject: [PATCH 52/76] Update and rename cmake-multi-platform.yml to test.yml --- .github/workflows/{cmake-multi-platform.yml => test.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{cmake-multi-platform.yml => test.yml} (99%) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/test.yml similarity index 99% rename from .github/workflows/cmake-multi-platform.yml rename to .github/workflows/test.yml index 73d88c9..7b49923 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: test-build +name: test on: pull_request: From c59195c9e9d72725f7efbedcf70b196f2f6d9754 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Mon, 2 Mar 2026 18:47:23 +0700 Subject: [PATCH 53/76] Update position.h --- position.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/position.h b/position.h index a902949..93c08e8 100644 --- a/position.h +++ b/position.h @@ -458,7 +458,7 @@ template = ply; } // Test if it's draw of 75 move rule (that forces everyone to draw). It doesn't consider checkmates. inline bool is_draw(int ply) const { return rule50_count() > 99 || is_repetition(ply); } // Tests whether there has been at least one repetition From 18b18114c73e78e7254f7054ab166b53aaa67266 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Mon, 2 Mar 2026 20:19:35 +0700 Subject: [PATCH 54/76] fixed (break when same hash, not counting) --- position.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/position.cpp b/position.cpp index 6ba92c1..06f581f 100644 --- a/position.cpp +++ b/position.cpp @@ -185,7 +185,6 @@ template template void _Positionhash == hash()) { current_state.repetition++; - break; } } } From 776d0ee5d8650e56b3b5b5e6137d4ca097b2db5d Mon Sep 17 00:00:00 2001 From: winapiadmin Date: Fri, 20 Mar 2026 15:14:23 +0700 Subject: [PATCH 55/76] tiny more test and tidy up code --- movegen.cpp | 45 ++++++----------- movegen.h | 4 -- moves_io.cpp | 8 +-- non_core_tests.cpp | 11 ++++- position.cpp | 118 +++++++++++++++++++++------------------------ position.h | 93 ++++++++++++++++++----------------- types.h | 35 ++++++-------- 7 files changed, 145 insertions(+), 169 deletions(-) diff --git a/movegen.cpp b/movegen.cpp index 666d6b1..5bdfcc5 100644 --- a/movegen.cpp +++ b/movegen.cpp @@ -20,13 +20,13 @@ template struct alignas(64) SplatTable { constexpr SplatTable<> SPLAT_TABLE{}; template constexpr SplatTable SPLAT_PAWN_TABLE{}; // AVX-512 (32 lanes of uint16_t) -static Move *write_moves(Move *moveList, uint32_t mask, __m512i vector) { +static inline Move *write_moves(Move *moveList, uint32_t mask, __m512i vector) { // Avoid _mm512_mask_compressstoreu_epi16() as it's 256 uOps on Zen4 _mm512_storeu_si512(reinterpret_cast<__m512i *>(moveList), _mm512_maskz_compress_epi16(mask, vector)); return moveList + popcount(mask); } -Move *splat_moves(Move *moveList, Square from, Bitboard to_bb) { +inline Move *splat_moves(Move *moveList, Square from, Bitboard to_bb) { const auto *table = reinterpret_cast(SPLAT_TABLE.data.data()); __m512i fromVec = _mm512_set1_epi16(Move(from, SQUARE_ZERO).raw()); // two 32-lane blocks (0..31, 32..63) @@ -37,7 +37,7 @@ Move *splat_moves(Move *moveList, Square from, Bitboard to_bb) { return moveList; } -template Move *splat_pawn_moves(Move *moveList, Bitboard to_bb) { +template inline Move *splat_pawn_moves(Move *moveList, Bitboard to_bb) { const auto *table = reinterpret_cast(SPLAT_PAWN_TABLE.data.data()); moveList = write_moves(moveList, static_cast(to_bb >> 0), _mm512_load_si512(table + 0)); moveList = write_moves(moveList, static_cast(to_bb >> 32), _mm512_load_si512(table + 1)); @@ -45,9 +45,9 @@ template Move *splat_pawn_moves(Move *moveList, Bitboard to_b return moveList; } #else -template Move *splat_pawn_moves(Move *moveList, Bitboard to_bb) { +template inline Move *splat_pawn_moves(Move *moveList, Bitboard to_bb) { while (to_bb) { - Square to = (Square)pop_lsb(to_bb); + auto to = static_cast(pop_lsb(to_bb)); #if defined(_DEBUG) || !defined(NDEBUG) Square from = to - offset; assert(from >= 0 && from < 64); // sanity check @@ -59,9 +59,9 @@ template Move *splat_pawn_moves(Move *moveList, Bitboard to_b return moveList; } -Move *splat_moves(Move *moveList, Square from, Bitboard to_bb) { +inline Move *splat_moves(Move *moveList, Square from, Bitboard to_bb) { while (to_bb) - *moveList++ = Move(from, (Square)pop_lsb(to_bb)); + *moveList++ = Move(from, static_cast(pop_lsb(to_bb))); return moveList; } #endif @@ -79,7 +79,7 @@ template void movegen::genEP(const _Position &pos if (!candidates) return; - const Square ep_pawn_sq = static_cast(ep_sq - pawn_push(c)); + const Square ep_pawn_sq = ep_sq - pawn_push(c); const Bitboard ep_mask = (1ULL << ep_pawn_sq) | (1ULL << ep_sq); // ASSUME(popcount(candidates) <= 32); @@ -131,14 +131,9 @@ template void movegen::genPawnSingleMoves( const _Position &pos, Movelist &moves, Bitboard _rook_pin, Bitboard _bishop_pin, Bitboard _check_mask) { constexpr auto UP = relative_direction(c, NORTH); - constexpr auto DOWN = relative_direction(c, SOUTH); - constexpr auto DOWN_LEFT = relative_direction(c, SOUTH_WEST); - constexpr auto DOWN_RIGHT = relative_direction(c, SOUTH_EAST); constexpr auto UP_LEFT = relative_direction(c, NORTH_WEST); constexpr auto UP_RIGHT = relative_direction(c, NORTH_EAST); - constexpr auto RANK_B_PROMO = attacks::MASK_RANK[relative_rank(c, RANK_7)]; constexpr auto RANK_PROMO = attacks::MASK_RANK[relative_rank(c, RANK_8)]; - constexpr auto DOUBLE_PUSH_RANK = attacks::MASK_RANK[relative_rank(c, RANK_3)]; const auto pawns = pos.template pieces(); const auto occ_opp = pos.occ(~c); @@ -237,7 +232,7 @@ void movegen::genKingMoves(const _Position &pos, Movelist &out, Bitboar const Bitboard myOcc = pos.occ(c); // Remove king from board when computing enemy attacks - const Bitboard occWithoutKing = occAll ^ (1ULL << kingSq); + const Bitboard occWithoutKing = occAll ^ 1ULL << kingSq; Bitboard enemyAttacks = 0ULL; // Sliding pieces @@ -278,12 +273,12 @@ void movegen::genKingMoves(const _Position &pos, Movelist &out, Bitboar Bitboard OOO_SAFE = between(kingSq, castling_king_square(c, false)); Square rookKing = pos.getCastlingMetadata(c).rook_start_ks, rookQueen = pos.getCastlingMetadata(c).rook_start_qs; - if ((pos.castlingRights() & kingRights) && - !((occupancy & OO_EMPTY) || (enemy_attacks & OO_SAFE) || (_pin_mask & (1ULL << rookKing)))) { + if (pos.castlingRights() & kingRights && + !(occupancy & OO_EMPTY || enemy_attacks & OO_SAFE || _pin_mask & 1ULL << rookKing)) { out.push_back(Move::make(kingSq, rookKing)); } - if ((pos.castlingRights() & queenRights) && - !((occupancy & OOO_EMPTY) || (enemy_attacks & OOO_SAFE) || (_pin_mask & (1ULL << rookQueen)))) { + if (pos.castlingRights() & queenRights && + !(occupancy & OOO_EMPTY || enemy_attacks & OOO_SAFE || _pin_mask & 1ULL << rookQueen)) { out.push_back(Move::make(kingSq, rookQueen)); } } @@ -312,9 +307,9 @@ void movegen::genSlidingMoves( // Bitboard blockers = occ() ^ from_bb; // remove piece temporarily auto func = attacks::queen; - if constexpr (pt == PieceType::BISHOP) + if constexpr (pt == BISHOP) func = attacks::bishop; - else if constexpr (pt == PieceType::ROOK) + else if constexpr (pt == ROOK) func = attacks::rook; func = rook_hit ? attacks::rook : bishop_hit ? attacks::bishop : func; Bitboard filtered_pin = pin_mask & filter_list; @@ -325,16 +320,6 @@ void movegen::genSlidingMoves( moves.size_ += popcount(targets); } } -template Move *chess::_chess::splat_pawn_moves(Move *, Bitboard); -template Move *chess::_chess::splat_pawn_moves(Move *, Bitboard); -template Move *chess::_chess::splat_pawn_moves(Move *, Bitboard); -template Move *chess::_chess::splat_pawn_moves(Move *, Bitboard); -template Move *chess::_chess::splat_pawn_moves<(Direction)16>(Move *, Bitboard); -template Move *chess::_chess::splat_pawn_moves<(Direction)-16>(Move *, Bitboard); -template Move *chess::_chess::splat_pawn_moves(Move *, Bitboard); -template Move *chess::_chess::splat_pawn_moves(Move *, Bitboard); -template Move *chess::_chess::splat_pawn_moves(Move *, Bitboard); -template Move *chess::_chess::splat_pawn_moves(Move *, Bitboard); #define INSTANTIATE(PieceC) \ template void chess::movegen::genEP(const _Position &, Movelist &); \ template void chess::movegen::genEP(const _Position &, Movelist &); \ diff --git a/movegen.h b/movegen.h index 6e4eb2b..db535ed 100644 --- a/movegen.h +++ b/movegen.h @@ -1,10 +1,6 @@ #pragma once #include "fwd_decl.h" #include -namespace chess::_chess { -template extern Move *splat_pawn_moves(Move *moveList, Bitboard to_bb); -extern Move *splat_moves(Move *moveList, Square from, Bitboard to_bb); -} // namespace chess::_chess namespace chess::movegen { template void genEP(const _Position &, Movelist &); diff --git a/moves_io.cpp b/moves_io.cpp index b5bee47..36e1d27 100644 --- a/moves_io.cpp +++ b/moves_io.cpp @@ -362,9 +362,8 @@ template Move parseSan(const _Position &pos, std: } } template std::string moveToSan(const _Position &pos, Move move, bool long_, bool suffix) { - const char FILE_NAMES[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; + constexpr char FILE_NAMES[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; - const char RANK_NAMES[] = { '1', '2', '3', '4', '5', '6', '7', '8' }; constexpr char PieceTypeChar[] = " pnbrqk"; // Null move. (or none) if (!move.is_ok()) { @@ -385,7 +384,7 @@ template std::string moveToSan(const _Position &p } } if (piece_type == NO_PIECE_TYPE) { - THROW_IF_EXCEPTIONS_ON(IllegalMoveException("san() and lan() expect move to be pseudo-legal or null, but got " + + THROW_IF_EXCEPTIONS_ON(IllegalMoveException("moveToSan() expect move to be pseudo-legal or null, but got " + moveToUci(move) + " in " + pos.fen())); return ""; } @@ -414,6 +413,7 @@ template std::string moveToSan(const _Position &p // Disambiguate only if there are other candidates that can move to the same square. if (others) { + const char RANK_NAMES[] = { '1', '2', '3', '4', '5', '6', '7', '8' }; bool need_file = false, need_rank = false; for (Square sq = SQ_A1; sq < SQ_NONE; ++sq) { if (others & (1ULL << sq)) { @@ -454,7 +454,7 @@ template std::string moveToSan(const _Position &p return san; _Position p = pos; p.doMove(move); - bool _check = p.is_check(); + const bool _check = p.is_check(); Movelist moves; p.legals(moves); // Checkmate: no legal moves and in check; Stalemate: no legal moves and not in check diff --git a/non_core_tests.cpp b/non_core_tests.cpp index 68f4669..449a54d 100644 --- a/non_core_tests.cpp +++ b/non_core_tests.cpp @@ -843,12 +843,12 @@ TEST_SUITE("SAN Parser") { Move m = Move::make(Square::SQ_C4, Square::SQ_E5); - // CHECK(uci::moveToSan(b, m) == "Nxe5"); + CHECK(uci::moveToSan(b, m) == "Nxe5"); REQUIRE(uci::parseSan(b, "Nxe5") == m); } TEST_CASE("Parse No Move") { - Position b = Position(); + Position b; REQUIRE(uci::parseSan(b, "") == Move::NO_MOVE); } @@ -891,6 +891,13 @@ TEST_SUITE("SAN Parser") { REQUIRE(uci::parseSan(b, "O-O+") == m); } } + +TEST_SUITE("misc tests") { + TEST_CASE("FEN reconstruction") { + Position pos(Position::START_CHESS960_FEN, true); + REQUIRE(pos.fen()==Position::START_CHESS960_FEN); + } +} int main(int argc, char **argv) { doctest::Context ctx; ctx.setOption("success", true); diff --git a/position.cpp b/position.cpp index 06f581f..4f90b8e 100644 --- a/position.cpp +++ b/position.cpp @@ -1,8 +1,8 @@ #include "position.h" #include "movegen.h" #include "moves_io.h" -#include "position.h" #include +#include #ifndef GENERATE_AT_RUNTIME #define _POSSIBLY_CONSTEXPR constexpr #else @@ -41,6 +41,7 @@ template template void _Position template void _Position void _Position::setFEN(const s for (char c : castling) { switch (c) { case 'K': - INVALID_ARG_IF(chess960, "Chess960 needs specific file"); current_state.castlingRights |= WHITE_OO; current_state.castlingMetadata[WHITE].king_start = kingSq(WHITE); current_state.castlingMetadata[WHITE].rook_start_ks = SQ_H1; break; case 'Q': - INVALID_ARG_IF(chess960, "Chess960 needs specific file"); current_state.castlingRights |= WHITE_OOO; current_state.castlingMetadata[WHITE].king_start = kingSq(WHITE); current_state.castlingMetadata[WHITE].rook_start_qs = SQ_A1; break; case 'k': - INVALID_ARG_IF(chess960, "Chess960 needs specific file"); current_state.castlingRights |= BLACK_OO; current_state.castlingMetadata[BLACK].king_start = kingSq(BLACK); current_state.castlingMetadata[BLACK].rook_start_ks = SQ_H8; break; case 'q': - INVALID_ARG_IF(chess960, "Chess960 needs specific file"); current_state.castlingRights |= BLACK_OOO; current_state.castlingMetadata[BLACK].king_start = kingSq(BLACK); current_state.castlingMetadata[BLACK].rook_start_qs = SQ_A8; @@ -530,8 +528,7 @@ template template bool _Position(PAWN, ~stm)) + if (piece_at(ep_sq - pawn_push(stm)) != make_piece(PAWN, ~stm)) return false; if (!(attacks::pawn(~stm, ep_sq) & pieces(stm))) @@ -540,35 +537,38 @@ template template bool _Position 2) return false; + if (_valid_ep_square()!=ep_square()) + return false; + if (clean_castling_rights()!=castlingRights()) return false; return true; } -template CheckType _Position::givesCheck(Move m) const { - const static auto getSniper = [](const _Position *p, Square ksq, Bitboard oc) { +template CheckType _Position::givesCheck(Move move) const { + const static auto getSniper = [](const _Position *p, const Square ksq, Bitboard oc) { const auto us_occ = p->us(p->sideToMove()); const auto bishop = attacks::bishop(ksq, oc) & p->pieces(PieceType::BISHOP, PieceType::QUEEN) & us_occ; const auto rook = attacks::rook(ksq, oc) & p->pieces(PieceType::ROOK, PieceType::QUEEN) & us_occ; return (bishop | rook); }; - assert(color_of(at(m.from())) == sideToMove()); + assert(color_of(at(move.from())) == sideToMove()); - const Square from = m.from(); - const Square to = m.to(); + const Square from = move.from(); + const Square to = move.to(); const Square ksq = kingSq(~sideToMove()); const Bitboard toBB = 1ULL << (to); const PieceType pt = piece_of(at(from)); Bitboard fromKing = 0ull; - if (pt == PieceType::PAWN) { + if (pt == PAWN) { fromKing = attacks::pawn(~side_to_move(), ksq); - } else if (pt == PieceType::KNIGHT) { + } else if (pt == KNIGHT) { fromKing = attacks::knight(ksq); - } else if (pt == PieceType::BISHOP) { + } else if (pt == BISHOP) { fromKing = attacks::bishop(ksq, occ()); - } else if (pt == PieceType::ROOK) { + } else if (pt == ROOK) { fromKing = attacks::rook(ksq, occ()); - } else if (pt == PieceType::QUEEN) { + } else if (pt == QUEEN) { fromKing = attacks::queen(ksq, occ()); } @@ -579,22 +579,20 @@ template CheckType _Position::givesChec const Bitboard fromBB = 1ULL << (from); const Bitboard oc = occ() ^ fromBB; - Bitboard sniper = getSniper(this, ksq, oc); - - if (sniper) { - Square sq = (Square)pop_lsb(sniper); - return (!(movegen::between(ksq, sq) & toBB) || m.typeOf() == Move::CASTLING) ? CheckType::DISCOVERY_CHECK + if (Bitboard sniper = getSniper(this, ksq, oc)) { + const auto sq = static_cast(pop_lsb(sniper)); + return (!(movegen::between(ksq, sq) & toBB) || move.typeOf() == Move::CASTLING) ? CheckType::DISCOVERY_CHECK : CheckType::NO_CHECK; } - switch (m.typeOf()) { + switch (move.typeOf()) { case Move::NORMAL: return CheckType::NO_CHECK; case Move::PROMOTION: { Bitboard attacks = 0ull; - switch (m.promotionType()) { + switch (move.promotionType()) { case KNIGHT: attacks = attacks::knight(to); break; @@ -620,7 +618,7 @@ template CheckType _Position::givesChec } case Move::CASTLING: { - Square rookTo = relative_square(side_to_move(), to > from ? SQ_F1 : SQ_D1); + const Square rookTo = relative_square(side_to_move(), to > from ? SQ_F1 : SQ_D1); return (attacks::rook(ksq, occ()) & (1ULL << rookTo)) ? CheckType::DISCOVERY_CHECK : CheckType::NO_CHECK; } } @@ -629,7 +627,7 @@ template CheckType _Position::givesChec return CheckType::NO_CHECK; // Prevent a compiler warning } template void _Position::refresh_attacks() { - Color c = sideToMove(); + const Color c = sideToMove(); Square king_sq = kingSq(c); _bishop_pin = pinMask(c, king_sq); @@ -638,17 +636,14 @@ template void _Position::refresh_attack _checkers = attackers(~c, king_sq); - int num_checks = popcount(_checkers); - - switch (num_checks) { + switch (popcount(_checkers)) { case 0: _check_mask = ~0ULL; // no checks, full mask break; case 1: { - Square sq = static_cast(lsb(_checkers)); - Bitboard mask = (1ULL << sq) | movegen::between(king_sq, sq); - _check_mask = mask; + auto sq = static_cast(lsb(_checkers)); + _check_mask = 1ULL << sq | movegen::between(king_sq, sq); break; } @@ -660,20 +655,20 @@ template void _Position::refresh_attack template uint64_t _Position::zobrist() const { uint64_t hash = 0; for (int sq = 0; sq < 64; ++sq) { - auto p = piece_on((Square)sq); - hash ^= (p == PieceC::NO_PIECE) ? 0 : zobrist::RandomPiece[enum_idx()][(int)p][sq]; + auto p = piece_on(static_cast(sq)); + hash ^= (p == PieceC::NO_PIECE) ? 0 : zobrist::RandomPiece[enum_idx()][static_cast(p)][sq]; } hash ^= (current_state.turn == WHITE) ? zobrist::RandomTurn : 0; hash ^= zobrist::RandomCastle[current_state.castlingRights]; auto ep_sq = current_state.enPassant; if (ep_sq == SQ_NONE) return hash; - if (ep_sq != SQ_NONE) { - File f = file_of(ep_sq); + { + const File f = file_of(ep_sq); Bitboard ep_mask = (1ULL << ep_sq); // Shift to the rank where the opposing pawn sits - Color stm = sideToMove(); + const Color stm = sideToMove(); // Color them = ~stm; ep_mask = (stm == WHITE) ? (ep_mask >> 8) : (ep_mask << 8); @@ -687,19 +682,20 @@ template uint64_t _Position::zobrist() } template Move _Position::parse_uci(std::string uci) const { - return chess::uci::uciToMove(*this, uci); + return uci::uciToMove(*this, uci); } template Move _Position::push_uci(std::string uci) { - auto mv = parse_uci(uci); + const auto mv = parse_uci(std::move(uci)); doMove(mv); return mv; } template Square _Position::_valid_ep_square() const { if (ep_square() == SQ_NONE) return SQ_NONE; - Rank ep_rank = sideToMove() == WHITE ? RANK_6 : RANK_3; - Bitboard mask = 1ULL << ep_square(); + Rank ep_rank; + ep_rank = sideToMove() == WHITE ? RANK_6 : RANK_3; + const Bitboard mask = 1ULL << ep_square(); Bitboard pawn_mask = mask << 8; Bitboard org_pawn_mask = mask >> 8; if (sideToMove() == BLACK) @@ -727,35 +723,33 @@ template bool _Position::is_insufficien // only bishop + knight, can't mate if (count == 3) { - if (pieces(PieceType::BISHOP, Color::WHITE) || pieces(PieceType::BISHOP, Color::BLACK)) + if (pieces(BISHOP, WHITE) || pieces(BISHOP, BLACK)) return true; - if (pieces(PieceType::KNIGHT, Color::WHITE) || pieces(PieceType::KNIGHT, Color::BLACK)) + if (pieces(KNIGHT, WHITE) || pieces(KNIGHT, BLACK)) return true; } // same-colored bishops, can't mate if (count == 4) { // bishops on same color (one per side) - if (pieces(PieceType::BISHOP, Color::WHITE) && pieces(PieceType::BISHOP, Color::BLACK)) { - Square w = (Square)lsb(pieces(PieceType::BISHOP, Color::WHITE)); - Square b = (Square)lsb(pieces(PieceType::BISHOP, Color::BLACK)); - if (((9 * (w ^ b)) & 8) == 0) + if (pieces(BISHOP, WHITE) && pieces(BISHOP, BLACK)) { + if (auto w = static_cast(lsb(pieces(BISHOP, WHITE))), + b = static_cast(lsb(pieces(BISHOP, BLACK))); + ((9 * (w ^ b)) & 8) == 0) return true; } // one side with two bishops on same color - auto white_bishops = pieces(PieceType::BISHOP, Color::WHITE); - auto black_bishops = pieces(PieceType::BISHOP, Color::BLACK); + auto white_bishops = pieces(BISHOP, WHITE); + auto black_bishops = pieces(BISHOP, BLACK); if (popcount(white_bishops) == 2) { - Square b1 = (Square)lsb(white_bishops); - Square b2 = (Square)msb(white_bishops); - if (((9 * (b1 ^ b2)) & 8) == 0) + if (auto b1 = static_cast(lsb(white_bishops)), b2 = static_cast(msb(white_bishops)); + ((9 * (b1 ^ b2)) & 8) == 0) return true; } else if (popcount(black_bishops) == 2) { - Square b1 = (Square)lsb(black_bishops); - Square b2 = (Square)msb(black_bishops); - if (((9 * (b1 ^ b2)) & 8) == 0) + if (auto b1 = static_cast(lsb(black_bishops)), b2 = static_cast(msb(black_bishops)); + ((9 * (b1 ^ b2)) & 8) == 0) return true; } } @@ -763,12 +757,10 @@ template bool _Position::is_insufficien return false; } template CastlingRights _Position::clean_castling_rights() const { - constexpr Bitboard cr_WOO = 1ULL << SQ_H1; - constexpr Bitboard cr_WOOO = 1ULL << SQ_A1; - constexpr Bitboard cr_BOO = 1ULL << SQ_H8; - constexpr Bitboard cr_BOOO = 1ULL << SQ_A8; - if (history.size()) - return castlingRights(); + const Bitboard cr_BOO = current_state.castlingMetadata[BLACK].rook_start_ks; + const Bitboard cr_BOOO = current_state.castlingMetadata[BLACK].rook_start_qs; + const Bitboard cr_WOO = current_state.castlingMetadata[WHITE].rook_start_ks; + const Bitboard cr_WOOO = current_state.castlingMetadata[WHITE].rook_start_qs; Bitboard castling = 0; // mappings castling |= (castlingRights() & WHITE_OO) ? cr_WOO : 0; @@ -783,9 +775,9 @@ template CastlingRights _Position::clea white_castling &= (cr_WOO | cr_WOOO); black_castling &= (cr_BOO | cr_BOOO); // king exists in e1/e8 depending on color - if (!(occ(WHITE) & pieces(KING) & (1ULL << SQ_E1))) + if (!(occ(WHITE) & pieces(KING) & (1ULL << current_state.castlingMetadata[WHITE].king_start))) white_castling = 0; - if (!(occ(BLACK) & pieces(KING) & (1ULL << SQ_E8))) + if (!(occ(BLACK) & pieces(KING) & (1ULL << current_state.castlingMetadata[BLACK].king_start))) black_castling = 0; castling = white_castling | black_castling; // Re-map diff --git a/position.h b/position.h index 93c08e8..c2cc2f7 100644 --- a/position.h +++ b/position.h @@ -74,11 +74,11 @@ template > history; - Bitboard _rook_pin; - Bitboard _bishop_pin; - Bitboard _checkers; - Bitboard _check_mask; - Bitboard _pin_mask; + Bitboard _rook_pin{}; + Bitboard _bishop_pin{}; + Bitboard _checkers{}; + Bitboard _check_mask{}; + Bitboard _pin_mask{}; PieceC pieces_list[SQUARE_NB + 1] = { PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, @@ -92,14 +92,15 @@ template inline void legals(Movelist &out) const { + template void legals(Movelist &out) const { - constexpr uint16_t raw = static_cast(type); + constexpr auto raw = static_cast(type); constexpr uint16_t pieceBits = raw & static_cast(MoveGenType::PIECE_MASK); constexpr uint16_t modeBits = raw & (static_cast(MoveGenType::CAPTURE) | static_cast(MoveGenType::QUIET)); @@ -154,10 +155,7 @@ template inline void legals(Movelist &out) const { - const Color stm = sideToMove(); // Cache it - ASSUME(stm == WHITE || stm == BLACK); // Now clearly no side effects - - switch (stm) { + switch (sideToMove()) { case WHITE: legals(out); return; @@ -171,7 +169,7 @@ template void doMove(const Move &move); template inline auto undoMove() -> std::conditional_t &, void> { - assert(history.size() > 0 && "undoMove called with empty history"); + assert(!history.empty() && "undoMove called with empty history"); // Restore previous state from history assert(current_state.mv.is_ok() && "Corrupted history entry"); @@ -184,6 +182,7 @@ template inline Bitboard pieces(Color c) const { - ASSUME(c == WHITE || c == BLACK); + [[nodiscard]] inline Bitboard pieces() const { return occ(); } + template [[nodiscard]] inline Bitboard pieces(Color c) const { + assert(c!=COLOR_NB); if constexpr (pt == PIECE_TYPE_NB || pt == ALL_PIECES) return occ(c); return current_state.pieces[pt] & current_state.occ[c]; } - template inline Bitboard pieces(PieceType pt) const { - ASSUME(c == WHITE || c == BLACK); + template [[nodiscard]] inline Bitboard pieces(PieceType pt) const { + static_assert(c!=COLOR_NB); if (pt == PIECE_TYPE_NB || pt == ALL_PIECES) return occ(c); return current_state.pieces[pt] & current_state.occ[c]; } - template inline Bitboard pieces() const { - ASSUME(c == WHITE || c == BLACK); + template [[nodiscard]] inline Bitboard pieces() const { + static_assert(c!=COLOR_NB); if constexpr (pt == PIECE_TYPE_NB || pt == ALL_PIECES) return occ(c); return current_state.pieces[pt] & current_state.occ[c]; } - inline Bitboard pieces(PieceType pt, Color c) const { - ASSUME(c == WHITE || c == BLACK); + [[nodiscard]] inline Bitboard pieces(PieceType pt, Color c) const { + assert(c!=COLOR_NB); // still branchless switch (pt) { case PIECE_TYPE_NB: @@ -225,8 +224,8 @@ template (pt)) { case PIECE_TYPE_NB: case ALL_PIECES: return occ(); @@ -355,11 +354,11 @@ template inline Square square(Color c) const { return Square(lsb(pieces(c))); } - inline Square kingSq(Color c) const { return current_state.kings[c]; } - inline Bitboard checkers() const { return _checkers; } - inline Bitboard pin_mask() const { return _pin_mask; } + [[nodiscard]] inline Bitboard us(Color c) const { return occ(c); } + [[nodiscard]] inline Color sideToMove() const { return current_state.turn; } + [[nodiscard]] inline uint64_t hash() const { return current_state.hash; } + [[nodiscard]] inline uint64_t key() const { return current_state.hash; } + [[nodiscard]] inline Color side_to_move() const { return current_state.turn; } + [[nodiscard]] inline Square ep_square() const { return current_state.enPassant; } + template [[nodiscard]] inline Square square(Color c) const { return static_cast(lsb(pieces(c))); } + [[nodiscard]] inline Square kingSq(Color c) const { return current_state.kings[c]; } + [[nodiscard]] inline Bitboard checkers() const { return _checkers; } + [[nodiscard]] inline Bitboard pin_mask() const { return _pin_mask; } inline _Position(std::string fen = START_FEN, bool chess960 = false) { history.reserve(6144); setFEN(fen, chess960); } - inline bool isCapture(Move mv) const { + [[nodiscard]] inline bool isCapture(Move mv) const { return mv.type_of() == EN_PASSANT || (mv.type_of() != CASTLING && piece_on(mv.to_sq()) != PieceC::NO_PIECE); } - inline bool is_capture(Move mv) const { return isCapture(mv); } - inline bool is_zeroing(Move mv) const { return isCapture(mv) || at(mv.from_sq()) == PAWN; } - std::string fen() const; - inline uint8_t halfmoveClock() const { return current_state.halfMoveClock; } - inline uint16_t fullmoveNumber() const { return current_state.fullMoveNumber; } - inline uint8_t rule50_count() const { return current_state.halfMoveClock; } - inline CastlingRights castlingRights(Color c) const { + [[nodiscard]] inline bool is_capture(Move mv) const { return isCapture(mv); } + [[nodiscard]] inline bool is_zeroing(Move mv) const { return isCapture(mv) || at(mv.from_sq()) == PAWN; } + [[nodiscard]] std::string fen() const; + [[nodiscard]] inline uint8_t halfmoveClock() const { return current_state.halfMoveClock; } + [[nodiscard]] inline uint16_t fullmoveNumber() const { return current_state.fullMoveNumber; } + [[nodiscard]] inline uint8_t rule50_count() const { return current_state.halfMoveClock; } + [[nodiscard]] inline CastlingRights castlingRights(Color c) const { return current_state.castlingRights & (c == WHITE ? WHITE_CASTLING : BLACK_CASTLING); } - inline CastlingRights castlingRights() const { return current_state.castlingRights; } - inline bool is_castling(Move mv) const { return mv.type_of() == CASTLING; } + [[nodiscard]] inline CastlingRights castlingRights() const { return current_state.castlingRights; } + [[nodiscard]] inline bool is_castling(Move mv) const { return mv.type_of() == CASTLING; } inline const HistoryEntry &state() const { return current_state; } uint64_t zobrist() const; inline PieceC piece_at(Square sq) const { return piece_on(sq); } diff --git a/types.h b/types.h index 1cc991d..0f69870 100644 --- a/types.h +++ b/types.h @@ -45,16 +45,13 @@ constexpr bool is_constant_evaluated() { // both MSVC (non-comformant __cplusplus) and by-default _MSVC_LANG and other compiles with // conformant __cplusplus #elif __cplusplus >= 202002L || _MSVC_LANG >= 202002L - if (std::is_constant_evaluated()) - return true; + return std::is_constant_evaluated(); #elif defined(__GNUC__) // defined for both GCC and clang - if (__builtin_is_constant_evaluated()) - return true; + return __builtin_is_constant_evaluated(); #elif _MSC_VER >= 1925 - if (__builtin_is_constant_evaluated()) - return true; + return __builtin_is_constant_evaluated(); #else -#error "NAWH we don't think we can detect compile time in this compiler"; +# warning "NAWH we don't think we can detect compile time in this compiler"; #endif return false; } @@ -79,30 +76,30 @@ enum Square : int8_t { enum File : int8_t { FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB }; enum Rank : int8_t { RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB }; -constexpr Square square_mirror(Square sq){return (Square)((int)sq^56);} -constexpr Square flip_sq(Square sq){return square_mirror(sq);} +constexpr Square square_mirror(const Square sq){return static_cast(static_cast(sq)^56);} +constexpr Square flip_sq(const Square sq){return square_mirror(sq);} constexpr bool is_valid(const Rank r, const File f) { return 0 <= r && r <= 7 && 0 <= f && f <= 7; } constexpr bool is_valid(const Square s) { return 0 <= s && s < 64; } -constexpr File file_of(Square s) { +constexpr File file_of(const Square s) { assert(0 <= s && s < 64); - return File(s & 7); + return static_cast(s & 7); } -constexpr Rank rank_of(Square s) { +constexpr Rank rank_of(const Square s) { assert(0 <= s && s < 64); - return Rank(s >> 3); + return static_cast(s >> 3); } -constexpr Square make_sq(Rank r, File f) { +constexpr Square make_sq(const Rank r, const File f) { assert(0 <= r && r <= 7 && 0 <= f && f <= 7); return static_cast(static_cast(r * 8 + f)); } -constexpr Square make_sq(File f, Rank r) { +constexpr Square make_sq(const File f, const Rank r) { ASSUME(0 <= r && r <= 7 && 0 <= f && f <= 7); return static_cast(static_cast(r * 8 + f)); } enum Color : uint8_t { WHITE = 0, BLACK = 1, COLOR_NB = 2 }; // Toggle color -constexpr Color operator~(Color c) { return Color(c ^ BLACK); } +constexpr Color operator~(const Color c) { return static_cast(c ^ BLACK); } enum PieceType : std::int8_t { NO_PIECE_TYPE = 0, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING, ALL_PIECES = 0, PIECE_TYPE_NB = 8 }; enum Direction : int8_t { NORTH = 8, @@ -117,11 +114,11 @@ enum Direction : int8_t { DIR_NONE = 0 }; // clang-format on -inline constexpr Square relative_square(Color c, Square s) { return Square(s ^ (c * 56)); } -inline constexpr Square castling_rook_square(Color c, bool is_king_side) { +inline constexpr Square relative_square(const Color c, const Square s) { return static_cast(s ^ (c * 56)); } +inline constexpr Square castling_rook_square(const Color c, const bool is_king_side) { return relative_square(c, is_king_side ? SQ_F1 : Square::SQ_D1); } -inline constexpr Square castling_king_square(Color c, bool is_king_side) { +inline constexpr Square castling_king_square(const Color c, const bool is_king_side) { return relative_square(c, is_king_side ? SQ_G1 : Square::SQ_C1); } From 0e6db856965e177fdaec4a16b7a1ffb0d511b6e3 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 20 Mar 2026 08:16:29 +0000 Subject: [PATCH 56/76] Apply clang-format --- non_core_tests.cpp | 2 +- position.cpp | 12 ++++++------ position.h | 12 +++++++----- types.h | 2 +- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/non_core_tests.cpp b/non_core_tests.cpp index 449a54d..b9536ac 100644 --- a/non_core_tests.cpp +++ b/non_core_tests.cpp @@ -895,7 +895,7 @@ TEST_SUITE("SAN Parser") { TEST_SUITE("misc tests") { TEST_CASE("FEN reconstruction") { Position pos(Position::START_CHESS960_FEN, true); - REQUIRE(pos.fen()==Position::START_CHESS960_FEN); + REQUIRE(pos.fen() == Position::START_CHESS960_FEN); } } int main(int argc, char **argv) { diff --git a/position.cpp b/position.cpp index 4f90b8e..2ec08a2 100644 --- a/position.cpp +++ b/position.cpp @@ -41,7 +41,7 @@ template template void _Position template bool _Position 2) return false; - if (_valid_ep_square()!=ep_square()) + if (_valid_ep_square() != ep_square()) + return false; + if (clean_castling_rights() != castlingRights()) return false; - if (clean_castling_rights()!=castlingRights()) return false; return true; } template CheckType _Position::givesCheck(Move move) const { @@ -582,7 +583,7 @@ template CheckType _Position::givesChec if (Bitboard sniper = getSniper(this, ksq, oc)) { const auto sq = static_cast(pop_lsb(sniper)); return (!(movegen::between(ksq, sq) & toBB) || move.typeOf() == Move::CASTLING) ? CheckType::DISCOVERY_CHECK - : CheckType::NO_CHECK; + : CheckType::NO_CHECK; } switch (move.typeOf()) { @@ -733,8 +734,7 @@ template bool _Position::is_insufficien if (count == 4) { // bishops on same color (one per side) if (pieces(BISHOP, WHITE) && pieces(BISHOP, BLACK)) { - if (auto w = static_cast(lsb(pieces(BISHOP, WHITE))), - b = static_cast(lsb(pieces(BISHOP, BLACK))); + if (auto w = static_cast(lsb(pieces(BISHOP, WHITE))), b = static_cast(lsb(pieces(BISHOP, BLACK))); ((9 * (w ^ b)) & 8) == 0) return true; } diff --git a/position.h b/position.h index c2cc2f7..98a358d 100644 --- a/position.h +++ b/position.h @@ -196,25 +196,25 @@ template [[nodiscard]] inline Bitboard pieces(Color c) const { - assert(c!=COLOR_NB); + assert(c != COLOR_NB); if constexpr (pt == PIECE_TYPE_NB || pt == ALL_PIECES) return occ(c); return current_state.pieces[pt] & current_state.occ[c]; } template [[nodiscard]] inline Bitboard pieces(PieceType pt) const { - static_assert(c!=COLOR_NB); + static_assert(c != COLOR_NB); if (pt == PIECE_TYPE_NB || pt == ALL_PIECES) return occ(c); return current_state.pieces[pt] & current_state.occ[c]; } template [[nodiscard]] inline Bitboard pieces() const { - static_assert(c!=COLOR_NB); + static_assert(c != COLOR_NB); if constexpr (pt == PIECE_TYPE_NB || pt == ALL_PIECES) return occ(c); return current_state.pieces[pt] & current_state.occ[c]; } [[nodiscard]] inline Bitboard pieces(PieceType pt, Color c) const { - assert(c!=COLOR_NB); + assert(c != COLOR_NB); // still branchless switch (pt) { case PIECE_TYPE_NB: @@ -394,7 +394,9 @@ template [[nodiscard]] inline Square square(Color c) const { return static_cast(lsb(pieces(c))); } + template [[nodiscard]] inline Square square(Color c) const { + return static_cast(lsb(pieces(c))); + } [[nodiscard]] inline Square kingSq(Color c) const { return current_state.kings[c]; } [[nodiscard]] inline Bitboard checkers() const { return _checkers; } [[nodiscard]] inline Bitboard pin_mask() const { return _pin_mask; } diff --git a/types.h b/types.h index 0f69870..afcc3a5 100644 --- a/types.h +++ b/types.h @@ -51,7 +51,7 @@ constexpr bool is_constant_evaluated() { #elif _MSC_VER >= 1925 return __builtin_is_constant_evaluated(); #else -# warning "NAWH we don't think we can detect compile time in this compiler"; +#warning "NAWH we don't think we can detect compile time in this compiler"; #endif return false; } From 0d41592fcdb1970a6d5c5be8019baae91ef0bfef Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Fri, 20 Mar 2026 16:05:27 +0700 Subject: [PATCH 57/76] Update clang-format.yml --- .github/workflows/clang-format.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 3dcc630..2985327 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -16,6 +16,7 @@ jobs: steps: - uses: actions/checkout@v4 with: + ref: ${{ github.head_ref }} token: ${{ secrets.GITHUB_TOKEN }} - name: Install clang-format From d7ac346871db782e2636439e11f89a0d9b8dc440 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:43:45 +0700 Subject: [PATCH 58/76] Update test.yml no functional change --- .github/workflows/test.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7b49923..23df641 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,10 +3,6 @@ name: test on: pull_request: branches: [ "main" ] - workflow_run: - workflows: ["Compilation"] - types: - - completed jobs: build: From 0fbca1a11351980b8117efb5c732b0a7d61e42c7 Mon Sep 17 00:00:00 2001 From: winapiadmin Date: Sat, 21 Mar 2026 16:15:08 +0700 Subject: [PATCH 59/76] simplification --- .gitattributes | 1 + .github/ISSUE_TEMPLATE/bug_report.md | 4 ++++ .gitignore | 28 ++++++++++++++++------------ attacks.h | 12 +++++++++--- movegen.h | 3 +++ position.cpp | 9 ++++++--- position.h | 12 +++--------- printers.h | 2 +- 8 files changed, 43 insertions(+), 28 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..aae67d9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +./** text=auto eol=lf diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 1ae7e05..f91faa5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -26,3 +26,7 @@ Test log from the tester. **Hardware/Software info** List about the OS, hardware, compiler and tester. + +**Specifically, if the issue is reported by logic analysis, provide solution if applicable.** + +solution to the issue if applicable. If not, please provide the logic analysis data and your interpretation of it. \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6ba49a4..27f2772 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,22 @@ -out/ -build/ -.vs/ +# Ignore everything +* + +# Allow source and essential files !*.cpp !*.h +!CMakeLists.txt !README.md !LICENSE -!CMakeLists.txt !.gitignore !.gitattributes -CMakePresets.json -CMakeSettings.json -*.s -.vscode -.cache -deps/* -!deps/ -* \ No newline at end of file +!.github/** +!.github/ +# Ignore build/editor junk +build/ +out/ +.vs/ +.vscode/ +.cache/ +cmake-*/ +# Ignore asm outputs +*.s \ No newline at end of file diff --git a/attacks.h b/attacks.h index de5e176..5027d84 100644 --- a/attacks.h +++ b/attacks.h @@ -144,9 +144,7 @@ extern const std::array BishopAttacks; * @param b * @return */ -template [[nodiscard]] static constexpr Bitboard shift(const Bitboard b) { - ASSUME(direction == NORTH || direction == EAST || direction == SOUTH || direction == WEST || direction == NORTH_EAST || - direction == SOUTH_EAST || direction == SOUTH_WEST || direction == NORTH_WEST); +[[nodiscard]] static constexpr Bitboard shift(const Bitboard b, Direction direction) { switch (direction) { case Direction::NORTH: return b << 8; @@ -166,8 +164,16 @@ template [[nodiscard]] static constexpr Bitboard shift(con return (b & ~MASK_FILE[7]) >> 7; default: UNREACHABLE(); + return 0; } } +/** + * @brief Shifts a bitboard in a given direction + * @tparam direction + * @param b + * @return + */ +template [[nodiscard]] static constexpr Bitboard shift(const Bitboard b) { return shift(b, direction); } /** * @brief diff --git a/movegen.h b/movegen.h index db535ed..df2eb41 100644 --- a/movegen.h +++ b/movegen.h @@ -13,5 +13,8 @@ template void genKingMoves(cons template void genSlidingMoves(const _Position &, Movelist &, Bitboard, Bitboard, Bitboard); extern std::array, 65> SQUARES_BETWEEN_BB; +/* + * [(file(sq1), rank(sq1)), (file(sq2), rank(sq2))] -> bitboard of squares between sq1 and sq2, excluding sq1 and sq2 + */ [[nodiscard]] inline Bitboard between(Square sq1, Square sq2) noexcept { return SQUARES_BETWEEN_BB[sq1][sq2]; } } // namespace chess::movegen diff --git a/position.cpp b/position.cpp index 2ec08a2..54d77a7 100644 --- a/position.cpp +++ b/position.cpp @@ -1,8 +1,11 @@ #include "position.h" #include "movegen.h" #include "moves_io.h" +#include "zobrist.h" +#include "printers.h" #include #include +#include #ifndef GENERATE_AT_RUNTIME #define _POSSIBLY_CONSTEXPR constexpr #else @@ -170,10 +173,10 @@ template template void _Position std::string _Position::fen() c emptyCount++; } else { if (emptyCount > 0) { - ss << std::to_string(emptyCount); + ss << emptyCount; emptyCount = 0; } ss << piece; } } if (emptyCount > 0) - ss << std::to_string(emptyCount); + ss << emptyCount; if (rank > 0) ss << '/'; } diff --git a/position.h b/position.h index 98a358d..5bad8ca 100644 --- a/position.h +++ b/position.h @@ -2,14 +2,8 @@ #include "attacks.h" #include "bitboard.h" #include "movegen.h" -#include "printers.h" #include "types.h" -#include "zobrist.h" -#include -#include #include -#include -#include #include namespace chess { @@ -546,8 +540,8 @@ template [[nodiscard]] inline Bitboard pinMask(Color c, Square sq) const { static_assert(pt == BISHOP || pt == ROOK, "Only bishop or rook allowed!"); - Bitboard occ_opp = occ(~sideToMove()); - Bitboard occ_us = occ(sideToMove()); + Bitboard occ_opp = occ(~c); + Bitboard occ_us = occ(c); Bitboard opp_sliders; opp_sliders = (pieces(~c) | pieces(QUEEN, ~c)) & occ_opp; @@ -558,7 +552,7 @@ template #include namespace chess { -// disclaimer: please don't pass Chess960 moves +// disclaimer: please don't pass Chess960 moves for move functions, use uci::uciToMove std::ostream &operator<<(std::ostream &os, const Move mv); std::ostream &operator<<(std::ostream &os, const Color c); std::ostream &operator<<(std::ostream &os, const CastlingRights cr); From 85545c017dd226459446f09cfbb2e3bfc2aec9fc Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sat, 21 Mar 2026 09:15:41 +0000 Subject: [PATCH 60/76] Apply clang-format --- position.cpp | 4 ++-- position.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/position.cpp b/position.cpp index 54d77a7..5b16e4d 100644 --- a/position.cpp +++ b/position.cpp @@ -1,11 +1,11 @@ #include "position.h" #include "movegen.h" #include "moves_io.h" -#include "zobrist.h" #include "printers.h" +#include "zobrist.h" +#include #include #include -#include #ifndef GENERATE_AT_RUNTIME #define _POSSIBLY_CONSTEXPR constexpr #else diff --git a/position.h b/position.h index 5bad8ca..8965bfc 100644 --- a/position.h +++ b/position.h @@ -552,7 +552,7 @@ template Date: Sat, 21 Mar 2026 16:34:54 +0700 Subject: [PATCH 61/76] compile error fix and add new feature: disabling testing (yah look at tests.cpp on amount of tests) --- .github/workflows/test.yml | 10 +------- CMakeLists.txt | 51 ++++++++++++++++++++++---------------- position.cpp | 1 - position.h | 2 ++ 4 files changed, 33 insertions(+), 31 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 23df641..d9f40bd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -51,19 +51,11 @@ jobs: - name: Configure CMake shell: bash run: | - CXX_FLAGS="" - LINK_FLAGS="" - if [[ "${{ matrix.os }}" == "ubuntu-latest" && "${{ matrix.build_type }}" == "Debug" ]]; then - CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g" - LINK_FLAGS="-fsanitize=address,undefined" - fi - cmake -B "${{ steps.vars.outputs.dir }}" \ -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} \ -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} \ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ - -DCMAKE_CXX_FLAGS_DEBUG="$CXX_FLAGS" \ - -DCMAKE_EXE_LINKER_FLAGS="$LINK_FLAGS" \ + -DSANITIZERS="address,undefined" \ -S "${{ github.workspace }}" - name: Build diff --git a/CMakeLists.txt b/CMakeLists.txt index a825409..6236634 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,24 +44,33 @@ set(SOURCES add_library(chesslib STATIC ${SOURCES}) target_include_directories(chesslib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) # --- Enable CTest integration --- -enable_testing() -include(FetchContent) -FetchContent_Declare( - doctest - GIT_REPOSITORY https://github.com/doctest/doctest.git - GIT_TAG v2.4.12 -) -FetchContent_MakeAvailable(doctest) -# --- Test executable --- -add_executable(test_chess - tests.cpp -) -add_executable(NonImportantTests - non_core_tests.cpp -) -target_link_libraries(test_chess PRIVATE chesslib) -target_include_directories(test_chess PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${doctest_SOURCE_DIR}) -target_link_libraries(NonImportantTests PRIVATE chesslib) -target_include_directories(NonImportantTests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${doctest_SOURCE_DIR}) -add_test(NAME test_core COMMAND test_chess) -add_test(NAME api_tests COMMAND NonImportantTests) +include(CTest) + +if(BUILD_TESTING) + include(FetchContent) + FetchContent_Declare( + doctest + GIT_REPOSITORY https://github.com/doctest/doctest.git + GIT_TAG v2.4.12 + ) + FetchContent_MakeAvailable(doctest) + + # --- Test executable --- + add_executable(test_chess tests.cpp) + add_executable(NonImportantTests non_core_tests.cpp) + + target_link_libraries(test_chess PRIVATE chesslib doctest::doctest) + target_link_libraries(NonImportantTests PRIVATE chesslib doctest::doctest) + + add_test(NAME test_core COMMAND test_chess) + add_test(NAME api_tests COMMAND NonImportantTests) + if (UNIX AND CMAKE_BUILD_TYPE MATCHES "Debug") + set(SANITIZERS "" CACHE STRING "sanitizers such as undefined,address") + + if (NOT "${SANITIZERS}" STREQUAL "") + add_compile_options(-fsanitize=${SANITIZERS} -fno-omit-frame-pointer) + add_link_options(-fsanitize=${SANITIZERS}) + endif() + endif() + +endif() \ No newline at end of file diff --git a/position.cpp b/position.cpp index 54d77a7..b09e466 100644 --- a/position.cpp +++ b/position.cpp @@ -1,7 +1,6 @@ #include "position.h" #include "movegen.h" #include "moves_io.h" -#include "zobrist.h" #include "printers.h" #include #include diff --git a/position.h b/position.h index 5bad8ca..9161f29 100644 --- a/position.h +++ b/position.h @@ -3,6 +3,8 @@ #include "bitboard.h" #include "movegen.h" #include "types.h" +#include "zobrist.h" +#include #include #include namespace chess { From 599bfc13f9e6d10486f23a0f2f0f8c82b292520b Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Sun, 22 Mar 2026 09:26:50 +0700 Subject: [PATCH 62/76] unlocked time limit (it was a problem?) --- tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests.cpp b/tests.cpp index 6463d57..3c60bf0 100644 --- a/tests.cpp +++ b/tests.cpp @@ -423,7 +423,7 @@ TEST_CASE("Captures only?") { }; check_perfts(tests); } -TEST_CASE("Perfts") { +TEST_CASE("Perfts" * doctest::timeout(36000)) { std::vector> tests = { { "5k2/8/8/8/3K4/8/8/8 w - - 0 1", 1, 8 }, { "5k2/8/8/8/3K4/8/8/8 w - - 0 1", 3, 310 }, @@ -1345,7 +1345,7 @@ TEST_CASE("Perfts") { }; check_perfts(tests); } -TEST_CASE("Chess960") { +TEST_CASE("Chess960" * doctest::timeout(36000)) { std::vector> tests = { { "bqnb1rkr/pp3ppp/3ppn2/2p5/5P2/P2P4/NPP1P1PP/BQ1BNRKR w HFhf - 2 9", 1, 21 }, { "bqnb1rkr/pp3ppp/3ppn2/2p5/5P2/P2P4/NPP1P1PP/BQ1BNRKR w HFhf - 2 9", 2, 528 }, From 0f922696bc98eebe00bbc92667dc298024d02e36 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Sun, 22 Mar 2026 10:14:51 +0700 Subject: [PATCH 63/76] Update test.yml --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d9f40bd..58ed942 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,6 +56,7 @@ jobs: -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} \ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ -DSANITIZERS="address,undefined" \ + -DCTEST_TEST_TIMEOUT=36000 \ -S "${{ github.workspace }}" - name: Build From 8c2a1bda5161dd269c14fea7140b1e04217b67c5 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Sun, 22 Mar 2026 10:22:51 +0700 Subject: [PATCH 64/76] Update position.cpp --- position.cpp | 106 +++++++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 55 deletions(-) diff --git a/position.cpp b/position.cpp index 5b16e4d..f672d6e 100644 --- a/position.cpp +++ b/position.cpp @@ -281,7 +281,6 @@ template void _Position::setFEN(const s } } - // Final assertions after parsing INVALID_ARG_IF(file_count != 8, "Last rank must have 8 squares"); INVALID_ARG_IF(rank_count != 7, "FEN must contain exactly 8 ranks"); } @@ -298,60 +297,57 @@ template void _Position::setFEN(const s // 3. Castling rights current_state.castlingRights = NO_CASTLING; - for (char c : castling) { - switch (c) { - case 'K': - current_state.castlingRights |= WHITE_OO; - current_state.castlingMetadata[WHITE].king_start = kingSq(WHITE); - current_state.castlingMetadata[WHITE].rook_start_ks = SQ_H1; - break; - case 'Q': - current_state.castlingRights |= WHITE_OOO; - current_state.castlingMetadata[WHITE].king_start = kingSq(WHITE); - current_state.castlingMetadata[WHITE].rook_start_qs = SQ_A1; - break; - case 'k': - current_state.castlingRights |= BLACK_OO; - current_state.castlingMetadata[BLACK].king_start = kingSq(BLACK); - current_state.castlingMetadata[BLACK].rook_start_ks = SQ_H8; - break; - case 'q': - current_state.castlingRights |= BLACK_OOO; - current_state.castlingMetadata[BLACK].king_start = kingSq(BLACK); - current_state.castlingMetadata[BLACK].rook_start_qs = SQ_A8; - break; - // some optional chess960? maybe not. - case '-': - break; - default: { - INVALID_ARG_IF(!chess960, "Invalid castling right for non-Chess960"); - if (c >= 'A' && c <= 'H') // white-castling - { - const auto king_start = kingSq(WHITE); - const auto rook_start = make_sq(RANK_1, static_cast(c - 'A')); - - CastlingRights cr = (file_of(rook_start) > file_of(king_start) ? WHITE_OO : WHITE_OOO); - current_state.castlingRights |= cr; - current_state.castlingMetadata[WHITE].king_start = kingSq(WHITE); - if (cr & KING_SIDE) - current_state.castlingMetadata[WHITE].rook_start_ks = rook_start; - else - current_state.castlingMetadata[WHITE].rook_start_qs = rook_start; - } else if (c >= 'a' && c <= 'h') // white-castling - { - const auto king_start = kingSq(BLACK); - const auto rook_start = make_sq(RANK_8, static_cast(c - 'a')); - - CastlingRights cr = (file_of(rook_start) > file_of(king_start) ? BLACK_OO : BLACK_OOO); - current_state.castlingRights |= cr; - current_state.castlingMetadata[BLACK].king_start = king_start; - if (cr & KING_SIDE) - current_state.castlingMetadata[BLACK].rook_start_ks = rook_start; - else - current_state.castlingMetadata[BLACK].rook_start_qs = rook_start; - } - break; - } + { + for (Color color : {WHITE, BLACK}) { + auto findKing = [&]() -> Square { + auto it = std::find_if(std::begin(pieces_list), std::end(pieces_list), + [&](PieceC p) { return p == make_piece(color); }); + INVALID_ARG_IF(it == std::end(pieces_list), "No king found for castling"); + return static_cast(it - pieces_list); + }; + + auto findRookKS = [&](Square king_sq) -> Square { + auto it = std::find_if(pieces_list + king_sq + 1, std::end(pieces_list), + [&](PieceC p) { return p == make_piece(color); }); + return (it != std::end(pieces_list)) ? static_cast(it - pieces_list) : SQ_NONE; + }; + + auto findRookQS = [&](Square king_sq) -> Square { + auto it = std::find_if(std::reverse_iterator(pieces_list + king_sq), + std::reverse_iterator(pieces_list), + [&](PieceC p) { return p == make_piece(color); }); + return (it != std::reverse_iterator(pieces_list)) ? static_cast((it.base() - 1) - pieces_list) : SQ_NONE; + }; + + Square king_sq = findKing(); + Square rook_ks = findRookKS(king_sq); + Square rook_qs = findRookQS(king_sq); + + auto validate = [&](char c) { + if(color == WHITE) { + if(c == 'K') INVALID_ARG_IF(rook_ks == SQ_NONE, "White KS castling illegal: no rook"); + if(c == 'Q') INVALID_ARG_IF(rook_qs == SQ_NONE, "White QS castling illegal: no rook"); + } else { + if(c == 'k') INVALID_ARG_IF(rook_ks == SQ_NONE, "Black KS castling illegal: no rook"); + if(c == 'q') INVALID_ARG_IF(rook_qs == SQ_NONE, "Black QS castling illegal: no rook"); + } + + INVALID_ARG_IF(rank_of(king_sq) != rank_of(rook_ks) && (c=='K'||c=='k'), "KS rook not on same rank"); + INVALID_ARG_IF(rank_of(king_sq) != rank_of(rook_qs) && (c=='Q'||c=='q'), "QS rook not on same rank"); + }; + + auto apply = [&](char c) { + validate(c); + if(color == WHITE) { + if(c == 'K') { current_state.castlingRights |= WHITE_OO; current_state.castlingMetadata[WHITE].king_start = king_sq; current_state.castlingMetadata[WHITE].rook_start_ks = rook_ks; } + if(c == 'Q') { current_state.castlingRights |= WHITE_OOO; current_state.castlingMetadata[WHITE].king_start = king_sq; current_state.castlingMetadata[WHITE].rook_start_qs = rook_qs; } + } else { + if(c == 'k') { current_state.castlingRights |= BLACK_OO; current_state.castlingMetadata[BLACK].king_start = king_sq; current_state.castlingMetadata[BLACK].rook_start_ks = rook_ks; } + if(c == 'q') { current_state.castlingRights |= BLACK_OOO; current_state.castlingMetadata[BLACK].king_start = king_sq; current_state.castlingMetadata[BLACK].rook_start_qs = rook_qs; } + } + }; + + std::for_each(castling.begin(), castling.end(), apply); } } current_state.hash ^= zobrist::RandomCastle[current_state.castlingRights]; From 008b51200fb74bf01ff79accc34e41524bdaa97f Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 22 Mar 2026 03:23:09 +0000 Subject: [PATCH 65/76] Apply clang-format --- position.cpp | 75 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 26 deletions(-) diff --git a/position.cpp b/position.cpp index f672d6e..8936723 100644 --- a/position.cpp +++ b/position.cpp @@ -298,55 +298,78 @@ template void _Position::setFEN(const s // 3. Castling rights current_state.castlingRights = NO_CASTLING; { - for (Color color : {WHITE, BLACK}) { + for (Color color : { WHITE, BLACK }) { auto findKing = [&]() -> Square { - auto it = std::find_if(std::begin(pieces_list), std::end(pieces_list), - [&](PieceC p) { return p == make_piece(color); }); + auto it = std::find_if(std::begin(pieces_list), std::end(pieces_list), [&](PieceC p) { + return p == make_piece(color); + }); INVALID_ARG_IF(it == std::end(pieces_list), "No king found for castling"); return static_cast(it - pieces_list); }; - + auto findRookKS = [&](Square king_sq) -> Square { - auto it = std::find_if(pieces_list + king_sq + 1, std::end(pieces_list), - [&](PieceC p) { return p == make_piece(color); }); + auto it = std::find_if(pieces_list + king_sq + 1, std::end(pieces_list), [&](PieceC p) { + return p == make_piece(color); + }); return (it != std::end(pieces_list)) ? static_cast(it - pieces_list) : SQ_NONE; }; - + auto findRookQS = [&](Square king_sq) -> Square { auto it = std::find_if(std::reverse_iterator(pieces_list + king_sq), std::reverse_iterator(pieces_list), [&](PieceC p) { return p == make_piece(color); }); - return (it != std::reverse_iterator(pieces_list)) ? static_cast((it.base() - 1) - pieces_list) : SQ_NONE; + return (it != std::reverse_iterator(pieces_list)) ? static_cast((it.base() - 1) - pieces_list) + : SQ_NONE; }; - + Square king_sq = findKing(); Square rook_ks = findRookKS(king_sq); Square rook_qs = findRookQS(king_sq); - - auto validate = [&](char c) { - if(color == WHITE) { - if(c == 'K') INVALID_ARG_IF(rook_ks == SQ_NONE, "White KS castling illegal: no rook"); - if(c == 'Q') INVALID_ARG_IF(rook_qs == SQ_NONE, "White QS castling illegal: no rook"); + + auto validate = [&](char c) { + if (color == WHITE) { + if (c == 'K') + INVALID_ARG_IF(rook_ks == SQ_NONE, "White KS castling illegal: no rook"); + if (c == 'Q') + INVALID_ARG_IF(rook_qs == SQ_NONE, "White QS castling illegal: no rook"); } else { - if(c == 'k') INVALID_ARG_IF(rook_ks == SQ_NONE, "Black KS castling illegal: no rook"); - if(c == 'q') INVALID_ARG_IF(rook_qs == SQ_NONE, "Black QS castling illegal: no rook"); + if (c == 'k') + INVALID_ARG_IF(rook_ks == SQ_NONE, "Black KS castling illegal: no rook"); + if (c == 'q') + INVALID_ARG_IF(rook_qs == SQ_NONE, "Black QS castling illegal: no rook"); } - - INVALID_ARG_IF(rank_of(king_sq) != rank_of(rook_ks) && (c=='K'||c=='k'), "KS rook not on same rank"); - INVALID_ARG_IF(rank_of(king_sq) != rank_of(rook_qs) && (c=='Q'||c=='q'), "QS rook not on same rank"); + + INVALID_ARG_IF(rank_of(king_sq) != rank_of(rook_ks) && (c == 'K' || c == 'k'), "KS rook not on same rank"); + INVALID_ARG_IF(rank_of(king_sq) != rank_of(rook_qs) && (c == 'Q' || c == 'q'), "QS rook not on same rank"); }; - + auto apply = [&](char c) { validate(c); - if(color == WHITE) { - if(c == 'K') { current_state.castlingRights |= WHITE_OO; current_state.castlingMetadata[WHITE].king_start = king_sq; current_state.castlingMetadata[WHITE].rook_start_ks = rook_ks; } - if(c == 'Q') { current_state.castlingRights |= WHITE_OOO; current_state.castlingMetadata[WHITE].king_start = king_sq; current_state.castlingMetadata[WHITE].rook_start_qs = rook_qs; } + if (color == WHITE) { + if (c == 'K') { + current_state.castlingRights |= WHITE_OO; + current_state.castlingMetadata[WHITE].king_start = king_sq; + current_state.castlingMetadata[WHITE].rook_start_ks = rook_ks; + } + if (c == 'Q') { + current_state.castlingRights |= WHITE_OOO; + current_state.castlingMetadata[WHITE].king_start = king_sq; + current_state.castlingMetadata[WHITE].rook_start_qs = rook_qs; + } } else { - if(c == 'k') { current_state.castlingRights |= BLACK_OO; current_state.castlingMetadata[BLACK].king_start = king_sq; current_state.castlingMetadata[BLACK].rook_start_ks = rook_ks; } - if(c == 'q') { current_state.castlingRights |= BLACK_OOO; current_state.castlingMetadata[BLACK].king_start = king_sq; current_state.castlingMetadata[BLACK].rook_start_qs = rook_qs; } + if (c == 'k') { + current_state.castlingRights |= BLACK_OO; + current_state.castlingMetadata[BLACK].king_start = king_sq; + current_state.castlingMetadata[BLACK].rook_start_ks = rook_ks; + } + if (c == 'q') { + current_state.castlingRights |= BLACK_OOO; + current_state.castlingMetadata[BLACK].king_start = king_sq; + current_state.castlingMetadata[BLACK].rook_start_qs = rook_qs; + } } }; - + std::for_each(castling.begin(), castling.end(), apply); } } From 9ad396e99044542f534fa6030519f8c904a60cf6 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Sun, 22 Mar 2026 10:31:25 +0700 Subject: [PATCH 66/76] Update position.cpp --- position.cpp | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/position.cpp b/position.cpp index 8936723..d75f2b7 100644 --- a/position.cpp +++ b/position.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #ifndef GENERATE_AT_RUNTIME #define _POSSIBLY_CONSTEXPR constexpr #else @@ -297,11 +298,11 @@ template void _Position::setFEN(const s // 3. Castling rights current_state.castlingRights = NO_CASTLING; - { + if (castling!="-"){ for (Color color : { WHITE, BLACK }) { auto findKing = [&]() -> Square { auto it = std::find_if(std::begin(pieces_list), std::end(pieces_list), [&](PieceC p) { - return p == make_piece(color); + return p == make_piece(KING, color); }); INVALID_ARG_IF(it == std::end(pieces_list), "No king found for castling"); return static_cast(it - pieces_list); @@ -309,7 +310,7 @@ template void _Position::setFEN(const s auto findRookKS = [&](Square king_sq) -> Square { auto it = std::find_if(pieces_list + king_sq + 1, std::end(pieces_list), [&](PieceC p) { - return p == make_piece(color); + return p == make_piece(ROOK, color); }); return (it != std::end(pieces_list)) ? static_cast(it - pieces_list) : SQ_NONE; }; @@ -317,16 +318,16 @@ template void _Position::setFEN(const s auto findRookQS = [&](Square king_sq) -> Square { auto it = std::find_if(std::reverse_iterator(pieces_list + king_sq), std::reverse_iterator(pieces_list), - [&](PieceC p) { return p == make_piece(color); }); + [&](PieceC p) { return p == make_piece(ROOK, color); }); return (it != std::reverse_iterator(pieces_list)) ? static_cast((it.base() - 1) - pieces_list) : SQ_NONE; }; - Square king_sq = findKing(); - Square rook_ks = findRookKS(king_sq); - Square rook_qs = findRookQS(king_sq); - - auto validate = [&](char c) { + auto apply = [&](char c) { + Square king_sq = findKing(); + Square rook_ks = findRookKS(king_sq); + Square rook_qs = findRookQS(king_sq); + if (color == WHITE) { if (c == 'K') INVALID_ARG_IF(rook_ks == SQ_NONE, "White KS castling illegal: no rook"); @@ -341,10 +342,6 @@ template void _Position::setFEN(const s INVALID_ARG_IF(rank_of(king_sq) != rank_of(rook_ks) && (c == 'K' || c == 'k'), "KS rook not on same rank"); INVALID_ARG_IF(rank_of(king_sq) != rank_of(rook_qs) && (c == 'Q' || c == 'q'), "QS rook not on same rank"); - }; - - auto apply = [&](char c) { - validate(c); if (color == WHITE) { if (c == 'K') { current_state.castlingRights |= WHITE_OO; From e384906c2b3f8e086603ecb86187dd398bafc59b Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 22 Mar 2026 03:31:44 +0000 Subject: [PATCH 67/76] Apply clang-format --- position.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/position.cpp b/position.cpp index d75f2b7..1255273 100644 --- a/position.cpp +++ b/position.cpp @@ -3,10 +3,10 @@ #include "moves_io.h" #include "printers.h" #include "zobrist.h" +#include #include #include #include -#include #ifndef GENERATE_AT_RUNTIME #define _POSSIBLY_CONSTEXPR constexpr #else @@ -298,7 +298,7 @@ template void _Position::setFEN(const s // 3. Castling rights current_state.castlingRights = NO_CASTLING; - if (castling!="-"){ + if (castling != "-") { for (Color color : { WHITE, BLACK }) { auto findKing = [&]() -> Square { auto it = std::find_if(std::begin(pieces_list), std::end(pieces_list), [&](PieceC p) { @@ -327,7 +327,7 @@ template void _Position::setFEN(const s Square king_sq = findKing(); Square rook_ks = findRookKS(king_sq); Square rook_qs = findRookQS(king_sq); - + if (color == WHITE) { if (c == 'K') INVALID_ARG_IF(rook_ks == SQ_NONE, "White KS castling illegal: no rook"); From 568420414d597d4ef8b824770b04b82fc11bde12 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Sun, 22 Mar 2026 04:04:59 +0000 Subject: [PATCH 68/76] castling fix --- position.cpp | 41 ++++++++++++++++++++++++++--------------- types.h | 2 +- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/position.cpp b/position.cpp index 1255273..3a35490 100644 --- a/position.cpp +++ b/position.cpp @@ -308,25 +308,32 @@ template void _Position::setFEN(const s return static_cast(it - pieces_list); }; - auto findRookKS = [&](Square king_sq) -> Square { - auto it = std::find_if(pieces_list + king_sq + 1, std::end(pieces_list), [&](PieceC p) { - return p == make_piece(ROOK, color); - }); - return (it != std::end(pieces_list)) ? static_cast(it - pieces_list) : SQ_NONE; + auto findRookQS = [&](Square king_sq, Color color) -> Square { + Rank r = rank_of(king_sq); + for (int f = file_of(king_sq) - 1; f >= FILE_A; --f) { + Square sq = make_sq(r, static_cast(f)); + PieceC p = pieces_list[sq]; + if (p != PieceC::NO_PIECE && type_of(p) == ROOK && color_of(p) == color) + return sq; + } + return SQ_NONE; }; - auto findRookQS = [&](Square king_sq) -> Square { - auto it = std::find_if(std::reverse_iterator(pieces_list + king_sq), - std::reverse_iterator(pieces_list), - [&](PieceC p) { return p == make_piece(ROOK, color); }); - return (it != std::reverse_iterator(pieces_list)) ? static_cast((it.base() - 1) - pieces_list) - : SQ_NONE; + auto findRookKS = [&](Square king_sq, Color color) -> Square { + Rank r = rank_of(king_sq); + for (int f = file_of(king_sq) + 1; f <= FILE_H; ++f) { + Square sq = make_sq(r, static_cast(f)); + PieceC p = pieces_list[sq]; + if (p != PieceC::NO_PIECE && type_of(p) == ROOK && color_of(p) == color) + return sq; + } + return SQ_NONE; }; auto apply = [&](char c) { Square king_sq = findKing(); - Square rook_ks = findRookKS(king_sq); - Square rook_qs = findRookQS(king_sq); + Square rook_ks = findRookKS(king_sq,color); + Square rook_qs = findRookQS(king_sq,color); if (color == WHITE) { if (c == 'K') @@ -340,8 +347,12 @@ template void _Position::setFEN(const s INVALID_ARG_IF(rook_qs == SQ_NONE, "Black QS castling illegal: no rook"); } - INVALID_ARG_IF(rank_of(king_sq) != rank_of(rook_ks) && (c == 'K' || c == 'k'), "KS rook not on same rank"); - INVALID_ARG_IF(rank_of(king_sq) != rank_of(rook_qs) && (c == 'Q' || c == 'q'), "QS rook not on same rank"); + if (c == 'K' || c == 'k') + INVALID_ARG_IF(rook_ks != SQ_NONE && rank_of(king_sq) != rank_of(rook_ks), + "KS rook not on same rank"); + if (c == 'Q' || c == 'q') + INVALID_ARG_IF(rook_qs != SQ_NONE && rank_of(king_sq) != rank_of(rook_qs), + "QS rook not on same rank"); if (color == WHITE) { if (c == 'K') { current_state.castlingRights |= WHITE_OO; diff --git a/types.h b/types.h index afcc3a5..f6177f8 100644 --- a/types.h +++ b/types.h @@ -94,7 +94,7 @@ constexpr Square make_sq(const Rank r, const File f) { return static_cast(static_cast(r * 8 + f)); } constexpr Square make_sq(const File f, const Rank r) { - ASSUME(0 <= r && r <= 7 && 0 <= f && f <= 7); + assert(0 <= r && r <= 7 && 0 <= f && f <= 7); return static_cast(static_cast(r * 8 + f)); } enum Color : uint8_t { WHITE = 0, BLACK = 1, COLOR_NB = 2 }; From 53ea3e5f8fada26655f46722846bae4c4c2f93ac Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 22 Mar 2026 04:05:29 +0000 Subject: [PATCH 69/76] Apply clang-format --- position.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/position.cpp b/position.cpp index 3a35490..1eceda3 100644 --- a/position.cpp +++ b/position.cpp @@ -332,8 +332,8 @@ template void _Position::setFEN(const s auto apply = [&](char c) { Square king_sq = findKing(); - Square rook_ks = findRookKS(king_sq,color); - Square rook_qs = findRookQS(king_sq,color); + Square rook_ks = findRookKS(king_sq, color); + Square rook_qs = findRookQS(king_sq, color); if (color == WHITE) { if (c == 'K') @@ -348,11 +348,9 @@ template void _Position::setFEN(const s } if (c == 'K' || c == 'k') - INVALID_ARG_IF(rook_ks != SQ_NONE && rank_of(king_sq) != rank_of(rook_ks), - "KS rook not on same rank"); + INVALID_ARG_IF(rook_ks != SQ_NONE && rank_of(king_sq) != rank_of(rook_ks), "KS rook not on same rank"); if (c == 'Q' || c == 'q') - INVALID_ARG_IF(rook_qs != SQ_NONE && rank_of(king_sq) != rank_of(rook_qs), - "QS rook not on same rank"); + INVALID_ARG_IF(rook_qs != SQ_NONE && rank_of(king_sq) != rank_of(rook_qs), "QS rook not on same rank"); if (color == WHITE) { if (c == 'K') { current_state.castlingRights |= WHITE_OO; From 166d621cb223e3165a31dd492284019e79411fc9 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Sun, 22 Mar 2026 11:33:09 +0700 Subject: [PATCH 70/76] disable timeout --- .github/workflows/test.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 58ed942..987da5e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,7 +56,6 @@ jobs: -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} \ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ -DSANITIZERS="address,undefined" \ - -DCTEST_TEST_TIMEOUT=36000 \ -S "${{ github.workspace }}" - name: Build @@ -67,7 +66,7 @@ jobs: shell: bash run: | if [[ "${{ matrix.os }}" == "windows-latest" ]]; then - ctest --build-config ${{ matrix.build_type }} --verbose -j 4 + ctest --build-config ${{ matrix.build_type }} --verbose -j 4 --timeout 0 else - ctest --verbose -j 4 + ctest --verbose -j 4 --timeout 0 fi From 4e2baa261b687f65691201b6957f723319d4b806 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Sun, 22 Mar 2026 12:38:31 +0700 Subject: [PATCH 71/76] Update test.yml --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 987da5e..2258f1f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,6 +56,7 @@ jobs: -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} \ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ -DSANITIZERS="address,undefined" \ + -DCTEST_TEST_TIMEOUT=0 \ -S "${{ github.workspace }}" - name: Build From cbc28ecdc9b7e76dcacc227b59a345731ff8c5c1 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Sun, 22 Mar 2026 05:55:14 +0000 Subject: [PATCH 72/76] fully disable timeout --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2258f1f..f80f75c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,7 +56,7 @@ jobs: -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} \ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ -DSANITIZERS="address,undefined" \ - -DCTEST_TEST_TIMEOUT=0 \ + -DDART_TESTING_TIMEOUT=0 \ -S "${{ github.workspace }}" - name: Build From f6f478c2aa5adc739bdcb68cb7fcd813e335c24d Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Sun, 22 Mar 2026 20:58:22 +0700 Subject: [PATCH 73/76] Update position.cpp --- position.cpp | 83 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/position.cpp b/position.cpp index 1eceda3..5b5adad 100644 --- a/position.cpp +++ b/position.cpp @@ -329,51 +329,70 @@ template void _Position::setFEN(const s } return SQ_NONE; }; - auto apply = [&](char c) { Square king_sq = findKing(); + if (king_sq == SQ_NONE) return; + Square rook_ks = findRookKS(king_sq, color); Square rook_qs = findRookQS(king_sq, color); - - if (color == WHITE) { - if (c == 'K') - INVALID_ARG_IF(rook_ks == SQ_NONE, "White KS castling illegal: no rook"); - if (c == 'Q') - INVALID_ARG_IF(rook_qs == SQ_NONE, "White QS castling illegal: no rook"); - } else { - if (c == 'k') - INVALID_ARG_IF(rook_ks == SQ_NONE, "Black KS castling illegal: no rook"); - if (c == 'q') - INVALID_ARG_IF(rook_qs == SQ_NONE, "Black QS castling illegal: no rook"); - } - - if (c == 'K' || c == 'k') - INVALID_ARG_IF(rook_ks != SQ_NONE && rank_of(king_sq) != rank_of(rook_ks), "KS rook not on same rank"); - if (c == 'Q' || c == 'q') - INVALID_ARG_IF(rook_qs != SQ_NONE && rank_of(king_sq) != rank_of(rook_qs), "QS rook not on same rank"); - if (color == WHITE) { - if (c == 'K') { + + auto setKS = [&](Square rook_sq) { + INVALID_ARG_IF(rook_sq == SQ_NONE, "kingside rook not found"); + INVALID_ARG_IF(rank_of(king_sq) != rank_of(rook_sq), "kingside rook not on same rank"); + + if (color == WHITE) { current_state.castlingRights |= WHITE_OO; current_state.castlingMetadata[WHITE].king_start = king_sq; - current_state.castlingMetadata[WHITE].rook_start_ks = rook_ks; - } - if (c == 'Q') { - current_state.castlingRights |= WHITE_OOO; - current_state.castlingMetadata[WHITE].king_start = king_sq; - current_state.castlingMetadata[WHITE].rook_start_qs = rook_qs; - } - } else { - if (c == 'k') { + current_state.castlingMetadata[WHITE].rook_start_ks = rook_sq; + } else { current_state.castlingRights |= BLACK_OO; current_state.castlingMetadata[BLACK].king_start = king_sq; - current_state.castlingMetadata[BLACK].rook_start_ks = rook_ks; + current_state.castlingMetadata[BLACK].rook_start_ks = rook_sq; } - if (c == 'q') { + }; + + auto setQS = [&](Square rook_sq) { + INVALID_ARG_IF(rook_sq == SQ_NONE, "queenside rook not found"); + INVALID_ARG_IF(rank_of(king_sq) != rank_of(rook_sq), "queenside rook not on same rank"); + + if (color == WHITE) { + current_state.castlingRights |= WHITE_OOO; + current_state.castlingMetadata[WHITE].king_start = king_sq; + current_state.castlingMetadata[WHITE].rook_start_qs = rook_sq; + } else { current_state.castlingRights |= BLACK_OOO; current_state.castlingMetadata[BLACK].king_start = king_sq; - current_state.castlingMetadata[BLACK].rook_start_qs = rook_qs; + current_state.castlingMetadata[BLACK].rook_start_qs = rook_sq; } + }; + + if (c == 'K' && color == WHITE) setKS(rook_ks); + else if (c == 'Q' && color == WHITE) setQS(rook_qs); + else if (c == 'k' && color == BLACK) setKS(rook_ks); + else if (c == 'q' && color == BLACK) setQS(rook_qs); + + else if (c >= 'A' && c <= 'H' && color == WHITE) { + File f = static_cast(c - 'A'); + Square rook_sq = make_sq(RANK_1, f); + + PieceC p = pieces_list[rook_sq]; + INVALID_ARG_IF(p == PieceC::NO_PIECE || type_of(p) != ROOK || color_of(p) != WHITE, + "Invalid white Chess960 rook"); + + (f > file_of(king_sq)) ? setKS(rook_sq) : setQS(rook_sq); + } + else if (c >= 'a' && c <= 'h' && color == BLACK) { + File f = static_cast(c - 'a'); + Square rook_sq = make_sq(RANK_8, f); + + PieceC p = pieces_list[rook_sq]; + INVALID_ARG_IF(p == PieceC::NO_PIECE || type_of(p) != ROOK || color_of(p) != BLACK, + "Invalid black Chess960 rook"); + + (f > file_of(king_sq)) ? setKS(rook_sq) : setQS(rook_sq); } + + // ignore '-' }; std::for_each(castling.begin(), castling.end(), apply); From b32a718ad1b07cd1929e99a8b098205e7121f377 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 22 Mar 2026 13:58:43 +0000 Subject: [PATCH 74/76] Apply clang-format --- position.cpp | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/position.cpp b/position.cpp index 5b5adad..57ae0be 100644 --- a/position.cpp +++ b/position.cpp @@ -331,15 +331,16 @@ template void _Position::setFEN(const s }; auto apply = [&](char c) { Square king_sq = findKing(); - if (king_sq == SQ_NONE) return; - + if (king_sq == SQ_NONE) + return; + Square rook_ks = findRookKS(king_sq, color); Square rook_qs = findRookQS(king_sq, color); - + auto setKS = [&](Square rook_sq) { INVALID_ARG_IF(rook_sq == SQ_NONE, "kingside rook not found"); INVALID_ARG_IF(rank_of(king_sq) != rank_of(rook_sq), "kingside rook not on same rank"); - + if (color == WHITE) { current_state.castlingRights |= WHITE_OO; current_state.castlingMetadata[WHITE].king_start = king_sq; @@ -350,11 +351,11 @@ template void _Position::setFEN(const s current_state.castlingMetadata[BLACK].rook_start_ks = rook_sq; } }; - + auto setQS = [&](Square rook_sq) { INVALID_ARG_IF(rook_sq == SQ_NONE, "queenside rook not found"); INVALID_ARG_IF(rank_of(king_sq) != rank_of(rook_sq), "queenside rook not on same rank"); - + if (color == WHITE) { current_state.castlingRights |= WHITE_OOO; current_state.castlingMetadata[WHITE].king_start = king_sq; @@ -365,33 +366,36 @@ template void _Position::setFEN(const s current_state.castlingMetadata[BLACK].rook_start_qs = rook_sq; } }; - - if (c == 'K' && color == WHITE) setKS(rook_ks); - else if (c == 'Q' && color == WHITE) setQS(rook_qs); - else if (c == 'k' && color == BLACK) setKS(rook_ks); - else if (c == 'q' && color == BLACK) setQS(rook_qs); - + + if (c == 'K' && color == WHITE) + setKS(rook_ks); + else if (c == 'Q' && color == WHITE) + setQS(rook_qs); + else if (c == 'k' && color == BLACK) + setKS(rook_ks); + else if (c == 'q' && color == BLACK) + setQS(rook_qs); + else if (c >= 'A' && c <= 'H' && color == WHITE) { File f = static_cast(c - 'A'); Square rook_sq = make_sq(RANK_1, f); - + PieceC p = pieces_list[rook_sq]; INVALID_ARG_IF(p == PieceC::NO_PIECE || type_of(p) != ROOK || color_of(p) != WHITE, "Invalid white Chess960 rook"); - + (f > file_of(king_sq)) ? setKS(rook_sq) : setQS(rook_sq); - } - else if (c >= 'a' && c <= 'h' && color == BLACK) { + } else if (c >= 'a' && c <= 'h' && color == BLACK) { File f = static_cast(c - 'a'); Square rook_sq = make_sq(RANK_8, f); - + PieceC p = pieces_list[rook_sq]; INVALID_ARG_IF(p == PieceC::NO_PIECE || type_of(p) != ROOK || color_of(p) != BLACK, "Invalid black Chess960 rook"); - + (f > file_of(king_sq)) ? setKS(rook_sq) : setQS(rook_sq); } - + // ignore '-' }; From 98cefa6e21b99268575cdfbc10dd44d87727a614 Mon Sep 17 00:00:00 2001 From: winapiadmin <138602885+winapiadmin@users.noreply.github.com> Date: Sun, 22 Mar 2026 14:22:41 +0000 Subject: [PATCH 75/76] ignore syntactically illegal FEN castling input (clean them) --- position.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/position.cpp b/position.cpp index 57ae0be..536c77f 100644 --- a/position.cpp +++ b/position.cpp @@ -367,14 +367,26 @@ template void _Position::setFEN(const s } }; - if (c == 'K' && color == WHITE) + if (c == 'K' && color == WHITE) { + if (rook_ks == SQ_NONE) return; + if (rank_of(king_sq) != rank_of(rook_ks)) return; setKS(rook_ks); - else if (c == 'Q' && color == WHITE) + } + else if (c == 'Q' && color == WHITE) { + if (rook_qs == SQ_NONE) return; + if (rank_of(king_sq) != rank_of(rook_qs)) return; setQS(rook_qs); - else if (c == 'k' && color == BLACK) + } + else if (c == 'k' && color == BLACK) { + if (rook_ks == SQ_NONE) return; + if (rank_of(king_sq) != rank_of(rook_ks)) return; setKS(rook_ks); - else if (c == 'q' && color == BLACK) + } + else if (c == 'q' && color == BLACK) { + if (rook_qs == SQ_NONE) return; + if (rank_of(king_sq) != rank_of(rook_qs)) return; setQS(rook_qs); + } else if (c >= 'A' && c <= 'H' && color == WHITE) { File f = static_cast(c - 'A'); From 551ed6e573e7294dc4763937d9983790bed1b98e Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 22 Mar 2026 14:23:04 +0000 Subject: [PATCH 76/76] Apply clang-format --- position.cpp | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/position.cpp b/position.cpp index 536c77f..c8bc9ec 100644 --- a/position.cpp +++ b/position.cpp @@ -368,23 +368,28 @@ template void _Position::setFEN(const s }; if (c == 'K' && color == WHITE) { - if (rook_ks == SQ_NONE) return; - if (rank_of(king_sq) != rank_of(rook_ks)) return; + if (rook_ks == SQ_NONE) + return; + if (rank_of(king_sq) != rank_of(rook_ks)) + return; setKS(rook_ks); - } - else if (c == 'Q' && color == WHITE) { - if (rook_qs == SQ_NONE) return; - if (rank_of(king_sq) != rank_of(rook_qs)) return; + } else if (c == 'Q' && color == WHITE) { + if (rook_qs == SQ_NONE) + return; + if (rank_of(king_sq) != rank_of(rook_qs)) + return; setQS(rook_qs); - } - else if (c == 'k' && color == BLACK) { - if (rook_ks == SQ_NONE) return; - if (rank_of(king_sq) != rank_of(rook_ks)) return; + } else if (c == 'k' && color == BLACK) { + if (rook_ks == SQ_NONE) + return; + if (rank_of(king_sq) != rank_of(rook_ks)) + return; setKS(rook_ks); - } - else if (c == 'q' && color == BLACK) { - if (rook_qs == SQ_NONE) return; - if (rank_of(king_sq) != rank_of(rook_qs)) return; + } else if (c == 'q' && color == BLACK) { + if (rook_qs == SQ_NONE) + return; + if (rank_of(king_sq) != rank_of(rook_qs)) + return; setQS(rook_qs); }