From e1b7051c52c74171c114a4c00a7529dab5a3a092 Mon Sep 17 00:00:00 2001 From: winapiadmin Date: Sat, 22 Nov 2025 10:52:52 +0700 Subject: [PATCH 1/2] removed examples and modernize with forward declarations (bonus: make more incremental update) --- CMakeLists.txt | 6 +- examples/dbg_efficiency.cpp | 6 -- examples/perft_with_tt.cpp | 132 -------------------------------- fwd_decl.h | 25 ++++++ position.cpp | 149 ++++++++++++------------------------ position.h | 23 +++++- printers.cpp | 40 ++++------ printers.h | 13 +--- tests.cpp | 40 ++++++++++ types.h | 11 ++- 10 files changed, 159 insertions(+), 286 deletions(-) delete mode 100644 examples/dbg_efficiency.cpp delete mode 100644 examples/perft_with_tt.cpp create mode 100644 fwd_decl.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 856e7cc..c1f0fd6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,13 +40,9 @@ endif() set(SOURCES position.cpp attacks.cpp "zobrist.cpp" - "moves_io.cpp" "printers.cpp" "repetition.h") + "moves_io.cpp" "printers.cpp" "repetition.h" "fwd_decl.h") add_library(chesslib STATIC ${SOURCES}) target_include_directories(chesslib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -add_executable(perft_tt examples/perft_with_tt.cpp) -target_link_libraries(perft_tt PRIVATE chesslib "${ASAN_RUNTIME_LIBS}") -add_executable(debug_efficiency examples/dbg_efficiency.cpp) -target_link_libraries(debug_efficiency PRIVATE chesslib) # --- Enable CTest integration --- enable_testing() include(FetchContent) diff --git a/examples/dbg_efficiency.cpp b/examples/dbg_efficiency.cpp deleted file mode 100644 index 29dc3fd..0000000 --- a/examples/dbg_efficiency.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "position.h" -int main() { - std::cout << sizeof(chess::HistoryEntry) << " " << sizeof(chess::HistoryEntry) << '\n'; - std::cout << "8192 entries/board (impl) "<< (8192*sizeof(chess::HistoryEntry)) << " " << (8192*sizeof(chess::HistoryEntry)) << '\n'; - return 0; -} \ No newline at end of file diff --git a/examples/perft_with_tt.cpp b/examples/perft_with_tt.cpp deleted file mode 100644 index bf2c44b..0000000 --- a/examples/perft_with_tt.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include -#include "../position.h" -#include "../printers.h" -#include -using namespace chess; -//#define TT_ENABLED -struct TTEntry { - uint64_t hash; - uint64_t nodes; - int depth; -}; -std::vector tt; -template inline uint64_t perft(_Position &p) { - static_assert(Depth >= 0, "Negative depth in perft template!"); - if constexpr (Depth == 1) { - Movelist moves; - p.template legals(moves); - if constexpr (EnableDiv) - for (const Move &m : moves) { - std::cout << m << ": 1\n"; - } - return moves.size(); - } else { -#ifdef TT_ENABLED - const uint64_t hash = p.hash(); - TTEntry &entry = tt[hash & (tt.size() - 1)]; - if (entry.hash == hash && entry.depth == Depth) - return entry.nodes; -#endif - Movelist moves; - p.template legals(moves); - - uint64_t total = 0; - for (const Move &m : moves) { - p.template doMove(m); - const uint64_t nodes = perft(p); - total += nodes; - p.undoMove(); - - if constexpr (EnableDiv) - std::cout << m << ": " << nodes << '\n'; - } - -#ifdef TT_ENABLED - entry.hash = hash; - entry.depth = Depth; - entry.nodes = total; -#endif - return total; - } -} -template inline uint64_t perft(_Position &p, int Depth) { - if (Depth != 1) { -#ifdef TT_ENABLED - const uint64_t hash = p.hash(); - TTEntry &entry = tt[hash & (tt.size() - 1)]; - if (entry.hash == hash && entry.depth == Depth) - return entry.nodes; -#endif - Movelist moves; - p.template legals(moves); - - uint64_t total = 0; - for (const Move &m : moves) { - p.template doMove(m); - const uint64_t nodes = perft(p, Depth - 1); - total += nodes; - p.undoMove(); - - if constexpr (EnableDiv) - std::cout << m << ": " << nodes << '\n'; - } -#ifdef TT_ENABLED - entry.hash = hash; - entry.depth = Depth; - entry.nodes = total; -#endif - return total; - } else { - Movelist moves; - p.template legals(moves); - if constexpr (EnableDiv) - for (const Move &m : moves) { - std::cout << m << ": 1\n"; - } - return moves.size(); - } -} -template void benchmark(_Position &pos) { - using namespace std::chrono; - #ifndef TT_ENABLED - constexpr int N_RUNS = EnableDiv?1:5; - #else - constexpr int N_RUNS=1; - #endif - uint64_t nodes = 0; - double totalTime = 0.0; - - std::cout << "Running " << N_RUNS << " benchmark iterations:\n"; - - for (int i = 0; i < N_RUNS; ++i) { - auto start = high_resolution_clock::now(); - nodes = perft(pos); - auto end = high_resolution_clock::now(); - double elapsed = duration(end - start).count(); - totalTime += elapsed; - - double mnps = (nodes / elapsed) / 1'000'000.0; - std::cout << "Run " << (i + 1) << ": " << elapsed << " s, " << mnps << " Mnps\n"; - } - - double avgTime = totalTime / N_RUNS; - double avgMnps = (nodes / avgTime) / 1'000'000.0; - - std::cout << "Average: " << avgTime << " s, " << avgMnps << " Mnps (nodes="<<(nodes/N_RUNS)<<")\n"; -} - - -int main() { - _Position pos; - -#ifdef TT_ENABLED - tt.resize(1 << 28); -#endif - benchmark<7, true, EnginePiece>(pos); - // Movelist moves; - // pos.template legals(moves); - // for (const Move &m : moves) { - // std::cout << m << ": 1\n"; - // } -} diff --git a/fwd_decl.h b/fwd_decl.h new file mode 100644 index 0000000..2dec6b9 --- /dev/null +++ b/fwd_decl.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include +namespace chess { +enum Color : uint8_t; +enum PieceType : std::int8_t; + +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 Direction : int8_t; +enum MoveType : uint16_t; +enum File : int8_t; +enum Rank : int8_t; +class Move; +template class _Position; +using Bitboard=uint64_t; +using Key = uint64_t; +// bonus: define the piece enums here +enum class PolyglotPiece : uint8_t; +enum class EnginePiece : uint8_t; +enum class ContiguousMappingPiece : uint8_t; +} // namespace chess \ No newline at end of file diff --git a/position.cpp b/position.cpp index a5b6e8d..ed4aeea 100644 --- a/position.cpp +++ b/position.cpp @@ -113,9 +113,8 @@ template template void _Position(ep_sq - pawn_push(c)); const Bitboard ep_mask = (1ULL << ep_pawn_sq) | (1ULL << ep_sq); - // Bitboard _ir_1 = occ() & ~ep_mask; // ASSUME(popcount(candidates) <= 32); - if (popcount(candidates)>2) throw std::invalid_argument("Too many ep candidates??? i think there's only 2?"); + while (candidates) { Square from = static_cast(pop_lsb(candidates)); @@ -125,11 +124,8 @@ template template void _Position() &~ep_mask); - // atks |= attacks::knight(king_sq) & pieces(); atks |= attacks::bishop(king_sq, occ_temp) & (pieces() | pieces()); atks |= attacks::rook(king_sq, occ_temp) & (pieces() | pieces()); - // atks |= attacks::king(king_sq) & pieces(); atks &= occ(~c); if (!atks) { ep_moves.push_back(Move::make(from, ep_sq)); @@ -292,46 +288,43 @@ template template voi moves &= occ(~c); _chess::splat_moves(out.data() + out.size(), kingSq, moves); out.size_ += popcount(moves); - // clang-format off if constexpr (!capturesOnly) { - if (checkers()) return; + if (checkers()) + return; + + // Precompute all castling masks + constexpr Bitboard WHITE_OO_EMPTY = (1ULL << SQ_F1) | (1ULL << SQ_G1); + constexpr Bitboard WHITE_OO_SAFE = (1ULL << SQ_F1) | (1ULL << SQ_G1); + constexpr Bitboard WHITE_OOO_EMPTY = (1ULL << SQ_B1) | (1ULL << SQ_C1) | (1ULL << SQ_D1); + constexpr Bitboard WHITE_OOO_SAFE = (1ULL << SQ_C1) | (1ULL << SQ_D1); + + constexpr Bitboard BLACK_OO_EMPTY = (1ULL << SQ_F8) | (1ULL << SQ_G8); + constexpr Bitboard BLACK_OO_SAFE = (1ULL << SQ_F8) | (1ULL << SQ_G8); + constexpr Bitboard BLACK_OOO_EMPTY = (1ULL << SQ_B8) | (1ULL << SQ_C8) | (1ULL << SQ_D8); + constexpr Bitboard BLACK_OOO_SAFE = (1ULL << SQ_C8) | (1ULL << SQ_D8); + + Bitboard occupancy = occ(); + Bitboard enemy_attacks = enemyAttacks; if constexpr (c == WHITE) { - // Kingside - if ((current_state.castlingRights & WHITE_OO) - && !(occ() & ((1ULL << SQ_F1) | (1ULL << SQ_G1))) - && !(enemyAttacks & ((1ULL << SQ_F1) | (1ULL << SQ_G1)))) - { + if ((current_state.castlingRights & WHITE_OO) && !(occupancy & WHITE_OO_EMPTY) && !(enemy_attacks & WHITE_OO_SAFE)) { out.push_back(Move::make(SQ_E1, SQ_H1)); } - // Queenside - if ((current_state.castlingRights & WHITE_OOO) - && !(occ() & ((1ULL << SQ_B1) | (1ULL << SQ_C1) | (1ULL << SQ_D1))) - && !(enemyAttacks & ((1ULL << SQ_C1) | (1ULL << SQ_D1)))) - { + if ((current_state.castlingRights & WHITE_OOO) && !(occupancy & WHITE_OOO_EMPTY) && !(enemy_attacks & WHITE_OOO_SAFE)) { out.push_back(Move::make(SQ_E1, SQ_A1)); } - } else { // BLACK - // Kingside - if ((current_state.castlingRights & BLACK_OO) - && !(occ() & ((1ULL << SQ_F8) | (1ULL << SQ_G8))) - && !(enemyAttacks & ((1ULL << SQ_F8) | (1ULL << SQ_G8)))) - { + } else { + if ((current_state.castlingRights & BLACK_OO) && !(occupancy & BLACK_OO_EMPTY) && !(enemy_attacks & BLACK_OO_SAFE)) { out.push_back(Move::make(SQ_E8, SQ_H8)); } - // Queenside - if ((current_state.castlingRights & BLACK_OOO) - && !(occ() & ((1ULL << SQ_B8) | (1ULL << SQ_C8) | (1ULL << SQ_D8))) - && !(enemyAttacks & ((1ULL << SQ_C8) | (1ULL << SQ_D8)))) - { + if ((current_state.castlingRights & BLACK_OOO) && !(occupancy & BLACK_OOO_EMPTY) && !(enemy_attacks & BLACK_OOO_SAFE)) { out.push_back(Move::make(SQ_E8, SQ_A8)); } } } - // clang-format on } template template void _Position::doMove(const Move &move) { - + assert(move.is_ok() && "doMove called with invalid move"); Square from_sq = move.from_sq(), to_sq = move.to_sq(); Color us = side_to_move(), them = ~us; MoveType move_type = move.type_of(); @@ -342,10 +335,14 @@ template template void _Position template void _Position(ep_capture_sq, them); placePiece(to_sq, us); + current_state.incr_sqs[0] = from_sq; + current_state.incr_pc[0] = moving_piece; + current_state.incr_sqs[1] = ep_capture_sq; + current_state.incr_pc[1] = make_piece(PAWN, them); + current_state.incr_sqs[2] = to_sq; + current_state.incr_pc[2] = PieceC::NO_PIECE; break; } case CASTLING: { @@ -377,6 +388,14 @@ template template void _Position(rook_dest, us); placePiece(king_dest, us); + current_state.incr_sqs[0] = from_sq; + current_state.incr_pc[0] = moving_piece; + current_state.incr_sqs[1] = to_sq; + current_state.incr_pc[1] = target_piece; + current_state.incr_sqs[2] = king_dest; + current_state.incr_pc[2] = PieceC::NO_PIECE; + current_state.incr_sqs[3] = rook_dest; + current_state.incr_pc[3] = PieceC::NO_PIECE; break; } default: @@ -448,70 +467,6 @@ template template void _Position template auto _Position::undoMove() -> std::conditional_t &, void> { - // Save only the move (we'll restore the full state next) - const Move move = current_state.mv; - - // Make sure there's something to pop - assert(history.size_ > 0 && "undoMove called with empty history"); - - // Restore previous state from history - current_state = history.pop(); - - // After restore, sideToMove() is the mover - const Color mover = sideToMove(); - // Squares to update - Square sqs[4]; - int n_sqs = 0; - sqs[n_sqs++] = move.from_sq(); - sqs[n_sqs++] = move.to_sq(); - switch (auto type = move.type_of()) { - case CASTLING: { - bool is_king_side = file_of(move.to_sq()) > file_of(move.from_sq()); - sqs[n_sqs++] = relative_square(mover, is_king_side ? SQ_G1 : SQ_C1); // king to - sqs[n_sqs++] = relative_square(mover, is_king_side ? SQ_F1 : SQ_D1); // rook to - break; - } - case EN_PASSANT: { - sqs[n_sqs++] = static_cast(static_cast(move.to_sq()) - pawn_push(mover)); - break; - } - default: - break; - } - // Update all squares - ASSUME(n_sqs <= 4); - for (int i = 0; i < n_sqs; ++i) { - - const Square sq = sqs[i]; - Bitboard sq_bb = 1ULL << sq; - - if ((occ() & sq_bb) == 0) { - pieces_list[sq] = PieceC::NO_PIECE; - continue; - } - - // Determine color first - Color pc = (current_state.occ[WHITE] & sq_bb) ? WHITE : BLACK; - - const Bitboard *p = current_state.pieces; - // clang-format off - - PieceType pt = (p[PAWN] & sq_bb) ? PAWN : - (p[KNIGHT] & sq_bb) ? KNIGHT : - (p[BISHOP] & sq_bb) ? BISHOP : - (p[ROOK] & sq_bb) ? ROOK : - (p[QUEEN] & sq_bb) ? QUEEN : - (p[KING] & sq_bb) ? KING : NO_PIECE_TYPE; // fallback, though should never happen - // clang-format on - pieces_list[sq] = make_piece(pt, pc); - } - - if constexpr (RetAll) { - return current_state; - } -} - template void _Position::setFEN(const std::string &str) { current_state = HistoryEntry(); history.clear(); @@ -1090,8 +1045,6 @@ template void _Position::genSlidingMoves::genSlidingMoves(Movelist &) const; \ template _Position::_Position(std::string); \ template void _Position::setFEN(const std::string &); \ -template void _Position::undoMove(); \ -template HistoryEntry &_Position::undoMove(); \ template std::string _Position::fen() const; \ template void _Position::doMove(const Move &move); \ template void _Position::doMove(const Move &move); \ diff --git a/position.h b/position.h index 281558d..9f6f01f 100644 --- a/position.h +++ b/position.h @@ -53,6 +53,8 @@ template struct alignas(64) HistoryEntry { Square enPassant = SQ_NONE; // En passant target square Square kings[COLOR_NB] = { SQ_NONE }; CastlingRights castlingRights; // Castling rights bitmask + Square incr_sqs[4] = { SQ_NONE, SQ_NONE, SQ_NONE, SQ_NONE }; + Piece incr_pc[4] = { Piece::NO_PIECE, Piece::NO_PIECE, Piece::NO_PIECE, Piece::NO_PIECE }; // implementation-specific implementations goes here }; @@ -167,8 +169,8 @@ template void doMove(const Move &move); - template auto undoMove() -> std::conditional_t &, void>; + template inline auto undoMove() -> std::conditional_t &, void> { + assert(history.size_ > 0 && "undoMove called with empty history"); + + // Restore previous state from history + assert(current_state.mv.is_ok() && "Corrupted history entry"); + assert(current_state.incr_pc[0] != at(current_state.mv.from()) && "Corrupted history entry"); + pieces_list[current_state.incr_sqs[0]] = current_state.incr_pc[0]; + pieces_list[current_state.incr_sqs[1]] = current_state.incr_pc[1]; + pieces_list[current_state.incr_sqs[2]] = current_state.incr_pc[2]; + pieces_list[current_state.incr_sqs[3]] = current_state.incr_pc[3]; + current_state = history.pop(); + if constexpr (RetAll) { + return current_state; + } + } + __FORCEINLINE void doNullMove() { history.push_back(current_state); diff --git a/printers.cpp b/printers.cpp index 96b2bdf..2f90ef6 100644 --- a/printers.cpp +++ b/printers.cpp @@ -8,7 +8,7 @@ namespace chess { template using DescriptiveNameNotation = std::unordered_map; -template std::ostream &operator<<(std::ostream &os, const _Position &pos) { +template std::ostream &operator<<(std::ostream &os, const _Position &pos) { // RAII guard to save/restore stream state struct ios_guard { std::ostream &strm; @@ -23,15 +23,11 @@ template std::ostream &operator<<(std::ostream &os, const _Pos } } guard(os); - constexpr std::string_view EnginePieceToChar(" PNBRQK pnbrqk "); - constexpr std::string_view PolyglotPieceToChar("PNBRQKpnbrqk "); - constexpr std::string_view PieceToChar = std::is_same_v ? EnginePieceToChar : PolyglotPieceToChar; - os << "\n +---+---+---+---+---+---+---+---+\n"; for (Rank r = RANK_8; r >= RANK_1; --r) { for (File f = FILE_A; f <= FILE_H; ++f) - os << " | " << PieceToChar[static_cast(pos.piece_on(make_sq(r, f)))]; + os << " | " << pos.piece_on(make_sq(r, f)); os << " | " << (1 + r) << "\n +---+---+---+---+---+---+---+---+\n"; } @@ -77,32 +73,22 @@ std::ostream &operator<<(std::ostream &os, const CastlingRights &cr) { return os << castlingFlags[cr]; } static std::string str_toupper(std::string s) { - std::transform(s.begin(), - s.end(), - s.begin(), - // static_cast(std::toupper) // wrong - // [](int c){ return std::toupper(c); } // wrong - // [](char c){ return std::toupper(c); } // wrong - [](unsigned char c) { return std::toupper(c); } // correct - ); + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::toupper(c); }); return s; } std::ostream &operator<<(std::ostream &os, const Square &sq) { os << "SQ_" << str_toupper(chess::uci::squareToString(sq)); return os; } -std::ostream &operator<<(std::ostream &os, const PolyglotPiece &p) { - constexpr std::string_view EnginePieceToChar(".PNBRQK'/pnbrqk,"); - constexpr std::string_view PolyglotPieceToChar("PNBRQKpnbrqk."); - constexpr std::string_view PieceToChar = std::is_same_v ? EnginePieceToChar : PolyglotPieceToChar; - return os << PieceToChar[(int)p]; -} -std::ostream &operator<<(std::ostream &os, const EnginePiece &p) { - constexpr std::string_view EnginePieceToChar(".PNBRQK'/pnbrqk,"); - constexpr std::string_view PolyglotPieceToChar("PNBRQKpnbrqk."); - constexpr std::string_view PieceToChar = std::is_same_v ? EnginePieceToChar : PolyglotPieceToChar; - return os << PieceToChar[(int)p]; +template std::ostream & operator<<(std::ostream &os, PieceC p) { + constexpr std::string_view mapping = " PNBRQK..pnbrqk."; + return os << mapping[static_cast(make_piece(piece_of(p), color_of(p)))]; } -template std::ostream &operator<<(std::ostream &, const _Position &); -template std::ostream &operator<<(std::ostream &, const _Position &); + +#define INSTANTITATE(PieceC) \ +template std::ostream &operator<<(std::ostream &, const _Position &); + +INSTANTITATE(EnginePiece) +INSTANTITATE(PolyglotPiece) +INSTANTITATE(ContiguousMappingPiece) } // namespace chess \ No newline at end of file diff --git a/printers.h b/printers.h index 1bdcef6..ef5977b 100644 --- a/printers.h +++ b/printers.h @@ -1,20 +1,15 @@ #pragma once #include #include +#include +#include "fwd_decl.h" namespace chess { -class Move; -enum Color : uint8_t; -enum CastlingRights : int8_t; -enum class PolyglotPiece : uint8_t; -enum class EnginePiece : uint8_t; -enum Square : int8_t; 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); -std::ostream &operator<<(std::ostream &os, const PolyglotPiece &p); -std::ostream &operator<<(std::ostream &os, const EnginePiece &p); +template ::value>> std::ostream &operator<<(std::ostream &os, PieceC p); std::ostream &operator<<(std::ostream &os, const Square &sq); template class _Position; -template std::ostream &operator<<(std::ostream &os, const _Position &pos); +template ::value>> std::ostream &operator<<(std::ostream &os, const _Position &pos); } // namespace chess \ No newline at end of file diff --git a/tests.cpp b/tests.cpp index bd28690..1027162 100644 --- a/tests.cpp +++ b/tests.cpp @@ -614,43 +614,57 @@ TEST_CASE("Minor position test perft") { { "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1", 3, 13744 }, { "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1", 4, 314346 }, { "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1", 5, 7594526 }, +#if IS_RELEASE == 1 { "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1", 6, 179862938 }, +#endif { "r3k2r/8/8/8/8/8/8/1R2K2R w Kkq - 0 1", 1, 25 }, { "r3k2r/8/8/8/8/8/8/1R2K2R w Kkq - 0 1", 2, 567 }, { "r3k2r/8/8/8/8/8/8/1R2K2R w Kkq - 0 1", 3, 14095 }, { "r3k2r/8/8/8/8/8/8/1R2K2R w Kkq - 0 1", 4, 328965 }, { "r3k2r/8/8/8/8/8/8/1R2K2R w Kkq - 0 1", 5, 8153719 }, +#if IS_RELEASE == 1 { "r3k2r/8/8/8/8/8/8/1R2K2R w Kkq - 0 1", 6, 195629489 }, +#endif { "r3k2r/8/8/8/8/8/8/2R1K2R w Kkq - 0 1", 1, 25 }, { "r3k2r/8/8/8/8/8/8/2R1K2R w Kkq - 0 1", 2, 548 }, { "r3k2r/8/8/8/8/8/8/2R1K2R w Kkq - 0 1", 3, 13502 }, { "r3k2r/8/8/8/8/8/8/2R1K2R w Kkq - 0 1", 4, 312835 }, { "r3k2r/8/8/8/8/8/8/2R1K2R w Kkq - 0 1", 5, 7736373 }, +#if IS_RELEASE == 1 { "r3k2r/8/8/8/8/8/8/2R1K2R w Kkq - 0 1", 6, 184411439 }, +#endif { "r3k2r/8/8/8/8/8/8/R3K1R1 w Qkq - 0 1", 1, 25 }, { "r3k2r/8/8/8/8/8/8/R3K1R1 w Qkq - 0 1", 2, 547 }, { "r3k2r/8/8/8/8/8/8/R3K1R1 w Qkq - 0 1", 3, 13579 }, { "r3k2r/8/8/8/8/8/8/R3K1R1 w Qkq - 0 1", 4, 316214 }, { "r3k2r/8/8/8/8/8/8/R3K1R1 w Qkq - 0 1", 5, 7878456 }, +#if IS_RELEASE == 1 { "r3k2r/8/8/8/8/8/8/R3K1R1 w Qkq - 0 1", 6, 189224276 }, +#endif { "1r2k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1", 1, 26 }, { "1r2k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1", 2, 583 }, { "1r2k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1", 3, 14252 }, { "1r2k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1", 4, 334705 }, { "1r2k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1", 5, 8198901 }, +#if IS_RELEASE == 1 { "1r2k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1", 6, 198328929 }, +#endif { "2r1k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1", 1, 25 }, { "2r1k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1", 2, 560 }, { "2r1k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1", 3, 13592 }, { "2r1k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1", 4, 317324 }, { "2r1k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1", 5, 7710115 }, +#if IS_RELEASE == 1 { "2r1k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1", 6, 185959088 }, +#endif { "r3k1r1/8/8/8/8/8/8/R3K2R w KQq - 0 1", 1, 25 }, { "r3k1r1/8/8/8/8/8/8/R3K2R w KQq - 0 1", 2, 560 }, { "r3k1r1/8/8/8/8/8/8/R3K2R w KQq - 0 1", 3, 13607 }, { "r3k1r1/8/8/8/8/8/8/R3K2R w KQq - 0 1", 4, 320792 }, { "r3k1r1/8/8/8/8/8/8/R3K2R w KQq - 0 1", 5, 7848606 }, +#if IS_RELEASE == 1 { "r3k1r1/8/8/8/8/8/8/R3K2R w KQq - 0 1", 6, 190755813 }, +#endif { "4k3/8/8/8/8/8/8/4K2R b K - 0 1", 1, 5 }, { "4k3/8/8/8/8/8/8/4K2R b K - 0 1", 2, 75 }, { "4k3/8/8/8/8/8/8/4K2R b K - 0 1", 3, 459 }, @@ -716,43 +730,57 @@ TEST_CASE("Minor position test perft") { { "r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1", 3, 13744 }, { "r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1", 4, 314346 }, { "r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1", 5, 7594526 }, +#if IS_RELEASE == 1 { "r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1", 6, 179862938 }, +#endif { "r3k2r/8/8/8/8/8/8/1R2K2R b Kkq - 0 1", 1, 26 }, { "r3k2r/8/8/8/8/8/8/1R2K2R b Kkq - 0 1", 2, 583 }, { "r3k2r/8/8/8/8/8/8/1R2K2R b Kkq - 0 1", 3, 14252 }, { "r3k2r/8/8/8/8/8/8/1R2K2R b Kkq - 0 1", 4, 334705 }, { "r3k2r/8/8/8/8/8/8/1R2K2R b Kkq - 0 1", 5, 8198901 }, +#if IS_RELEASE == 1 { "r3k2r/8/8/8/8/8/8/1R2K2R b Kkq - 0 1", 6, 198328929 }, +#endif { "r3k2r/8/8/8/8/8/8/2R1K2R b Kkq - 0 1", 1, 25 }, { "r3k2r/8/8/8/8/8/8/2R1K2R b Kkq - 0 1", 2, 560 }, { "r3k2r/8/8/8/8/8/8/2R1K2R b Kkq - 0 1", 3, 13592 }, { "r3k2r/8/8/8/8/8/8/2R1K2R b Kkq - 0 1", 4, 317324 }, { "r3k2r/8/8/8/8/8/8/2R1K2R b Kkq - 0 1", 5, 7710115 }, +#if IS_RELEASE == 1 { "r3k2r/8/8/8/8/8/8/2R1K2R b Kkq - 0 1", 6, 185959088 }, +#endif { "r3k2r/8/8/8/8/8/8/R3K1R1 b Qkq - 0 1", 1, 25 }, { "r3k2r/8/8/8/8/8/8/R3K1R1 b Qkq - 0 1", 2, 560 }, { "r3k2r/8/8/8/8/8/8/R3K1R1 b Qkq - 0 1", 3, 13607 }, { "r3k2r/8/8/8/8/8/8/R3K1R1 b Qkq - 0 1", 4, 320792 }, { "r3k2r/8/8/8/8/8/8/R3K1R1 b Qkq - 0 1", 5, 7848606 }, +#if IS_RELEASE == 1 { "r3k2r/8/8/8/8/8/8/R3K1R1 b Qkq - 0 1", 6, 190755813 }, +#endif { "1r2k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1", 1, 25 }, { "1r2k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1", 2, 567 }, { "1r2k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1", 3, 14095 }, { "1r2k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1", 4, 328965 }, { "1r2k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1", 5, 8153719 }, +#if IS_RELEASE == 1 { "1r2k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1", 6, 195629489 }, +#endif { "2r1k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1", 1, 25 }, { "2r1k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1", 2, 548 }, { "2r1k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1", 3, 13502 }, { "2r1k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1", 4, 312835 }, { "2r1k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1", 5, 7736373 }, +#if IS_RELEASE == 1 { "2r1k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1", 6, 184411439 }, +#endif { "r3k1r1/8/8/8/8/8/8/R3K2R b KQq - 0 1", 1, 25 }, { "r3k1r1/8/8/8/8/8/8/R3K2R b KQq - 0 1", 2, 547 }, { "r3k1r1/8/8/8/8/8/8/R3K2R b KQq - 0 1", 3, 13579 }, { "r3k1r1/8/8/8/8/8/8/R3K2R b KQq - 0 1", 4, 316214 }, { "r3k1r1/8/8/8/8/8/8/R3K2R b KQq - 0 1", 5, 7878456 }, +#if IS_RELEASE == 1 { "r3k1r1/8/8/8/8/8/8/R3K2R b KQq - 0 1", 6, 189224276 }, +#endif { "8/1n4N1/2k5/8/8/5K2/1N4n1/8 w - - 0 1", 1, 14 }, { "8/1n4N1/2k5/8/8/5K2/1N4n1/8 w - - 0 1", 2, 195 }, { "8/1n4N1/2k5/8/8/5K2/1N4n1/8 w - - 0 1", 3, 2760 }, @@ -1422,6 +1450,18 @@ TEST_CASE("Experienced bugs in this repo") { 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)); diff --git a/types.h b/types.h index 6818203..3417514 100644 --- a/types.h +++ b/types.h @@ -3,7 +3,7 @@ #include #include #include -#include +#include "fwd_decl.h" #if defined(_MSC_VER) #define UNREACHABLE() __assume(false) #elif defined(__clang__) || defined(__GNUC__) @@ -166,7 +166,7 @@ enum class PolyglotPiece : uint8_t { WPAWN = 1, WKNIGHT = 3, WBISHOP = 5, WROOK // Normal board, you can use ANY! (but comfortable for certain chess engines such as Stockfish) enum class EnginePiece : uint8_t { NO_PIECE, WPAWN = PAWN + 0, WKNIGHT, WBISHOP, WROOK, WQUEEN, WKING, BPAWN = PAWN + 8, BKNIGHT, BBISHOP, BROOK, BQUEEN, BKING, PIECE_NB = 16 }; enum class ContiguousMappingPiece : uint8_t { WPAWN = 0, WKNIGHT = 1, WBISHOP = 2, WROOK = 3, WQUEEN = 4, WKING = 5, BPAWN = 6, BKNIGHT = 7, BBISHOP = 8, BROOK = 9, BQUEEN = 10, BKING = 11, NO_PIECE = 12, PIECE_NB = 12 }; -template size_t enum_idx() { +template constexpr size_t enum_idx() { if constexpr (std::is_same_v) return 0; else if constexpr (std::is_same_v) @@ -179,15 +179,14 @@ template size_t enum_idx() { constexpr PieceType piece_of(PolyglotPiece p) { return p == decltype(p)::NO_PIECE ? NO_PIECE_TYPE : static_cast(static_cast(p) / 2 + 1); } constexpr PieceType piece_of(EnginePiece p) { return p == decltype(p)::NO_PIECE ? NO_PIECE_TYPE : static_cast((static_cast(p) - 1) % 8 + 1); } constexpr PieceType piece_of(ContiguousMappingPiece p) { return p == decltype(p)::NO_PIECE ? NO_PIECE_TYPE : static_cast(static_cast(p) % 6 + 1); } -constexpr PieceType type_of(PolyglotPiece p) { return piece_of(p); } -constexpr PieceType type_of(EnginePiece p) { return piece_of(p); } -constexpr PieceType type_of(ContiguousMappingPiece p) { return piece_of(p); } constexpr Color color_of(PolyglotPiece pt) { return static_cast((static_cast(pt) + 1) % 2); } constexpr Color color_of(EnginePiece pt) { return static_cast(static_cast(pt) / static_cast(EnginePiece::BPAWN)); } constexpr Color color_of(ContiguousMappingPiece pt) { return static_cast(static_cast(pt) / 6); } template , bool> = 0> constexpr EnginePiece make_piece(PieceType pt, Color c) { return static_cast((c << 3) + pt); } template , bool> = 0> constexpr PolyglotPiece make_piece(PieceType pt, Color c) { return static_cast(~c + 2 * (pt - 1)); } template , bool> = 0> constexpr ContiguousMappingPiece make_piece(PieceType pt, Color c) { return static_cast((static_cast(pt) - 1) + 6 * static_cast(c)); } +template ::value>> constexpr PieceType type_of(T p) { return piece_of(p); } + enum CastlingRights : int8_t { NO_CASTLING, WHITE_OO, @@ -219,7 +218,7 @@ inline CastlingRights &operator&=(CastlingRights &a, CastlingRights b) { } inline CastlingRights operator~(CastlingRights a) { return static_cast(static_cast(a) ^ ANY_CASTLING); } -enum MoveType { NORMAL, PROMOTION = 1 << 14, EN_PASSANT = 2 << 14, CASTLING = 3 << 14 }; +enum MoveType : uint16_t { NORMAL, PROMOTION = 1 << 14, EN_PASSANT = 2 << 14, CASTLING = 3 << 14 }; // A move needs 16 bits to be stored // // bit 0- 5: destination square (from 0 to 63) From 333a58071f83649e328ca21a367b6118f4947e7e Mon Sep 17 00:00:00 2001 From: winapiadmin Date: Sat, 22 Nov 2025 10:55:31 +0700 Subject: [PATCH 2/2] fix no testers --- .github/workflows/cmake-multi-platform.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 5be825a..d56b3ac 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -61,7 +61,11 @@ jobs: run: cmake --build ${{ steps.vars.outputs.dir }} --config ${{ matrix.build_type }} - name: Test - working-directory: ${{ steps.strings.outputs.build-output-dir }} - # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest --build-config ${{ matrix.build_type }} --verbose + working-directory: ${{ steps.vars.outputs.dir }} + shell: bash + run: | + if [[ "${{ matrix.os }}" == "windows-latest" ]]; then + ctest --build-config ${{ matrix.build_type }} --verbose + else + ctest --verbose + fi \ No newline at end of file