From 7e45e495028b3407038f69982db0a3fceea74f30 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 13 Jul 2024 07:20:18 -0700 Subject: [PATCH] [BitBoard] Build out the documentation --- bitboard/src/bitboard.rs | 125 +++++++++++++++++++++++++-------------- bitboard/src/library.rs | 2 +- 2 files changed, 81 insertions(+), 46 deletions(-) diff --git a/bitboard/src/bitboard.rs b/bitboard/src/bitboard.rs index 3787bb8..2b30b9a 100644 --- a/bitboard/src/bitboard.rs +++ b/bitboard/src/bitboard.rs @@ -7,6 +7,25 @@ use forward_ref::{forward_ref_binop, forward_ref_op_assign, forward_ref_unop}; use std::fmt; 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)] pub struct BitBoard(pub(crate) u64); @@ -78,27 +97,30 @@ impl BitBoard { } impl BitBoard { + /// Converts this [BitBoard] to an unsigned 64-bit integer. #[must_use] pub const fn as_bits(&self) -> u64 { 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 /// /// ``` /// use chessfriend_bitboard::BitBoard; - /// assert!(BitBoard::EMPTY.is_populated()); - /// assert!(!BitBoard::FULL.is_populated()); - /// assert!(!BitBoard::new(0b1000).is_populated()); + /// assert!(BitBoard::EMPTY.is_empty()); + /// assert!(!BitBoard::FULL.is_empty()); + /// assert!(!BitBoard::new(0b1000).is_empty()); /// ``` #[must_use] pub const fn is_empty(&self) -> bool { 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 /// @@ -112,13 +134,26 @@ impl BitBoard { 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 { let square_bitboard: BitBoard = square.into(); !(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 /// @@ -132,11 +167,37 @@ impl BitBoard { 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) { let square_bitboard: BitBoard = square.into(); 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) { let square_bitboard: BitBoard = square.into(); self.0 &= !square_bitboard.0 @@ -156,25 +217,25 @@ impl BitBoard { pub fn is_single_square(&self) -> bool { self.0.is_power_of_two() } -} -impl BitBoard { - /// Returns an Iterator over the occupied squares. - /// - /// The Iterator yields squares starting from the leading (most-significant bit) end of the - /// board to the trailing (least-significant bit) end. + /// Return an Iterator over the occupied squares. The Iterator yields + /// squares starting from the leading (most-significant bit) end of the + /// board. #[must_use] pub fn occupied_squares(&self) -> impl Iterator { LeadingBitScanner::new(self.0).map(|idx| unsafe { Square::from_index(idx as u8) }) } - /// Return an Iterator over the occupied squares, starting from the trailing - /// (least-significant bit) end of the field. - #[must_use] + /// Return an Iterator over the occupied squares. The Iterator yields + /// squares starting from the trailing (least-significant bit) end of the + /// board. pub fn occupied_squares_trailing(&self) -> impl Iterator { 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] pub fn first_occupied_square(&self) -> Option { 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] pub fn first_occupied_square_trailing(&self) -> Option { let trailing_zeros = self.0.trailing_zeros() as u8; @@ -380,35 +444,6 @@ mod tests { 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] fn single_rank_occupancy() { let bb = BitBoard(0b01010100); diff --git a/bitboard/src/library.rs b/bitboard/src/library.rs index 7a45906..419f6e2 100644 --- a/bitboard/src/library.rs +++ b/bitboard/src/library.rs @@ -3,7 +3,7 @@ //! # The Bitboard Library //! //! 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 //! provides getters for all available bitboards.