diff --git a/attacks.cpp b/attacks.cpp index 733a42f..ee5a025 100644 --- a/attacks.cpp +++ b/attacks.cpp @@ -93,7 +93,7 @@ namespace chess::attacks { #ifndef GENERATE_AT_RUNTIME #define _POSSIBLY_CONSTEXPR constexpr #else -#define _POSSIBLY_CONSTEXPR const +#define _POSSIBLY_CONSTEXPR #endif // clang-format off _POSSIBLY_CONSTEXPR std::array RookMagics = { @@ -140,7 +140,6 @@ _POSSIBLY_CONSTEXPR std::pair, std::array attacks{}; size_t offset = 0; - for (Square sq = SQ_A1; sq < SQ_NONE; ++sq) { Bitboard occ = 0; Bitboard edges = ((attacks::MASK_RANK[0] | attacks::MASK_RANK[7]) & ~attacks::MASK_RANK[rank_of(sq)]) | @@ -165,6 +164,7 @@ _POSSIBLY_CONSTEXPR std::pair, std::array(sq), occ); @@ -173,7 +173,6 @@ _POSSIBLY_CONSTEXPR std::pair, std::array, std::array> bishopData = @@ -185,6 +184,26 @@ _POSSIBLY_CONSTEXPR std::pair, std::array(); _POSSIBLY_CONSTEXPR std::array RookTable = rookData.first; _POSSIBLY_CONSTEXPR std::array RookAttacks = rookData.second; +/** + * @brief Returns the bishop attacks for a given square + * @param sq + * @param occupied + * @return + */ +[[nodiscard]] Bitboard bishop(Square sq, Bitboard occupied) { + return BishopAttacks[BishopTable[(int)sq].index + BishopTable[(int)sq](occupied)]; +} + +/** + * @brief Returns the rook attacks for a given square + * @param sq + * @param occupied + * @return + */ +[[nodiscard]] Bitboard rook(Square sq, Bitboard occupied) { + return RookAttacks[RookTable[(int)sq].index + RookTable[(int)sq](occupied)]; +} + } // namespace chess::attacks namespace chess::movegen { inline static Bitboard att(PieceType pt, Square sq, Bitboard occ) { @@ -208,4 +227,4 @@ inline static std::array, SQ_NONE + 1> generat return squares_between_bb; } std::array, SQ_NONE + 1> SQUARES_BETWEEN_BB = generate_between(); -} // namespace chess::movegen \ No newline at end of file +} // namespace chess::movegen diff --git a/attacks.h b/attacks.h index fb90680..c3e1654 100644 --- a/attacks.h +++ b/attacks.h @@ -23,6 +23,7 @@ #include "types.h" #include #include +#include namespace chess::attacks { // clang-format off // pre-calculated lookup table for pawn attacks @@ -110,13 +111,9 @@ namespace chess::attacks { 0x101010101010101, 0x202020202020202, 0x404040404040404, 0x808080808080808, 0x1010101010101010, 0x2020202020202020, 0x4040404040404040, 0x8080808080808080, }; - extern const std::array BishopMagics; - extern const std::array RookMagics; // clang-format on #ifdef __BMI2__ constexpr uint64_t software_pext_u64(uint64_t val, uint64_t mask) { - if (!is_constant_evaluated()) - return ~0ULL; uint64_t result = 0; uint64_t bit_position = 0; @@ -153,10 +150,6 @@ struct Magic { } // namespace chess::attacks namespace chess::attacks { -extern const std::array RookTable; -extern const std::array RookAttacks; -extern const std::array BishopTable; -extern const std::array BishopAttacks; /** * @brief Shifts a bitboard in a given direction * @tparam direction @@ -293,16 +286,13 @@ template [[nodiscard]] constexpr Bitboard pawn(const Bitboard pawns) { Bitboard h2 = l2 | r2; // 2-square horizontal shifts return (h1 << 16) | (h1 >> 16) | (h2 << 8) | (h2 >> 8); // vertical shifts: +2,+1,-2,-1 } - /** * @brief Returns the bishop attacks for a given square * @param sq * @param occupied * @return */ -[[nodiscard]] constexpr Bitboard bishop(Square sq, Bitboard occupied) { - return BishopAttacks[BishopTable[(int)sq].index + BishopTable[(int)sq](occupied)]; -} +[[nodiscard]] Bitboard bishop(Square sq, Bitboard occupied); /** * @brief Returns the rook attacks for a given square @@ -310,17 +300,14 @@ template [[nodiscard]] constexpr Bitboard pawn(const Bitboard pawns) { * @param occupied * @return */ -[[nodiscard]] constexpr Bitboard rook(Square sq, Bitboard occupied) { - return RookAttacks[RookTable[(int)sq].index + RookTable[(int)sq](occupied)]; -} - +[[nodiscard]] Bitboard rook(Square sq, Bitboard occupied); /** * @brief Returns the queen attacks for a given square * @param sq * @param occupied * @return */ -[[nodiscard]] constexpr Bitboard queen(Square sq, Bitboard occupied) { return bishop(sq, occupied) | rook(sq, occupied); } +[[nodiscard]] inline Bitboard queen(Square sq, Bitboard occupied) { return bishop(sq, occupied) | rook(sq, occupied); } /** * @brief Returns the king attacks for a given square @@ -336,7 +323,7 @@ template [[nodiscard]] constexpr Bitboard pawn(const Bitboard pawns) { * @tparam pt * @return */ -template [[nodiscard]] constexpr Bitboard slider(Square sq, Bitboard occupied) { +template [[nodiscard]] inline Bitboard slider(Square sq, Bitboard occupied) { static_assert(pt == PieceType::BISHOP || pt == PieceType::ROOK || pt == PieceType::QUEEN, "PieceType must be a slider!"); if constexpr (pt == PieceType::BISHOP) diff --git a/chess960_tests.cpp b/chess960_tests.cpp index eec59dc..b8bde42 100644 --- a/chess960_tests.cpp +++ b/chess960_tests.cpp @@ -115,10 +115,10 @@ auto split_testcases(std::vector> &entries) { size_t n1 = std::min(bucket1.size(), size_t(2000)); optimized.insert(optimized.end(), bucket1.begin(), bucket1.begin() + n1); - size_t n2 = std::min(bucket2.size(), size_t(30)); + size_t n2 = std::min(bucket2.size(), size_t(15)); optimized.insert(optimized.end(), bucket2.begin(), bucket2.begin() + n2); - size_t n3 = std::min(bucket3.size(), size_t(5)); + size_t n3 = std::min(bucket3.size(), size_t(1)); if (n3 > 0) { optimized.insert(optimized.end(), bucket3.end() - n3, bucket3.end()); } @@ -137,12 +137,6 @@ void check_perfts(std::vector> &entries) { auto start_time = high_resolution_clock::now(); for (auto &entry : entries) { std::cerr << entry.input << " (chess960=true) " << entry.info.depth; -#if !IS_RELEASE - if (entry.info.nodes > 1e6) { - std::cerr << "(skipped)\n"; - continue; - } -#endif std::cerr << '\n'; { _Position pos(entry.input, true); diff --git a/tests.cpp b/tests.cpp index 3a4d970..6eb9ec8 100644 --- a/tests.cpp +++ b/tests.cpp @@ -22,6 +22,7 @@ #include "printers.h" #include #include +#include using namespace chess; // --------- Color assertions ---------- static_assert(color_of(PolyglotPiece::BPAWN) == BLACK, "BPAWN should be BLACK"); @@ -254,19 +255,46 @@ template uint64_t perft(_Po return total; } } +#if !IS_RELEASE +auto split_testcases(std::vector> &entries) { + std::vector> optimized; + + std::sort(entries.begin(), entries.end(), [](const auto &a, const auto &b) { return a.info.nodes < b.info.nodes; }); + + std::vector> bucket1, bucket2, bucket3; + for (const auto &e : entries) { + if (e.info.nodes <= 1'000'000) + bucket1.push_back(e); + else if (e.info.nodes <= 10'000'000) + bucket2.push_back(e); + else if (e.info.nodes <= 1'000'000'000) + bucket3.push_back(e); + } + + size_t n1 = std::min(bucket1.size(), size_t(2000)); + optimized.insert(optimized.end(), bucket1.begin(), bucket1.begin() + n1); + + size_t n2 = std::min(bucket2.size(), size_t(15)); + optimized.insert(optimized.end(), bucket2.begin(), bucket2.begin() + n2); + + size_t n3 = std::min(bucket3.size(), size_t(1)); + if (n3 > 0) { + optimized.insert(optimized.end(), bucket3.end() - n3, bucket3.end()); + } + + return optimized; +} +#endif template -void check_perfts(const std::vector> &entries) { +void check_perfts(std::vector> &entries) { uint64_t nodes = 0; double elapsed = 0; using namespace std::chrono; - for (auto &entry : entries) { - std::cerr << entry.input << " (chess960=false) " << entry.info.depth; #if !IS_RELEASE - if (entry.info.nodes > 1e6) { - std::cerr << "(skipped)\n"; - continue; - } + entries = split_testcases(entries); #endif + for (auto &entry : entries) { + std::cerr << entry.input << " (chess960=false) " << entry.info.depth; std::cerr << '\n'; { _Position pos(entry.input); @@ -329,6 +357,50 @@ void check_perfts(const std::vector> &entries) { double mnps = (nodes / elapsed) / 1'000'000.0; std::cout << "Speed: " << mnps << "Mnps\n"; } + +template [[nodiscard]] inline Bitboard sliderAttacks(Square sq, Bitboard occupied) noexcept { + static constexpr Direction dirs[2][4][2] = { + { EAST, EAST, EAST, WEST, WEST, WEST, WEST, EAST }, + { EAST, DIR_NONE, DIR_NONE, WEST, WEST, DIR_NONE, DIR_NONE, EAST } + }; + + Bitboard attacks = 0ull; + + File pf = file_of(sq); + Rank pr = rank_of(sq); + + for (int i = 0; i < 4; ++i) { + Direction off_f = dirs[ISROOK][i][0]; + Direction off_r = dirs[ISROOK][i][1]; + + File f = pf + off_f; + Rank r = pr + off_r; + for (; is_valid(r, f); f += off_f, r += off_r) { + const auto index = make_sq(f, r); + attacks |= 1ULL << index; + if (occupied & (1ULL << index)) + break; + } + } + + return attacks; +} +TEST_CASE("attacks") { + std::mt19937_64 gen(19937); + std::uniform_int_distribution dis; + for (Square sq = SQ_A1; sq < SQ_NONE; sq++) { + for (int i = 0; i < 100; i++) { + uint64_t bb = dis(gen) & dis(gen) & dis(gen); + REQUIRE(attacks::bishop(sq, bb) == sliderAttacks(sq, bb)); + } + } + for (Square sq = SQ_A1; sq < SQ_NONE; sq++) { + for (int i = 0; i < 100; i++) { + uint64_t bb = dis(gen) & dis(gen) & dis(gen); + REQUIRE(attacks::rook(sq, bb) == sliderAttacks(sq, bb)); + } + } +} TEST_CASE("Castling move making FEN rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8") { std::string fen = "rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8"; Position position(fen); diff --git a/types.h b/types.h index f88af18..612fad9 100644 --- a/types.h +++ b/types.h @@ -190,7 +190,15 @@ constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); } constexpr Square &operator+=(Square &s, Direction d) { return s = s + d; } constexpr Square &operator-=(Square &s, Direction d) { return s = s - d; } -// specifically for Polyglot (a.k.a zobrist hashing but use proper hash) +constexpr Rank operator+(Rank s, Direction d) { return Rank(int(s) + int(d)); } +constexpr Rank operator-(Rank s, Direction d) { return Rank(int(s) - int(d)); } +constexpr Rank &operator+=(Rank &s, Direction d) { return s = s + d; } +constexpr Rank &operator-=(Rank &s, Direction d) { return s = s - d; } +constexpr File operator+(File s, Direction d) { return File(int(s) + int(d)); } +constexpr File operator-(File s, Direction d) { return File(int(s) - int(d)); } +constexpr File &operator+=(File &s, Direction d) { return s = s + d; } +constexpr File &operator-=(File &s, Direction d) { return s = s - d; } + enum class PolyglotPiece : uint8_t { WPAWN = 1, WKNIGHT = 3, @@ -207,7 +215,6 @@ enum class PolyglotPiece : uint8_t { NO_PIECE = 12, PIECE_NB = 12 }; -// 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,