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
52 changes: 51 additions & 1 deletion include/bitbishop/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ class Board {
*/
Board();

Board(const Board&) = default;

/**
* @brief Constructs a board from a FEN string.
* @param fen Forsyth–Edwards Notation describing a chess position.
Expand All @@ -77,6 +79,19 @@ class Board {
*/
[[nodiscard]] std::optional<Piece> get_piece(Square square) const;

/**
* @brief Moves a piece from one square to another.
*
* The function will move the piece of any type/color from `from` to `to`.
* If a piece already exists on `to`, it is removed (captured).
* If no piece exists on `from`, sinlently returns.
* If `from` and `to` are identical, silently returns.
*
* @param from Source square
* @param to Destination square
*/
void move_piece(Square from, Square to);

/**
* @brief (Re)Places a piece on a given square.
* @param square The square where the piece will be placed.
Expand Down Expand Up @@ -130,6 +145,18 @@ class Board {
*/
[[nodiscard]] Bitboard unoccupied() const { return ~occupied(); }

/**
* @brief Returns the square of the king for the given color.
*
* In standard chess, a king is always present. However, for tests, variants,
* or incomplete board setups, the king may be missing. To handle this safely,
* the result is returned as a std::optional.
*
* @param us Color of the king
* @return std::optional<Square> containing the king's square if present, std::nullopt otherwise
*/
[[nodiscard]] std::optional<Square> king_square(Color us) const { return king(us).pop_lsb(); }

/**
* @brief Returns a bitboard representing all pawns belonging to the given side to move.
*
Expand Down Expand Up @@ -254,4 +281,27 @@ class Board {
* @return true if queenside castling is legal, false otherwise
*/
[[nodiscard]] bool can_castle_queenside(Color side) const noexcept;
};

Board& operator=(const Board& other) = default;

/**
* @brief Checks if two boards represent the same chess position.
*
* Compares piece placement, side to move, en passant square, and castling rights.
* Ignores half-move clock and full-move number.
*
* @param other The board to compare against
* @return true if positions are identical, false otherwise
*/
[[nodiscard]] bool operator==(const Board& other) const;

/**
* @brief Checks if two boards represent different positions.
*
* Logical negation of operator==().
*
* @param other The board to compare against
* @return true if positions differ, false otherwise
*/
[[nodiscard]] bool operator!=(const Board& other) const;
};
111 changes: 109 additions & 2 deletions include/bitbishop/lookups/pawn_attacks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <array>
#include <bitbishop/bitboard.hpp>
#include <bitbishop/bitmasks.hpp>
#include <bitbishop/color.hpp>
#include <bitbishop/constants.hpp>
#include <cstdint>

Expand Down Expand Up @@ -167,7 +168,7 @@ constexpr std::array<Bitboard, Const::BOARD_SIZE> BLACK_PAWN_ATTACKS = []() cons
*
* Indexed by target square (0–63).
*/
constexpr std::array<Bitboard, 64> WHITE_PAWN_ATTACKERS = BLACK_PAWN_ATTACKS;
constexpr std::array<Bitboard, Const::BOARD_SIZE> WHITE_PAWN_ATTACKERS = BLACK_PAWN_ATTACKS;

/**
* @brief Precomputed bitboards of BLACK pawn attackers (reverse pawn attacks).
Expand All @@ -186,6 +187,112 @@ constexpr std::array<Bitboard, 64> WHITE_PAWN_ATTACKERS = BLACK_PAWN_ATTACKS;
*
* Indexed by target square (0–63).
*/
constexpr std::array<Bitboard, 64> BLACK_PAWN_ATTACKERS = WHITE_PAWN_ATTACKS;
constexpr std::array<Bitboard, Const::BOARD_SIZE> BLACK_PAWN_ATTACKERS = WHITE_PAWN_ATTACKS;

/**
* @brief Precomputed single-push destinations for pawns of both colors.
*
* For each color and source square, contains a bitboard with the destination
* square if a pawn moves one square forward:
* - White pawns move north (+1 rank)
* - Black pawns move south (-1 rank)
*
* This table represents theoretical move destinations only.
* The caller must ensure:
* - The destination square is empty
* - The move satisfies check and pin constraints
*
* Indexed as [color][source_square].
*/
constexpr std::array<std::array<Bitboard, Const::BOARD_SIZE>, ColorUtil::size()> PAWN_SINGLE_PUSH = []() constexpr {
using namespace Const;

std::array<std::array<Bitboard, BOARD_SIZE>, ColorUtil::size()> table{};

for (Color color : ColorUtil::all()) {
const auto idx = ColorUtil::to_index(color);
switch (color) {
case Color::WHITE:
table[idx] = WHITE_PAWN_SINGLE_PUSH;
break;
case Color::BLACK:
table[idx] = BLACK_PAWN_SINGLE_PUSH;
break;
}
}

return table;
}();

/**
* @brief Precomputed double-push destinations for pawns of both colors.
*
* For each color and source square, contains a bitboard with the destination
* square if a pawn moves two squares forward from its starting rank:
* - White: rank 2 β†’ rank 4
* - Black: rank 7 β†’ rank 5
*
* This table represents theoretical move destinations only.
* The caller must ensure:
* - The pawn is on its starting rank
* - Both the intermediate and destination squares are empty
* - The move satisfies check and pin constraints
*
* Indexed as [color][source_square].
*/
constexpr std::array<std::array<Bitboard, Const::BOARD_SIZE>, ColorUtil::size()> PAWN_DOUBLE_PUSH = []() constexpr {
using namespace Const;

std::array<std::array<Bitboard, BOARD_SIZE>, ColorUtil::size()> table{};

for (Color color : ColorUtil::all()) {
const auto idx = ColorUtil::to_index(color);
switch (color) {
case Color::WHITE:
table[idx] = WHITE_PAWN_DOUBLE_PUSH;
break;
case Color::BLACK:
table[idx] = BLACK_PAWN_DOUBLE_PUSH;
break;
}
}

return table;
}();

/**
* @brief Precomputed diagonal capture targets for pawns of both colors.
*
* For each color and source square, contains a bitboard with all squares
* a pawn could capture to diagonally:
* - White: north-west and north-east
* - Black: south-west and south-east
*
* This table is independent of board occupancy.
* The caller must verify that:
* - An enemy piece occupies the target square, or
* - The square corresponds to a valid en passant target
*
* Indexed as [color][source_square].
*/
constexpr std::array<std::array<Bitboard, Const::BOARD_SIZE>, ColorUtil::size()> PAWN_ATTACKS = []() constexpr {
using namespace Const;

std::array<std::array<Bitboard, BOARD_SIZE>, ColorUtil::size()> table{};

for (Color color : ColorUtil::all()) {
const auto idx = ColorUtil::to_index(color);
switch (color) {
case Color::WHITE:
table[idx] = WHITE_PAWN_ATTACKS;
break;
case Color::BLACK:
table[idx] = BLACK_PAWN_ATTACKS;
break;
}
}

return table;
}();

} // namespace Lookups
Loading