From 02e839ea262ce75cc5527a4194f9282cf5bfe3bc Mon Sep 17 00:00:00 2001 From: Baptiste Penot Date: Fri, 26 Dec 2025 01:18:07 +0100 Subject: [PATCH 1/5] added color utils --- include/bitbishop/color.hpp | 56 ++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/include/bitbishop/color.hpp b/include/bitbishop/color.hpp index 2cf0221..e87f05e 100644 --- a/include/bitbishop/color.hpp +++ b/include/bitbishop/color.hpp @@ -5,15 +5,63 @@ * @brief Depicts possible piece colors in the chess game. * * This enumeration represents the two player colors in chess. - * It can be used with std::format for automatic string conversion. + * It is intentionally compact and suitable for use as an array index. * - * @see std::formatter for formatting support + * @see ColorUtil for helper utilities operating on Color values */ enum class Color : std::uint8_t { - BLACK, ///< Black pieces/player - WHITE ///< White pieces/player + BLACK, ///< Black pieces / player + WHITE ///< White pieces / player }; +/** + * @brief Utility functions operating on the Color enum. + * + * This namespace groups constexpr helpers related to color manipulation, + * such as iteration, indexing, and color inversion. Keeping these helpers + * separate from the enum avoids polluting the global namespace while + * preserving a clear and expressive API. + */ +namespace ColorUtil { + +/** + * @brief Converts a Color value to a zero-based array index. + * + * This is primarily intended for indexing color-dependent lookup tables + * and arrays. + * + * @param c The color to convert + * @return 0 for Color::BLACK, 1 for Color::WHITE + */ +constexpr std::size_t to_index(Color c) { return static_cast(c); } + +/** + * @brief Returns the opposite color. + * + * @param c The input color + * @return Color::WHITE if c is Color::BLACK, otherwise Color::BLACK + */ +constexpr Color opposite(Color c) { return c == Color::WHITE ? Color::BLACK : Color::WHITE; } + +/** + * @brief Returns all valid Color values. + * + * This function enables constexpr-friendly iteration over all colors + * without relying on magic numbers or assumptions about enum layout. + * + * @return An array containing { Color::BLACK, Color::WHITE } + */ +constexpr std::array all() { return {Color::BLACK, Color::WHITE}; } + +/** + * @brief Returns the number of colors. + * + * @return 2 (the number of Color enum entries) + */ +constexpr std::size_t size() { return 2; } + +} // namespace ColorUtil + /** * @brief Custom formatter for Color enum to enable std::format support. * From 3f5c3ebc1427ff2081b7f58ff08fa51de2641fd5 Mon Sep 17 00:00:00 2001 From: Baptiste Penot Date: Fri, 26 Dec 2025 01:18:37 +0100 Subject: [PATCH 2/5] implemented compile time lookup table to retrieve attackers for a given square --- include/bitbishop/attacks/attackers_to.hpp | 0 include/bitbishop/lookups/attackers.hpp | 45 ++++++++++++++++++++ include/bitbishop/lookups/bishop_rays.hpp | 18 ++++++++ include/bitbishop/lookups/king_attacks.hpp | 17 ++++++++ include/bitbishop/lookups/knight_attacks.hpp | 17 ++++++++ include/bitbishop/lookups/pawn_attacks.hpp | 38 +++++++++++++++++ include/bitbishop/lookups/queen_rays.hpp | 18 ++++++++ include/bitbishop/lookups/rook_rays.hpp | 18 ++++++++ tests/bitbishop/lookups/test_attackers.cpp | 43 +++++++++++++++++++ 9 files changed, 214 insertions(+) delete mode 100644 include/bitbishop/attacks/attackers_to.hpp create mode 100644 include/bitbishop/lookups/attackers.hpp create mode 100644 tests/bitbishop/lookups/test_attackers.cpp diff --git a/include/bitbishop/attacks/attackers_to.hpp b/include/bitbishop/attacks/attackers_to.hpp deleted file mode 100644 index e69de29..0000000 diff --git a/include/bitbishop/lookups/attackers.hpp b/include/bitbishop/lookups/attackers.hpp new file mode 100644 index 0000000..103e1c9 --- /dev/null +++ b/include/bitbishop/lookups/attackers.hpp @@ -0,0 +1,45 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +constexpr Bitboard attackers_to(Square target, Color color) { + using namespace Lookups; + const int square_index = target.value(); + + Bitboard attackers; + attackers |= BISHOP_ATTACKER_RAYS[square_index]; + attackers |= KING_ATTACKERS[square_index]; + attackers |= KNIGHT_ATTACKERS[square_index]; + + if (color == Color::WHITE) { + attackers |= WHITE_PAWN_ATTACKERS[square_index]; + } else { + attackers |= BLACK_PAWN_ATTACKERS[square_index]; + } + + attackers |= QUEEN_ATTACKER_RAYS[square_index]; + attackers |= ROOK_ATTACKER_RAYS[square_index]; + + return attackers; +} + +constexpr std::array, ColorUtil::size()> ATTACKERS_TO = []() constexpr { + using namespace Const; + + std::array, ColorUtil::size()> table{}; + for (Color c : ColorUtil::all()) { + const std::size_t ci = ColorUtil::to_index(c); + for (int sq = 0; sq < BOARD_SIZE; ++sq) { + table[ci][sq] = Bitboard(attackers_to(Square(sq, std::in_place), c)); + } + } + return table; +}(); diff --git a/include/bitbishop/lookups/bishop_rays.hpp b/include/bitbishop/lookups/bishop_rays.hpp index 2e5aa2f..c401cf8 100644 --- a/include/bitbishop/lookups/bishop_rays.hpp +++ b/include/bitbishop/lookups/bishop_rays.hpp @@ -203,4 +203,22 @@ constexpr std::array BISHOP_SOUTHWEST_RAYS = []() c return table; }(); +/** + * @brief Precomputed bitboards of bishop attackers (ray-based). + * + * For each target square, this table contains a bitboard of all squares + * from which a bishop could attack that square, assuming an empty board. + * + * Bishop attack geometry: + * - Attacks are ray-based (diagonal directions) + * - Directional and not symmetric + * - Independent of board occupancy at this level + * + * For sliding pieces, the set of potential attackers to a square is + * exactly the set of squares lying on the corresponding rays. + * + * Indexed by target square (0–63). + */ +constexpr std::array BISHOP_ATTACKER_RAYS = BISHOP_RAYS; + } // namespace Lookups diff --git a/include/bitbishop/lookups/king_attacks.hpp b/include/bitbishop/lookups/king_attacks.hpp index b6578d4..67f43e1 100644 --- a/include/bitbishop/lookups/king_attacks.hpp +++ b/include/bitbishop/lookups/king_attacks.hpp @@ -58,4 +58,21 @@ constexpr std::array KING_ATTACKS = []() constexpr return table; }(); +/** + * @brief Precomputed bitboards of king attackers. + * + * For each target square, this table contains a bitboard of all squares + * from which a king could attack that square. + * + * King attack geometry: + * - Attacks are symmetric (attackers == attacks) + * - No directionality + * - Independent of board occupancy + * + * As a result, the king attacker table is identical to the king attack table. + * + * Indexed by target square (0–63). + */ +constexpr std::array KING_ATTACKERS = KING_ATTACKS; + } // namespace Lookups \ No newline at end of file diff --git a/include/bitbishop/lookups/knight_attacks.hpp b/include/bitbishop/lookups/knight_attacks.hpp index 578c040..0e6ed8d 100644 --- a/include/bitbishop/lookups/knight_attacks.hpp +++ b/include/bitbishop/lookups/knight_attacks.hpp @@ -51,4 +51,21 @@ constexpr std::array KNIGHT_ATTACKS = []() constexp return table; }(); +/** + * @brief Precomputed bitboards of knight attackers. + * + * For each target square, this table contains a bitboard of all squares + * from which a knight could attack that square. + * + * Knight attack geometry: + * - Attacks are symmetric (attackers == attacks) + * - No directionality + * - Independent of board occupancy + * + * As a result, the knight attacker table is identical to the knight attack table. + * + * Indexed by target square (0–63). + */ +constexpr std::array KNIGHT_ATTACKERS = KNIGHT_ATTACKS; + } // namespace Lookups \ No newline at end of file diff --git a/include/bitbishop/lookups/pawn_attacks.hpp b/include/bitbishop/lookups/pawn_attacks.hpp index db8ff88..2525621 100644 --- a/include/bitbishop/lookups/pawn_attacks.hpp +++ b/include/bitbishop/lookups/pawn_attacks.hpp @@ -131,4 +131,42 @@ constexpr std::array BLACK_PAWN_ATTACKS = []() cons return table; }(); +/** + * @brief Precomputed bitboards of WHITE pawn attackers (reverse pawn attacks). + * + * For each target square, this table contains a bitboard of all squares + * from which a WHITE pawn could attack that square. + * + * Important notes about pawn attack geometry: + * - Pawn attacks are directional and asymmetric + * - Pawn attackers are NOT equal to pawn attacks from the same square + * - White pawn attackers are equivalent to black pawn attacks + * when viewed from the target square + * + * This table is independent of board occupancy and contains only + * pure geometric information. + * + * Indexed by target square (0–63). + */ +constexpr std::array WHITE_PAWN_ATTACKERS = BLACK_PAWN_ATTACKS; + +/** + * @brief Precomputed bitboards of BLACK pawn attackers (reverse pawn attacks). + * + * For each target square, this table contains a bitboard of all squares + * from which a BLACK pawn could attack that square. + * + * Important notes about pawn attack geometry: + * - Pawn attacks are directional and asymmetric + * - Pawn attackers are NOT equal to pawn attacks from the same square + * - Black pawn attackers are equivalent to white pawn attacks + * when viewed from the target square + * + * This table is independent of board occupancy and contains only + * pure geometric information. + * + * Indexed by target square (0–63). + */ +constexpr std::array BLACK_PAWN_ATTACKERS = WHITE_PAWN_ATTACKS; + } // namespace Lookups \ No newline at end of file diff --git a/include/bitbishop/lookups/queen_rays.hpp b/include/bitbishop/lookups/queen_rays.hpp index ac39e14..36714e9 100644 --- a/include/bitbishop/lookups/queen_rays.hpp +++ b/include/bitbishop/lookups/queen_rays.hpp @@ -40,4 +40,22 @@ constexpr std::array QUEEN_RAYS = []() constexpr { return table; }(); +/** + * @brief Precomputed bitboards of queen attackers (composite ray-based). + * + * For each target square, this table contains a bitboard of all squares + * from which a queen could attack that square, assuming an empty board. + * + * Queen attack geometry: + * - Attacks are ray-based (diagonal + orthogonal directions) + * - Directional and not symmetric + * - Independent of board occupancy at this level + * + * Queen attackers are defined as the union of rook attackers and + * bishop attackers for the same target square. + * + * Indexed by target square (0–63). + */ +constexpr std::array QUEEN_ATTACKER_RAYS = QUEEN_RAYS; + } // namespace Lookups diff --git a/include/bitbishop/lookups/rook_rays.hpp b/include/bitbishop/lookups/rook_rays.hpp index af60a27..e6d7d17 100644 --- a/include/bitbishop/lookups/rook_rays.hpp +++ b/include/bitbishop/lookups/rook_rays.hpp @@ -189,4 +189,22 @@ constexpr std::array ROOK_WEST_RAYS = []() constexp return table; }(); +/** + * @brief Precomputed bitboards of rook attackers (ray-based). + * + * For each target square, this table contains a bitboard of all squares + * from which a rook could attack that square, assuming an empty board. + * + * Rook attack geometry: + * - Attacks are ray-based (orthogonal directions) + * - Directional and not symmetric + * - Independent of board occupancy at this level + * + * For sliding pieces, the set of potential attackers to a square is + * exactly the set of squares lying on the corresponding rays. + * + * Indexed by target square (0–63). + */ +constexpr std::array ROOK_ATTACKER_RAYS = ROOK_RAYS; + } // namespace Lookups diff --git a/tests/bitbishop/lookups/test_attackers.cpp b/tests/bitbishop/lookups/test_attackers.cpp new file mode 100644 index 0000000..bb5e02b --- /dev/null +++ b/tests/bitbishop/lookups/test_attackers.cpp @@ -0,0 +1,43 @@ +#include + +#include +#include +#include + +/** + * @brief Tests that attackers_to returns non-empty bitboards for obvious central squares. + */ +TEST(AttackersToTest, CentralSquareHasAttackers) { + Bitboard white_attackers = attackers_to(Squares::E4, Color::WHITE); + Bitboard black_attackers = attackers_to(Squares::E4, Color::BLACK); + + EXPECT_NE(white_attackers, 0ULL); + EXPECT_NE(black_attackers, 0ULL); +} + +/** + * @brief Tests that ATTACKERS_TO table matches direct attackers_to calls. + */ +TEST(AttackersToTest, TableMatchesFunction) { + using namespace Const; + + for (Color c : ColorUtil::all()) { + const std::size_t ci = ColorUtil::to_index(c); + for (int sq = 0; sq < BOARD_SIZE; ++sq) { + Square square(sq); + EXPECT_EQ(ATTACKERS_TO[ci][sq], attackers_to(square, c)) + << "Mismatch for square " << sq << " color " << static_cast(c); + } + } +} + +/** + * @brief Tests pawn attackers specifically for edge files. + */ +TEST(AttackersToTest, PawnEdgeFiles) { + Bitboard white_attackers = attackers_to(Squares::A2, Color::WHITE); + Bitboard black_attackers = attackers_to(Squares::H7, Color::BLACK); + + EXPECT_TRUE(white_attackers.test(Squares::B3)); + EXPECT_TRUE(black_attackers.test(Squares::G6)); +} From 5d1160677f36622f7dc9c2310cba8141a5f63ccc Mon Sep 17 00:00:00 2001 From: Baptiste Penot Date: Fri, 26 Dec 2025 01:26:56 +0100 Subject: [PATCH 3/5] fixed linting and tests --- include/bitbishop/color.hpp | 10 +++++----- include/bitbishop/lookups/attackers.hpp | 6 +++--- tests/bitbishop/lookups/test_attackers.cpp | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/bitbishop/color.hpp b/include/bitbishop/color.hpp index e87f05e..b61914b 100644 --- a/include/bitbishop/color.hpp +++ b/include/bitbishop/color.hpp @@ -30,18 +30,18 @@ namespace ColorUtil { * This is primarily intended for indexing color-dependent lookup tables * and arrays. * - * @param c The color to convert + * @param color The color to convert * @return 0 for Color::BLACK, 1 for Color::WHITE */ -constexpr std::size_t to_index(Color c) { return static_cast(c); } +constexpr std::size_t to_index(Color color) { return static_cast(color); } /** * @brief Returns the opposite color. * - * @param c The input color - * @return Color::WHITE if c is Color::BLACK, otherwise Color::BLACK + * @param color The input color + * @return Color::WHITE if color is Color::BLACK, otherwise Color::BLACK */ -constexpr Color opposite(Color c) { return c == Color::WHITE ? Color::BLACK : Color::WHITE; } +constexpr Color opposite(Color color) { return color == Color::WHITE ? Color::BLACK : Color::WHITE; } /** * @brief Returns all valid Color values. diff --git a/include/bitbishop/lookups/attackers.hpp b/include/bitbishop/lookups/attackers.hpp index 103e1c9..8b74dc5 100644 --- a/include/bitbishop/lookups/attackers.hpp +++ b/include/bitbishop/lookups/attackers.hpp @@ -35,10 +35,10 @@ constexpr std::array, ColorUtil::size()> using namespace Const; std::array, ColorUtil::size()> table{}; - for (Color c : ColorUtil::all()) { - const std::size_t ci = ColorUtil::to_index(c); + for (Color col : ColorUtil::all()) { + const std::size_t coli = ColorUtil::to_index(col); for (int sq = 0; sq < BOARD_SIZE; ++sq) { - table[ci][sq] = Bitboard(attackers_to(Square(sq, std::in_place), c)); + table[coli][sq] = Bitboard(attackers_to(Square(sq, std::in_place), col)); } } return table; diff --git a/tests/bitbishop/lookups/test_attackers.cpp b/tests/bitbishop/lookups/test_attackers.cpp index bb5e02b..9d1bcac 100644 --- a/tests/bitbishop/lookups/test_attackers.cpp +++ b/tests/bitbishop/lookups/test_attackers.cpp @@ -11,8 +11,8 @@ TEST(AttackersToTest, CentralSquareHasAttackers) { Bitboard white_attackers = attackers_to(Squares::E4, Color::WHITE); Bitboard black_attackers = attackers_to(Squares::E4, Color::BLACK); - EXPECT_NE(white_attackers, 0ULL); - EXPECT_NE(black_attackers, 0ULL); + EXPECT_TRUE(white_attackers.any()); + EXPECT_TRUE(black_attackers.any()); } /** From 30f584c42cd42bb596ad1b6095e5a1811cf979bf Mon Sep 17 00:00:00 2001 From: Baptiste Penot Date: Fri, 26 Dec 2025 01:34:50 +0100 Subject: [PATCH 4/5] added include array for msvc build --- include/bitbishop/color.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/bitbishop/color.hpp b/include/bitbishop/color.hpp index b61914b..d184908 100644 --- a/include/bitbishop/color.hpp +++ b/include/bitbishop/color.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include /** @@ -7,6 +8,7 @@ * This enumeration represents the two player colors in chess. * It is intentionally compact and suitable for use as an array index. * + * @see std::formatter for formatting support with std::format * @see ColorUtil for helper utilities operating on Color values */ enum class Color : std::uint8_t { From bc0709d4c829816e009bc7da10aa8808173726dc Mon Sep 17 00:00:00 2001 From: Baptiste Penot Date: Fri, 26 Dec 2025 01:35:14 +0100 Subject: [PATCH 5/5] fixed ordering in attackers to separate slinding and non sliding pieces --- include/bitbishop/lookups/attackers.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/bitbishop/lookups/attackers.hpp b/include/bitbishop/lookups/attackers.hpp index 8b74dc5..08644a5 100644 --- a/include/bitbishop/lookups/attackers.hpp +++ b/include/bitbishop/lookups/attackers.hpp @@ -15,7 +15,6 @@ constexpr Bitboard attackers_to(Square target, Color color) { const int square_index = target.value(); Bitboard attackers; - attackers |= BISHOP_ATTACKER_RAYS[square_index]; attackers |= KING_ATTACKERS[square_index]; attackers |= KNIGHT_ATTACKERS[square_index]; @@ -25,8 +24,9 @@ constexpr Bitboard attackers_to(Square target, Color color) { attackers |= BLACK_PAWN_ATTACKERS[square_index]; } - attackers |= QUEEN_ATTACKER_RAYS[square_index]; attackers |= ROOK_ATTACKER_RAYS[square_index]; + attackers |= BISHOP_ATTACKER_RAYS[square_index]; + attackers |= QUEEN_ATTACKER_RAYS[square_index]; return attackers; }