Skip to content
Merged
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
Empty file.
58 changes: 54 additions & 4 deletions include/bitbishop/color.hpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,69 @@
#pragma once
#include <array>
#include <format>

/**
* @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<Color> for formatting support
* @see std::formatter<Color> for formatting support with std::format
* @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 color The color to convert
* @return 0 for Color::BLACK, 1 for Color::WHITE
*/
constexpr std::size_t to_index(Color color) { return static_cast<std::size_t>(color); }

/**
* @brief Returns the opposite color.
*
* @param color The input color
* @return Color::WHITE if color is Color::BLACK, otherwise Color::BLACK
*/
constexpr Color opposite(Color color) { return color == 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<Color, 2> 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.
*
Expand Down
45 changes: 45 additions & 0 deletions include/bitbishop/lookups/attackers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once
#include <bitbishop/bitboard.hpp>
#include <bitbishop/board.hpp>
#include <bitbishop/color.hpp>
#include <bitbishop/lookups/bishop_rays.hpp>
#include <bitbishop/lookups/king_attacks.hpp>
#include <bitbishop/lookups/knight_attacks.hpp>
#include <bitbishop/lookups/pawn_attacks.hpp>
#include <bitbishop/lookups/queen_rays.hpp>
#include <bitbishop/lookups/rook_rays.hpp>
#include <bitbishop/square.hpp>

constexpr Bitboard attackers_to(Square target, Color color) {
using namespace Lookups;
const int square_index = target.value();

Bitboard attackers;
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 |= ROOK_ATTACKER_RAYS[square_index];
attackers |= BISHOP_ATTACKER_RAYS[square_index];
attackers |= QUEEN_ATTACKER_RAYS[square_index];

return attackers;
}

constexpr std::array<std::array<Bitboard, Const::BOARD_SIZE>, ColorUtil::size()> ATTACKERS_TO = []() constexpr {
using namespace Const;

std::array<std::array<Bitboard, BOARD_SIZE>, ColorUtil::size()> table{};
for (Color col : ColorUtil::all()) {
const std::size_t coli = ColorUtil::to_index(col);
for (int sq = 0; sq < BOARD_SIZE; ++sq) {
table[coli][sq] = Bitboard(attackers_to(Square(sq, std::in_place), col));
}
}
return table;
}();
18 changes: 18 additions & 0 deletions include/bitbishop/lookups/bishop_rays.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,22 @@ constexpr std::array<Bitboard, Const::BOARD_SIZE> 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<Bitboard, Const::BOARD_SIZE> BISHOP_ATTACKER_RAYS = BISHOP_RAYS;

} // namespace Lookups
17 changes: 17 additions & 0 deletions include/bitbishop/lookups/king_attacks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,21 @@ constexpr std::array<Bitboard, Const::BOARD_SIZE> 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<Bitboard, Const::BOARD_SIZE> KING_ATTACKERS = KING_ATTACKS;

} // namespace Lookups
17 changes: 17 additions & 0 deletions include/bitbishop/lookups/knight_attacks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,21 @@ constexpr std::array<Bitboard, Const::BOARD_SIZE> 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<Bitboard, Const::BOARD_SIZE> KNIGHT_ATTACKERS = KNIGHT_ATTACKS;

} // namespace Lookups
38 changes: 38 additions & 0 deletions include/bitbishop/lookups/pawn_attacks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,42 @@ constexpr std::array<Bitboard, Const::BOARD_SIZE> 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<Bitboard, 64> 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<Bitboard, 64> BLACK_PAWN_ATTACKERS = WHITE_PAWN_ATTACKS;

} // namespace Lookups
18 changes: 18 additions & 0 deletions include/bitbishop/lookups/queen_rays.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,22 @@ constexpr std::array<Bitboard, Const::BOARD_SIZE> 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<Bitboard, Const::BOARD_SIZE> QUEEN_ATTACKER_RAYS = QUEEN_RAYS;

} // namespace Lookups
18 changes: 18 additions & 0 deletions include/bitbishop/lookups/rook_rays.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,22 @@ constexpr std::array<Bitboard, Const::BOARD_SIZE> 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<Bitboard, Const::BOARD_SIZE> ROOK_ATTACKER_RAYS = ROOK_RAYS;

} // namespace Lookups
43 changes: 43 additions & 0 deletions tests/bitbishop/lookups/test_attackers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include <gtest/gtest.h>

#include <bitbishop/color.hpp>
#include <bitbishop/lookups/attackers.hpp>
#include <bitbishop/square.hpp>

/**
* @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_TRUE(white_attackers.any());
EXPECT_TRUE(black_attackers.any());
}

/**
* @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<int>(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));
}