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
22 changes: 22 additions & 0 deletions include/bitbishop/attacks/checkers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once
#include <bitbishop/board.hpp>
#include <bitbishop/color.hpp>
#include <bitbishop/square.hpp>

/**
* @brief Computes the set of enemy pieces currently giving check to a king.
*
* Returns a bitboard containing the squares of all pieces of color @p attacker
* that are directly attacking the king on @p king_sq in the current position.
*
* The result is occupancy-aware and exact:
* - Sliding attacks respect blockers
* - Only real pieces are included
* - The bit count corresponds to single / double check
*
* @param board Current board position
* @param king_sq Square of the king being attacked
* @param attacker Color of the attacking side
* @return Bitboard of checking piece squares
*/
Bitboard compute_checkers(const Board& board, Square king_sq, Color attacker);
24 changes: 10 additions & 14 deletions include/bitbishop/attacks/generate_attacks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,15 @@
#include <bitbishop/lookups/pawn_attacks.hpp>

/**
* @brief Computes the set of squares attacked by all pieces of a given side.
* @brief Computes the set of squares attacked by all pieces of a given side,
* assuming the opposing king is not present on the board.
*
* Generates a bitboard containing every square currently attacked by the
* specified color, based on the current board occupancy. This includes
* attacks from the king, knights, pawns, and all sliding pieces (rooks,
* bishops, queens).
* This attack map is suitable for validating king moves and castling legality.
* Sliding attacks (rook, bishop, queen) are generated with the opposing king
* removed from occupancy to correctly model x-ray attacks.
*
* The result represents geometric attacks only and does not account for
* move legality, pins, discovered checks, or whether the attacking pieces
* themselves are defended.
*
* This function is primarily used for king safety evaluation, including
* legal king move generation and castling validation.
* move legality, pins, or whether attacking pieces are defended.
*
* @param board The current board position.
* @param enemy The side whose attacks are to be generated.
Expand All @@ -32,7 +28,7 @@
Bitboard generate_attacks(const Board& board, Color enemy) {
Bitboard attacks = Bitboard::Zeros();

Bitboard occupied = board.occupied();
Bitboard occupied_no_king = board.occupied() ^ board.king(ColorUtil::opposite(enemy));

Bitboard king = board.king(enemy);
if (king.any()) {
Expand Down Expand Up @@ -62,19 +58,19 @@ Bitboard generate_attacks(const Board& board, Color enemy) {
Bitboard rooks = board.rooks(enemy);
while (rooks) {
Square sq = rooks.pop_lsb().value();
attacks |= rook_attacks(sq, occupied);
attacks |= rook_attacks(sq, occupied_no_king);
}

Bitboard bishops = board.bishops(enemy);
while (bishops) {
Square sq = bishops.pop_lsb().value();
attacks |= bishop_attacks(sq, occupied);
attacks |= bishop_attacks(sq, occupied_no_king);
}

Bitboard queens = board.queens(enemy);
while (queens) {
Square sq = queens.pop_lsb().value();
attacks |= queen_attacks(sq, occupied);
attacks |= queen_attacks(sq, occupied_no_king);
}

return attacks;
Expand Down
5 changes: 5 additions & 0 deletions include/bitbishop/bitboard.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class Bitboard {

constexpr bool operator==(const Bitboard& other) const { return m_bb == other.m_bb; }
constexpr bool operator!=(const Bitboard& other) const { return m_bb != other.m_bb; }
constexpr Bitboard operator^(const Bitboard& other) const { return m_bb ^ other.m_bb; }
constexpr Bitboard operator|(const Bitboard& other) const { return m_bb | other.m_bb; }
constexpr Bitboard operator&(const Bitboard& other) const { return m_bb & other.m_bb; }
constexpr Bitboard operator~() const { return ~m_bb; }
Expand All @@ -160,6 +161,10 @@ class Bitboard {
m_bb &= other.m_bb;
return *this;
}
constexpr Bitboard& operator^=(const Bitboard& other) {
m_bb ^= other.m_bb;
return *this;
}
constexpr operator bool() const noexcept { return m_bb != 0ULL; }

/**
Expand Down
3 changes: 1 addition & 2 deletions include/bitbishop/movegen/king_moves.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@
#include <vector>

void generate_legal_king_moves(std::vector<Move>& moves, const Board& board, Color us, Square king_sq,
const Bitboard& enemy_attacks, const Bitboard& check_mask) {
const Bitboard& enemy_attacks) {
const Bitboard own = board.friendly(us);
const Bitboard enemy = board.enemy(us);

Bitboard candidates = Lookups::KING_ATTACKS[king_sq.value()];

candidates &= ~own;
candidates &= ~enemy_attacks;
candidates &= check_mask;

for (Square to : candidates) {
const bool is_capture = enemy.test(to);
Expand Down
7 changes: 4 additions & 3 deletions include/bitbishop/movegen/legal_moves.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <bitbishop/attacks/checkers.hpp>
#include <bitbishop/attacks/generate_attacks.hpp>
#include <bitbishop/bitboard.hpp>
#include <bitbishop/board.hpp>
Expand Down Expand Up @@ -40,16 +41,16 @@
* @note The move list is appended to; it is not cleared by this function.
* @note Assumes the board position is internally consistent and legal.
*/
void generate_legal_moves(std::vector<Move> moves, const Board& board, Color us) {
void generate_legal_moves(std::vector<Move>& moves, const Board& board, Color us) {
Square king_sq = board.king_square(us).value();
Color them = ColorUtil::opposite(us);

Bitboard checkers = attackers_to(king_sq, them);
Bitboard checkers = compute_checkers(board, king_sq, them);
Bitboard check_mask = compute_check_mask(king_sq, checkers, board);
PinResult pins = compute_pins(king_sq, board, us);
Bitboard enemy_attacks = generate_attacks(board, them);

generate_legal_king_moves(moves, board, us, king_sq, enemy_attacks, check_mask);
generate_legal_king_moves(moves, board, us, king_sq, enemy_attacks);

generate_castling_moves(moves, board, us, checkers, enemy_attacks);

Expand Down
46 changes: 46 additions & 0 deletions src/bitbishop/attacks/checkers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include <bitbishop/attacks/bishop_attacks.hpp>
#include <bitbishop/attacks/queen_attacks.hpp>
#include <bitbishop/attacks/rook_attacks.hpp>
#include <bitbishop/bitboard.hpp>
#include <bitbishop/board.hpp>
#include <bitbishop/color.hpp>
#include <bitbishop/lookups/attackers.hpp>
#include <bitbishop/lookups/king_attacks.hpp>
#include <bitbishop/lookups/knight_attacks.hpp>
#include <bitbishop/lookups/pawn_attacks.hpp>
#include <bitbishop/square.hpp>

Bitboard compute_checkers(const Board& board, Square king_sq, Color attacker) {
using namespace Lookups;

Bitboard checkers;
Bitboard occupied = board.occupied();

checkers |= KNIGHT_ATTACKERS[king_sq.value()] & board.knights(attacker);

if (attacker == Color::WHITE) {
checkers |= WHITE_PAWN_ATTACKERS[king_sq.value()] & board.pawns(attacker);
} else {
checkers |= BLACK_PAWN_ATTACKERS[king_sq.value()] & board.pawns(attacker);
}

checkers |= KING_ATTACKERS[king_sq.value()] & board.king(attacker);

Bitboard diag_sliders = BISHOP_ATTACKER_RAYS[king_sq.value()] & (board.bishops(attacker) | board.queens(attacker));
while (diag_sliders) {
Square sq = diag_sliders.pop_lsb().value();
if (bishop_attacks(sq, occupied).test(king_sq)) {
checkers.set(sq);
}
}

Bitboard ortho_sliders = ROOK_ATTACKER_RAYS[king_sq.value()] & (board.rooks(attacker) | board.queens(attacker));
while (ortho_sliders) {
Square sq = ortho_sliders.pop_lsb().value();
if (rook_attacks(sq, occupied).test(king_sq)) {
checkers.set(sq);
}
}

return checkers;
}
Loading