[BitBoard] Build out the documentation

This commit is contained in:
Eryn Wells 2024-07-13 07:20:18 -07:00
parent daf5c86792
commit 7e45e49502
2 changed files with 81 additions and 46 deletions

View file

@ -7,6 +7,25 @@ use forward_ref::{forward_ref_binop, forward_ref_op_assign, forward_ref_unop};
use std::fmt; use std::fmt;
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not}; use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};
/// A bitfield representation of a chess board that uses the bits of a 64-bit
/// unsigned integer to represent whether a square on the board is occupied.
/// Squares are laid out as follows, starting at the bottom left, going row-wise,
/// and ending at the top right corner:
///
/// ```text
/// +-------------------------+
/// 8 | 56 57 58 59 60 61 62 63 |
/// 7 | 48 49 50 51 52 53 54 55 |
/// 6 | 40 41 42 43 44 45 46 47 |
/// 5 | 32 33 34 35 36 37 38 39 |
/// 4 | 24 25 26 27 28 29 30 31 |
/// 3 | 16 17 18 19 20 21 22 23 |
/// 2 | 8 9 10 11 12 13 14 15 |
/// 1 | 0 1 2 3 4 5 6 7 |
/// +-------------------------+
/// A B C D E F G H
/// ```
///
#[derive(Clone, Copy, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct BitBoard(pub(crate) u64); pub struct BitBoard(pub(crate) u64);
@ -78,27 +97,30 @@ impl BitBoard {
} }
impl BitBoard { impl BitBoard {
/// Converts this [BitBoard] to an unsigned 64-bit integer.
#[must_use] #[must_use]
pub const fn as_bits(&self) -> u64 { pub const fn as_bits(&self) -> u64 {
self.0 self.0
} }
/// Returns `true` if the [`BitBoard`] has no bits set. /// Returns `true` if this [BitBoard] has no bits set. This is the opposite
/// of [`BitBoard::is_populated`].
/// ///
/// ## Examples /// ## Examples
/// ///
/// ``` /// ```
/// use chessfriend_bitboard::BitBoard; /// use chessfriend_bitboard::BitBoard;
/// assert!(BitBoard::EMPTY.is_populated()); /// assert!(BitBoard::EMPTY.is_empty());
/// assert!(!BitBoard::FULL.is_populated()); /// assert!(!BitBoard::FULL.is_empty());
/// assert!(!BitBoard::new(0b1000).is_populated()); /// assert!(!BitBoard::new(0b1000).is_empty());
/// ``` /// ```
#[must_use] #[must_use]
pub const fn is_empty(&self) -> bool { pub const fn is_empty(&self) -> bool {
self.0 == 0 self.0 == 0
} }
/// Returns `true` if the [`BitBoard`] has at least one bit set. /// Returns `true` if the [BitBoard] has at least one bit set. This is the
/// opposite of [`BitBoard::is_empty`].
/// ///
/// ## Examples /// ## Examples
/// ///
@ -112,13 +134,26 @@ impl BitBoard {
self.0 != 0 self.0 != 0
} }
/// Returns `true` if this [`BitBoard`] has the bit corresponding to `square` set. /// Returns `true` if this [BitBoard] has the bit corresponding to `square` set.
///
/// ## Examples
///
/// ```
/// use chessfriend_bitboard::BitBoard;
/// use chessfriend_core::Square;
///
/// let square = Square::E4;
/// let mut bitboard = BitBoard::new(0b1001100);
///
/// assert!(bitboard.contains(Square::C1));
/// assert!(!bitboard.contains(Square::B1));
/// ```
pub fn contains(self, square: Square) -> bool { pub fn contains(self, square: Square) -> bool {
let square_bitboard: BitBoard = square.into(); let square_bitboard: BitBoard = square.into();
!(self & square_bitboard).is_empty() !(self & square_bitboard).is_empty()
} }
/// The number of 1 bits in the BitBoard. /// Counts the number of set squares (1 bits) in this [BitBoard].
/// ///
/// ## Examples /// ## Examples
/// ///
@ -132,11 +167,37 @@ impl BitBoard {
self.0.count_ones() self.0.count_ones()
} }
/// Set a square in this [BitBoard] by toggling the corresponding bit to 1.
/// This always succeeds, even if the bit was already set.
///
/// ## Examples
///
/// ```
/// use chessfriend_bitboard::BitBoard;
/// use chessfriend_core::Square;
///
/// let mut bitboard = BitBoard::new(0b1001100);
/// bitboard.set(Square::E4);
/// assert!(bitboard.contains(Square::E4));
/// ```
pub fn set(&mut self, square: Square) { pub fn set(&mut self, square: Square) {
let square_bitboard: BitBoard = square.into(); let square_bitboard: BitBoard = square.into();
self.0 |= square_bitboard.0 self.0 |= square_bitboard.0
} }
/// Clear a square (set it to 0) in this [BitBoard]. This always succeeds
/// even if the bit is not set.
///
/// ## Examples
///
/// ```
/// use chessfriend_bitboard::BitBoard;
/// use chessfriend_core::Square;
///
/// let mut bitboard = BitBoard::new(0b1001100);
/// bitboard.clear(Square::C1);
/// assert!(!bitboard.contains(Square::C1));
/// ```
pub fn clear(&mut self, square: Square) { pub fn clear(&mut self, square: Square) {
let square_bitboard: BitBoard = square.into(); let square_bitboard: BitBoard = square.into();
self.0 &= !square_bitboard.0 self.0 &= !square_bitboard.0
@ -156,25 +217,25 @@ impl BitBoard {
pub fn is_single_square(&self) -> bool { pub fn is_single_square(&self) -> bool {
self.0.is_power_of_two() self.0.is_power_of_two()
} }
}
impl BitBoard { /// Return an Iterator over the occupied squares. The Iterator yields
/// Returns an Iterator over the occupied squares. /// squares starting from the leading (most-significant bit) end of the
/// /// board.
/// The Iterator yields squares starting from the leading (most-significant bit) end of the
/// board to the trailing (least-significant bit) end.
#[must_use] #[must_use]
pub fn occupied_squares(&self) -> impl Iterator<Item = Square> { pub fn occupied_squares(&self) -> impl Iterator<Item = Square> {
LeadingBitScanner::new(self.0).map(|idx| unsafe { Square::from_index(idx as u8) }) LeadingBitScanner::new(self.0).map(|idx| unsafe { Square::from_index(idx as u8) })
} }
/// Return an Iterator over the occupied squares, starting from the trailing /// Return an Iterator over the occupied squares. The Iterator yields
/// (least-significant bit) end of the field. /// squares starting from the trailing (least-significant bit) end of the
#[must_use] /// board.
pub fn occupied_squares_trailing(&self) -> impl Iterator<Item = Square> { pub fn occupied_squares_trailing(&self) -> impl Iterator<Item = Square> {
TrailingBitScanner::new(self.0).map(|idx| unsafe { Square::from_index(idx as u8) }) TrailingBitScanner::new(self.0).map(|idx| unsafe { Square::from_index(idx as u8) })
} }
/// If the board is not empty, returns the first occupied square on the
/// board, starting at the leading (most-significant) end of the board. If
/// the board is empty, returns `None`.
#[must_use] #[must_use]
pub fn first_occupied_square(&self) -> Option<Square> { pub fn first_occupied_square(&self) -> Option<Square> {
let leading_zeros = self.0.leading_zeros() as u8; let leading_zeros = self.0.leading_zeros() as u8;
@ -185,6 +246,9 @@ impl BitBoard {
} }
} }
/// If the board is not empty, returns the first occupied square on the
/// board, starting at the trailing (least-significant) end of the board.
/// If the board is empty, returns `None`.
#[must_use] #[must_use]
pub fn first_occupied_square_trailing(&self) -> Option<Square> { pub fn first_occupied_square_trailing(&self) -> Option<Square> {
let trailing_zeros = self.0.trailing_zeros() as u8; let trailing_zeros = self.0.trailing_zeros() as u8;
@ -380,35 +444,6 @@ mod tests {
assert_eq!(BitBoard::rank(&7).0, 0xFF00000000000000, "Rank 8"); assert_eq!(BitBoard::rank(&7).0, 0xFF00000000000000, "Rank 8");
} }
#[test]
fn is_empty() {
assert!(BitBoard(0).is_empty());
assert!(!BitBoard(0xFF).is_empty());
}
#[test]
fn has_piece_at() {
let bb = BitBoard(0b1001100);
assert!(bb.is_set(Square::C1));
assert!(!bb.is_set(Square::B1));
}
#[test]
fn set_square() {
let sq = Square::E4;
let mut bb = BitBoard(0b1001100);
bb.set_square(sq);
assert!(bb.is_set(sq));
}
#[test]
fn clear_square() {
let sq = Square::A3;
let mut bb = BitBoard(0b1001100);
bb.clear_square(sq);
assert!(!bb.is_set(sq));
}
#[test] #[test]
fn single_rank_occupancy() { fn single_rank_occupancy() {
let bb = BitBoard(0b01010100); let bb = BitBoard(0b01010100);

View file

@ -3,7 +3,7 @@
//! # The Bitboard Library //! # The Bitboard Library
//! //!
//! This module implements a collection of commonly used bitboards that can be //! This module implements a collection of commonly used bitboards that can be
//! looked up efficiently as needed. //! looked up efficiently.
//! //!
//! The `library()` method returns a static instance of a `Library`, which //! The `library()` method returns a static instance of a `Library`, which
//! provides getters for all available bitboards. //! provides getters for all available bitboards.