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
28 changes: 28 additions & 0 deletions include/bitbishop/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,4 +211,32 @@ class Board {
[[nodiscard]] bool has_queenside_castling_rights(Color side) const {
return (side == Color::WHITE) ? m_white_castle_queenside : m_black_castle_queenside;
}

/**
* @brief Checks if kingside castling is legal.
*
* Kingside castling (also called short castling or O-O) is legal when:
* - Neither the king nor the kingside rook have previously moved
* - All squares between the king and the kingside rook are empty
* - The king is not currently in check
* - The king does not pass through or land on a square that is under attack
*
* @param side Color of the side to check
* @return true if kingside castling is legal, false otherwise
*/
[[nodiscard]] bool can_castle_kingside(Color side) const noexcept;

/**
* @brief Checks if queenside castling is legal.
*
* Queenside castling (also called long castling or O-O-O) is legal when:
* - Neither the king nor the queenside rook have previously moved
* - All squares between the king and the queenside rook are empty
* - The king is not currently in check
* - The king does not pass through or land on a square that is under attack
*
* @param side Color of the side to check
* @return true if queenside castling is legal, false otherwise
*/
[[nodiscard]] bool can_castle_queenside(Color side) const noexcept;
};
30 changes: 0 additions & 30 deletions include/bitbishop/moves/king_move_gen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,34 +55,4 @@ void generate_legal_moves(std::vector<Move>& moves, const Board& board, Color si
*/
void add_king_castling(std::vector<Move>& moves, Square from, Color side, const Board& board);

/**
* @brief Checks if kingside castling is legal.
*
* Kingside castling (also called short castling or O-O) is legal when:
* - Neither the king nor the kingside rook have previously moved
* - All squares between the king and the kingside rook are empty
* - The king is not currently in check
* - The king does not pass through or land on a square that is under attack
*
* @param board Current board state
* @param side Color of the side to check
* @return true if kingside castling is legal, false otherwise
*/
bool can_castle_kingside(const Board& board, Color side);

/**
* @brief Checks if queenside castling is legal.
*
* Queenside castling (also called long castling or O-O-O) is legal when:
* - Neither the king nor the queenside rook have previously moved
* - All squares between the king and the queenside rook are empty
* - The king is not currently in check
* - The king does not pass through or land on a square that is under attack
*
* @param board Current board state
* @param side Color of the side to check
* @return true if queenside castling is legal, false otherwise
*/
bool can_castle_queenside(const Board& board, Color side);

}; // namespace KingMoveGenerator
51 changes: 51 additions & 0 deletions src/bitbishop/board.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,54 @@ void Board::print() const {
}
std::cout << " a b c d e f g h\n"; // file labels
}

bool Board::can_castle_kingside(Color side) const noexcept {
if (!this->has_kingside_castling_rights(side)) {
return false;
}

Square king_sq = (side == Color::WHITE) ? Squares::E1 : Squares::E8;
Square rook_sq = (side == Color::WHITE) ? Squares::H1 : Squares::H8;
Square f_sq = (side == Color::WHITE) ? Squares::F1 : Squares::F8;
Square g_sq = (side == Color::WHITE) ? Squares::G1 : Squares::G8;

// Check if king is on the starting square
if (!this->king(side).test(king_sq)) {
return false;
}

// Check if rook is on the starting square
if (!this->rooks(side).test(rook_sq)) {
return false;
}

// Check if squares between king and rook are empty
const Bitboard occupied = this->occupied();
return !occupied.test(f_sq) && !occupied.test(g_sq);
}

bool Board::can_castle_queenside(Color side) const noexcept {
if (!this->has_queenside_castling_rights(side)) {
return false;
}

Square king_sq = (side == Color::WHITE) ? Squares::E1 : Squares::E8;
Square rook_sq = (side == Color::WHITE) ? Squares::A1 : Squares::A8;
Square b_sq = (side == Color::WHITE) ? Squares::B1 : Squares::B8;
Square c_sq = (side == Color::WHITE) ? Squares::C1 : Squares::C8;
Square d_sq = (side == Color::WHITE) ? Squares::D1 : Squares::D8;

// Check that the king is on the starting square
if (!this->king(side).test(king_sq)) {
return false;
}

// Check if the rook is on the starting square
if (!this->rooks(side).test(rook_sq)) {
return false;
}

// Check if the squares between king and rook are empty
const Bitboard occupied = this->occupied();
return !occupied.test(b_sq) && !occupied.test(c_sq) && !occupied.test(d_sq);
}
55 changes: 2 additions & 53 deletions src/bitbishop/moves/king_move_gen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,65 +47,14 @@ void KingMoveGenerator::generate_legal_moves(std::vector<Move>& moves, const Boa

void KingMoveGenerator::add_king_castling(std::vector<Move>& moves, Square from, Color side, const Board& board) {
// Kingside castling
if (can_castle_kingside(board, side)) {
if (board.can_castle_kingside(side)) {
Square to = (side == Color::WHITE) ? Squares::G1 : Squares::G8;
moves.emplace_back(from, to, std::nullopt, false, false, true);
}

// Queenside castling
if (can_castle_queenside(board, side)) {
if (board.can_castle_queenside(side)) {
Square to = (side == Color::WHITE) ? Squares::C1 : Squares::C8;
moves.emplace_back(from, to, std::nullopt, false, false, true);
}
}

bool KingMoveGenerator::can_castle_kingside(const Board& board, Color side) {
if (!board.has_kingside_castling_rights(side)) {
return false;
}

Square king_sq = (side == Color::WHITE) ? Squares::E1 : Squares::E8;
Square rook_sq = (side == Color::WHITE) ? Squares::H1 : Squares::H8;
Square f_sq = (side == Color::WHITE) ? Squares::F1 : Squares::F8;
Square g_sq = (side == Color::WHITE) ? Squares::G1 : Squares::G8;

// Check if king is on the starting square
if (!board.king(side).test(king_sq)) {
return false;
}

// Check if rook is on the starting square
if (!board.rooks(side).test(rook_sq)) {
return false;
}

// Check if squares between king and rook are empty
const Bitboard occupied = board.occupied();
return !occupied.test(f_sq) && !occupied.test(g_sq);
}

bool KingMoveGenerator::can_castle_queenside(const Board& board, Color side) {
if (!board.has_queenside_castling_rights(side)) {
return false;
}

Square king_sq = (side == Color::WHITE) ? Squares::E1 : Squares::E8;
Square rook_sq = (side == Color::WHITE) ? Squares::A1 : Squares::A8;
Square b_sq = (side == Color::WHITE) ? Squares::B1 : Squares::B8;
Square c_sq = (side == Color::WHITE) ? Squares::C1 : Squares::C8;
Square d_sq = (side == Color::WHITE) ? Squares::D1 : Squares::D8;

// Check that the king is on the starting square
if (!board.king(side).test(king_sq)) {
return false;
}

// Check if the rook is on the starting square
if (!board.rooks(side).test(rook_sq)) {
return false;
}

// Check if the squares between king and rook are empty
const Bitboard occupied = board.occupied();
return !occupied.test(b_sq) && !occupied.test(c_sq) && !occupied.test(d_sq);
}
4 changes: 2 additions & 2 deletions src/bitbishop/moves/rook_move_gen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ void RookMoveGenerator::add_rook_castling(std::vector<Move>& moves, Square from,
Square queenside_rook = (side == Color::WHITE) ? Squares::A1 : Squares::A8;

// Kingside castling
const bool can_castle_kingside = KingMoveGenerator::can_castle_kingside(board, side) && (from == kingside_rook);
const bool can_castle_kingside = board.can_castle_kingside(side) && (from == kingside_rook);
if (can_castle_kingside) {
Square to = (side == Color::WHITE) ? Squares::F1 : Squares::F8;
moves.emplace_back(from, to, std::nullopt, false, false, true);
}

// Queenside castling
const bool can_castle_queenside = KingMoveGenerator::can_castle_queenside(board, side) && (from == queenside_rook);
const bool can_castle_queenside = board.can_castle_queenside(side) && (from == queenside_rook);
if (can_castle_queenside) {
Square to = (side == Color::WHITE) ? Squares::D1 : Squares::D8;
moves.emplace_back(from, to, std::nullopt, false, false, true);
Expand Down
20 changes: 20 additions & 0 deletions tests/bitbishop/board/test_b_black_pieces.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include <gtest/gtest.h>

#include <bitbishop/board.hpp>
#include <bitbishop/piece.hpp>
#include <bitbishop/square.hpp>

/**
* @test BoardTest.GetBlackPieces
* @brief Confirms that black_pieces() returns the right squares using the standard fen opening.
*/
TEST(BoardTest, GetBlackPieces) {
Board board; // default opening position

Bitboard black_pieces = board.black_pieces();

// In the default opening position, black pieces are on rank 7 and 8
for (int sq_index = Square::A7; sq_index <= Square::H8; sq_index++) {
EXPECT_TRUE(black_pieces.test(Square(sq_index)));
}
}
Loading