Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions attacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint64_t, 64> RookMagics = {
Expand Down Expand Up @@ -140,7 +140,6 @@ _POSSIBLY_CONSTEXPR std::pair<std::array<Magic, 64>, std::array<Bitboard, TableS
std::array<Bitboard, TableSize> 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)]) |
Expand All @@ -165,6 +164,7 @@ _POSSIBLY_CONSTEXPR std::pair<std::array<Magic, 64>, std::array<Bitboard, TableS

// Carry-rippler loop over all blocker subsets
occ = 0;

do {
size_t idx = entry(occ);
attacks[offset + idx] = AttackFunc(static_cast<Square>(sq), occ);
Expand All @@ -173,7 +173,6 @@ _POSSIBLY_CONSTEXPR std::pair<std::array<Magic, 64>, std::array<Bitboard, TableS

offset += (1ULL << bits);
}

return std::pair{ table, attacks };
}
_POSSIBLY_CONSTEXPR std::pair<std::array<Magic, 64>, std::array<Bitboard, 0x1480>> bishopData =
Expand All @@ -185,6 +184,26 @@ _POSSIBLY_CONSTEXPR std::pair<std::array<Magic, 64>, std::array<Bitboard, 0x1900
generate_magic_table<_chess::_HyperbolaRookAttacks, 0x19000, false>();
_POSSIBLY_CONSTEXPR std::array<Magic, 64> RookTable = rookData.first;
_POSSIBLY_CONSTEXPR std::array<Bitboard, 0x19000> 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) {
Expand All @@ -208,4 +227,4 @@ inline static std::array<std::array<Bitboard, SQ_NONE + 1>, SQ_NONE + 1> generat
return squares_between_bb;
}
std::array<std::array<Bitboard, SQ_NONE + 1>, SQ_NONE + 1> SQUARES_BETWEEN_BB = generate_between();
} // namespace chess::movegen
} // namespace chess::movegen
23 changes: 5 additions & 18 deletions attacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "types.h"
#include <array>
#include <immintrin.h>
#include <vector>
namespace chess::attacks {
// clang-format off
// pre-calculated lookup table for pawn attacks
Expand Down Expand Up @@ -110,13 +111,9 @@ namespace chess::attacks {
0x101010101010101, 0x202020202020202, 0x404040404040404, 0x808080808080808,
0x1010101010101010, 0x2020202020202020, 0x4040404040404040, 0x8080808080808080,
};
extern const std::array<uint64_t, 64> BishopMagics;
extern const std::array<uint64_t, 64> 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;

Expand Down Expand Up @@ -153,10 +150,6 @@ struct Magic {

} // namespace chess::attacks
namespace chess::attacks {
extern const std::array<Magic, 64> RookTable;
extern const std::array<Bitboard, 0x19000> RookAttacks;
extern const std::array<Magic, 64> BishopTable;
extern const std::array<Bitboard, 0x1480> BishopAttacks;
/**
* @brief Shifts a bitboard in a given direction
* @tparam direction
Expand Down Expand Up @@ -293,34 +286,28 @@ template <Color c> [[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
* @param sq
* @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
Expand All @@ -336,7 +323,7 @@ template <Color c> [[nodiscard]] constexpr Bitboard pawn(const Bitboard pawns) {
* @tparam pt
* @return
*/
template <PieceType pt> [[nodiscard]] constexpr Bitboard slider(Square sq, Bitboard occupied) {
template <PieceType pt> [[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)
Expand Down
8 changes: 1 addition & 7 deletions chess960_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ auto split_testcases(std::vector<TestEntry<std::string, perft_t>> &entries) {
size_t n2 = std::min(bucket2.size(), size_t(30));
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());
}
Expand All @@ -137,12 +137,6 @@ void check_perfts(std::vector<TestEntry<std::string, perft_t>> &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<PolyglotPiece> pos(entry.input, true);
Expand Down
86 changes: 79 additions & 7 deletions tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "printers.h"
#include <chrono>
#include <doctest/doctest.h>
#include <random>
using namespace chess;
// --------- Color assertions ----------
static_assert(color_of(PolyglotPiece::BPAWN) == BLACK, "BPAWN should be BLACK");
Expand Down Expand Up @@ -254,19 +255,46 @@ template <typename T, MoveGenType mt, bool EnableDiv = false> uint64_t perft(_Po
return total;
}
}
#if !IS_RELEASE
auto split_testcases(std::vector<TestEntry<std::string, perft_t>> &entries) {
std::vector<TestEntry<std::string, perft_t>> optimized;

std::sort(entries.begin(), entries.end(), [](const auto &a, const auto &b) { return a.info.nodes < b.info.nodes; });

std::vector<TestEntry<std::string, perft_t>> 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 <MoveGenType mt = MoveGenType::ALL, bool EnableDiv = false>
void check_perfts(const std::vector<TestEntry<std::string, perft_t>> &entries) {
void check_perfts(std::vector<TestEntry<std::string, perft_t>> &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);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore the debug perft cap

In a default CMake test build (empty CMAKE_BUILD_TYPE, so NDEBUG is not defined and IS_RELEASE is 0), this replaces the old > 1e6 skip with split_testcases, which keeps 30 cases up to 10M nodes and five cases up to 1B nodes. With the current test data that is about 2.7B expected nodes per piece mapping before the loop repeats for the three piece representations, so test_normal can run for an impractical amount of time or time out instead of remaining a quick debug check; keep the cap or bound the sampled buckets to debug-sized cases.

Useful? React with 👍 / 👎.

#endif
for (auto &entry : entries) {
std::cerr << entry.input << " (chess960=false) " << entry.info.depth;
std::cerr << '\n';
{
_Position<PolyglotPiece> pos(entry.input);
Expand Down Expand Up @@ -329,6 +357,50 @@ void check_perfts(const std::vector<TestEntry<std::string, perft_t>> &entries) {
double mnps = (nodes / elapsed) / 1'000'000.0;
std::cout << "Speed: " << mnps << "Mnps\n";
}

template <bool ISROOK> [[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<uint64_t> dis;
for (Square sq = SQ_A1; sq < SQ_NONE; sq++) {
for (int i = 0; i < 10000; i++) {
uint64_t bb = dis(gen) & dis(gen);
REQUIRE(attacks::bishop(sq, bb) == sliderAttacks<false>(sq, bb));
}
}
for (Square sq = SQ_A1; sq < SQ_NONE; sq++) {
for (int i = 0; i < 10000; i++) {
uint64_t bb = dis(gen) & dis(gen);
REQUIRE(attacks::rook(sq, bb) == sliderAttacks<true>(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);
Expand Down
11 changes: 9 additions & 2 deletions types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
Loading