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
3 changes: 0 additions & 3 deletions include/chess_engine/attacks/pawn.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

namespace Attacks {


/**
* @brief Precomputed bitboards for white pawn single pushes (1 square forward).
*
Expand All @@ -24,7 +23,6 @@ constexpr std::array<Bitboard, 64> WHITE_PAWN_SINGLE_PUSH = []() constexpr {
return table;
}();


/**
* @brief Precomputed bitboards for black pawn single pushes (1 square forward).
*
Expand All @@ -42,7 +40,6 @@ constexpr std::array<Bitboard, 64> BLACK_PAWN_SINGLE_PUSH = []() constexpr {
return table;
}();


/**
* @brief Precomputed bitboards for white pawn double pushes (2 squares forward).
*
Expand Down
165 changes: 165 additions & 0 deletions include/chess_engine/bitboard.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#pragma once
#include <bit>
#include <chess_engine/square.hpp>
#include <cstdint>
#include <iostream>
#include <optional>

/**
* @class Bitboard
Expand Down Expand Up @@ -139,4 +141,167 @@ class Bitboard {
m_bb &= other.m_bb;
return *this;
}
constexpr operator bool() const noexcept { return m_bb != 0ULL; }

/**
* @brief Counts the number of set bits in the bitboard.
*
* @return The number of bits set to 1.
*
* @note Uses std::popcount (C++20 and later).
*/
constexpr int count() const noexcept { return std::popcount(m_bb); }

/**
* @brief Tells if any bit in the bitboard is set to one.
*
* @return True if at least one bit is set to one in the bitboard.
*
* @note Equivalent to `cout() > 0` and `bool()`
*/
constexpr bool any() const noexcept { return m_bb != 0ULL; }

/**
* @brief Removes and returns the least significant set bit (LSB) from the bitboard.
*
* This method identifies the lowest-index bit currently set to 1 (the least significant bit),
* converts it to a Square object, clears that bit from the bitboard, and returns the square.
*
* ### Example
*
* Suppose the bitboard has bits set for A1 (index 0) and C5 (index 34):
*
* ```
* m_bb (binary) = 000...01000000000000000000000000000001
* pop_lsb() β†’ returns Square(0) // A1
* m_bb (after) = 000...01000000000000000000000000000000 // only C5 remains
* ```
*
* ### Implementation Details
*
* - `std::countr_zero(m_bb)` efficiently finds the index of the lowest set bit.
* - The expression `m_bb &= (m_bb - 1)` clears that bit in constant time.
*
* The reason `m_bb &= (m_bb - 1)` works is due to binary subtraction:
* when subtracting 1 from a binary number, the rightmost set bit turns to 0,
* and all bits to its right become 1. Bits to the left of the LSB stay unchanged:
*
* m_bb = 0b10110000
* m_bb - 1 = 0b10101111
* m_bb &= (m_bb - 1) = 0b10100000
*
* ### Return Value
*
* - `std::optional<Square>` containing the lowest set square if one exists.
* - `std::nullopt` if the bitboard is empty.
*
* @note This method **modifies** the bitboard by clearing the bit that it returns.
* Use `lsb()` if you want to inspect the least significant bit *without* modification.
*/
constexpr std::optional<Square> pop_lsb() noexcept {
if (!*this) return std::nullopt;
int index = std::countr_zero(m_bb);
m_bb &= (m_bb - 1);
const Square sq(index);
return sq;
}

/**
* @brief Returns the least significant set bit (LSB) without modifying the bitboard.
*
* This function behaves like pop_lsb(), but it only reads the lowest set bit without clearing it.
*
* For example, if bits are set at C5 and A1, lsb() will return Square(0) (A1),
* but the bitboard will remain unchanged.
*
* @return std::optional<Square> β€” the square corresponding to the least significant bit,
* or std::nullopt if the bitboard is empty.
*
* @note This method is const and does not modify the underlying bitboard.
*/
constexpr std::optional<Square> lsb() const noexcept {
if (!*this) return std::nullopt;
int index = std::countr_zero(m_bb);
const Square sq(index);
return sq;
}

/**
* @brief Iterator for traversing all set bits (squares) in a Bitboard.
*
* This iterator provides a clean, efficient way to loop through all squares
* where the bitboard has a 1 bit set β€” from least significant bit (A1) upward.
*
* Example:
* @code
* Bitboard bb;
* bb.set(Square::A1);
* bb.set(Square::C3);
* bb.set(Square::H8);
* for (Square sq : bb) {
* std::cout << sq << "\n"; // Iterates A1, C3, H8 (in LSB order)
* }
* @endcode
*
* Internally, iteration uses `std::countr_zero()` to find the index of the
* least significant set bit (LSB), and the idiom `bits &= (bits - 1)` to
* clear it efficiently.
*
* The iterator is designed to be lightweight and constexpr-compatible.
*/
class Iterator {
uint64_t bits; ///< Internal copy of the bitboard bits being iterated.

public:
/**
* @brief Constructs an iterator for a given bit pattern.
* @param b The 64-bit bitboard value to iterate over.
*/
constexpr Iterator(uint64_t b) noexcept : bits(b) {}

/**
* @brief Inequality comparison for iterators.
* @param other Another iterator to compare to.
* @return true if this iterator does not equal `other`.
*/
constexpr bool operator!=(const Iterator& other) const noexcept { return bits != other.bits; }

/**
* @brief Equality comparison for iterators.
* @param other Another iterator to compare to.
* @return true if this iterator equals `other`.
*/
constexpr bool operator==(const Iterator& other) const noexcept { return bits == other.bits; }

/**
* @brief Dereferences the iterator to return the current square.
* @return A `Square` corresponding to the current least significant set bit.
*/
constexpr Square operator*() const noexcept { return Square(std::countr_zero(bits)); }

/**
* @brief Advances the iterator to the next set bit.
*
* Uses the fast bit trick `bits &= (bits - 1)` to clear the lowest set bit
* and move to the next one.
*
* @return Reference to the incremented iterator.
*/
constexpr Iterator& operator++() noexcept {
bits &= (bits - 1);
return *this;
}
};

/**
* @brief Returns an iterator pointing to the first set bit (if any).
* @return Iterator positioned at the first set bit.
*/
constexpr Iterator begin() const noexcept { return Iterator(m_bb); }

/**
* @brief Returns an iterator representing the end (no bits set).
* @return Iterator with no bits remaining.
*/
constexpr Iterator end() const noexcept { return Iterator(0ULL); }
};
77 changes: 62 additions & 15 deletions include/chess_engine/board.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once
#include <chess_engine/bitboard.hpp>
#include <chess_engine/color.hpp>
#include <chess_engine/piece.hpp>
#include <chess_engine/square.hpp>
#include <optional>
Expand Down Expand Up @@ -62,21 +63,6 @@ class Board {
*/
Board(const std::string& fen);

/**
* @brief Returns a bitboard containing all white pieces.
*/
Bitboard white_pieces() const;

/**
* @brief Returns a bitboard containing all black pieces.
*/
Bitboard black_pieces() const;

/**
* @brief Returns a bitboard containing all occupied squares (both sides).
*/
Bitboard occupied() const;

/**
* @brief Retrieves the piece on a given square.
* @param sq The square to query.
Expand Down Expand Up @@ -114,4 +100,65 @@ class Board {
* @endcode
*/
void print() const;

/**
* @brief Returns a bitboard containing all white pieces.
*/
Bitboard white_pieces() const;

/**
* @brief Returns a bitboard containing all black pieces.
*/
Bitboard black_pieces() const;

/**
* @brief Returns a bitboard containing all occupied squares (both sides).
*/
Bitboard occupied() const;

/**
* @brief Returns a bitboard of all empty squares on the board.
*
* @return Bitboard with bits set where no piece occupies a square.
*/
Bitboard unoccupied() const { return ~occupied(); }

/**
* @brief Returns a bitboard representing all pawns belonging to the given side to move.
*
* @param side The color corresponding to the side to move (Color::WHITE or Color::BLACK).
* @return Bitboard containing all squares occupied by that side's pawns.
*/
Bitboard pawns(Color side) const { return (side == Color::WHITE) ? m_w_pawns : m_b_pawns; }

/**
* @brief Returns a bitboard of all enemy pieces relative to the given side to move.
*
* @param side The color corresponding to the side to move (Color::WHITE or Color::BLACK).
* @return Bitboard of all opposing pieces.
*/
Bitboard enemy(Color side) const { return (side == Color::WHITE) ? black_pieces() : white_pieces(); }

/**
* @brief Returns a bitboard of all friendly pieces relative to the given side to move.
*
* @param side The color corresponding to the side to move (Color::WHITE or Color::BLACK).
* @return Bitboard of all friendly pieces.
*/
Bitboard friendly(Color side) const { return (side == Color::WHITE) ? white_pieces() : black_pieces(); }

/**
* @brief Returns the current en passant target square, if any.
*
* @return Optional square indicating where an en passant capture is possible,
* or `std::nullopt` if no en passant square is available.
*
* Example:
* @code
* if (auto sq = board.en_passant_square()) {
* std::cout << "En passant possible at " << *sq << "\n";
* }
* @endcode
*/
std::optional<Square> en_passant_square() const noexcept { return m_en_passant_sq; }
};
6 changes: 6 additions & 0 deletions include/chess_engine/color.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#pragma once

/**
* Depicts possible piece colors in the chess game.
*/
enum class Color { BLACK, WHITE };
22 changes: 22 additions & 0 deletions include/chess_engine/move.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once
#include <chess_engine/piece.hpp>
#include <chess_engine/square.hpp>
#include <optional>

/**
* @struct Move
* @brief Represents a chess move, including metadata about its properties.
*
* This struct encapsulates all information required to describe a move in chess:
* - The starting and target squares.
* - Optional promotion piece (for pawn promotions).
* - Flags indicating special move types (capture, en passant, castling).
*/
struct Move {
Square from; ///< The starting square of the move.
Square to; ///< The target square of the move.
std::optional<Piece> promotion; ///< The piece to promote to (if applicable, e.g., for pawns).
bool is_capture; ///< True if the move captures an opponent's piece.
bool is_en_passant; ///< True if the move is an en passant capture.
bool is_castling; ///< True if the move is a castling move (kingside or queenside).
};
Loading