Compare commits

...

7 commits

Author SHA1 Message Date
3a0541a2c3 [bitboard] Add a doc comment to BitBoard::first_occupied_square 2025-07-12 20:27:47 -07:00
b6d27356ac [bitboard] Implement BitBoard::occupied_squares_direction
Iterate a BitBoard in a direction (from leading or trailing edge) based on a
board direction.
2025-07-12 20:27:47 -07:00
146e4d34d3 [core] Fix an incorrect assertion in the Score doc test
Negate with - instead of with !.
2025-07-12 20:27:47 -07:00
b505606925 [core] Export Score::CENTIPAWNS_PER_POINT to the crate
This constant is a conversion factor of points to the internal fixed point unit
of centipawns. Points are more familiar to people because pawns are worth 1 pt.

Calculate the scores of the various piece shapes with this constant.
2025-07-12 20:27:47 -07:00
b3ff8dec49 [core] Make Shape::is_promotable() const 2025-07-12 20:27:47 -07:00
484fcf342e [board] Remove a useless .into() call
Clippy pointed this out to me. This .into() call serves no purpose.
2025-07-12 20:27:47 -07:00
45183c910c [bitboard] Replace some references to BitBoard::full() and BitBoard::empty() with the const values
Two doc tests reference the methods instead of the const variables. Update them.
2025-07-12 20:27:47 -07:00
4 changed files with 55 additions and 14 deletions

View file

@ -160,9 +160,9 @@ impl BitBoard {
///
/// ```
/// use chessfriend_bitboard::BitBoard;
/// assert_eq!(BitBoard::empty().population_count(), 0);
/// assert_eq!(BitBoard::EMPTY.population_count(), 0);
/// assert_eq!(BitBoard::new(0b01011110010).population_count(), 6);
/// assert_eq!(BitBoard::full().population_count(), 64);
/// assert_eq!(BitBoard::FULL.population_count(), 64);
/// ```
#[must_use]
pub const fn population_count(&self) -> u32 {
@ -211,8 +211,8 @@ impl BitBoard {
///
/// ```
/// use chessfriend_bitboard::BitBoard;
/// assert!(!BitBoard::empty().is_single_square(), "Empty bitboards represent no squares");
/// assert!(!BitBoard::full().is_single_square(), "Full bitboards represent all the squares");
/// assert!(!BitBoard::EMPTY.is_single_square(), "Empty bitboards represent no squares");
/// assert!(!BitBoard::FULL.is_single_square(), "Full bitboards represent all the squares");
/// assert!(!BitBoard::new(0b010011110101101100).is_single_square(), "This bitboard represents a bunch of squares");
/// assert!(BitBoard::new(0b10000000000000).is_single_square());
/// ```
@ -233,6 +233,38 @@ impl BitBoard {
}
}
/// Iterate through the occupied squares in a direction specified by a
/// compass direction. This method is mose useful for bitboards of slider
/// rays so that iteration proceeds in order along the ray's direction.
///
/// ## Examples
///
/// ```
/// use chessfriend_bitboard::BitBoard;
/// use chessfriend_core::{Direction, Square};
///
/// let ray = BitBoard::ray(Square::E4, Direction::North);
/// assert_eq!(
/// ray.occupied_squares_direction(Direction::North).collect::<Vec<Square>>(),
/// vec![Square::E5, Square::E6, Square::E7, Square::E8]
/// );
/// ```
///
#[must_use]
pub fn occupied_squares_direction(
&self,
direction: Direction,
) -> Box<dyn Iterator<Item = Square>> {
match direction {
Direction::North | Direction::NorthEast | Direction::NorthWest | Direction::East => {
Box::new(self.occupied_squares_trailing())
}
Direction::SouthEast | Direction::South | Direction::SouthWest | Direction::West => {
Box::new(self.occupied_squares_leading())
}
}
}
#[must_use]
pub fn occupied_squares_leading(&self) -> LeadingBitScanner {
LeadingBitScanner::new(self.0)
@ -255,6 +287,12 @@ impl BitBoard {
}
}
/// Get the first occupied square in the given direction.
///
/// ## To-Do
///
/// - Take `direction` by value instead of reference
///
#[must_use]
pub fn first_occupied_square(&self, direction: &IterationDirection) -> Option<Square> {
match direction {

View file

@ -46,7 +46,7 @@ impl Board {
let color = self.unwrap_color(color);
if !self.has_castling_right_unwrapped(color, wing.into()) {
if !self.has_castling_right_unwrapped(color, wing) {
return Err(CastleEvaluationError::NoRights { color, wing });
}

View file

@ -23,7 +23,7 @@ impl Score {
///
/// ```
/// use chessfriend_core::score::Score;
/// assert_eq!(!Score::MIN, Score::MAX);
/// assert_eq!(-Score::MIN, Score::MAX);
/// ```
///
pub const MIN: Score = Score(Value::MIN + 1);
@ -31,7 +31,7 @@ impl Score {
/// The maximum possible value of a score.
pub const MAX: Score = Score(Value::MAX);
const CENTIPAWNS_PER_POINT: f32 = 100.0;
pub(crate) const CENTIPAWNS_PER_POINT: f32 = 100.0;
#[must_use]
pub const fn new(value: Value) -> Self {

View file

@ -70,18 +70,21 @@ impl Shape {
}
#[must_use]
pub fn is_promotable(&self) -> bool {
pub const fn is_promotable(&self) -> bool {
matches!(self, Self::Knight | Self::Bishop | Self::Rook | Self::Queen)
}
#[must_use]
pub fn score(self) -> Score {
pub const fn score(self) -> Score {
#[allow(clippy::cast_possible_truncation)]
const CP_PER_PT: i32 = Score::CENTIPAWNS_PER_POINT as i32;
match self {
Shape::Pawn => Score::new(100),
Shape::Knight | Shape::Bishop => Score::new(300),
Shape::Rook => Score::new(500),
Shape::Queen => Score::new(900),
Shape::King => Score::new(20000),
Shape::Pawn => Score::new(CP_PER_PT),
Shape::Knight | Shape::Bishop => Score::new(3 * CP_PER_PT),
Shape::Rook => Score::new(5 * CP_PER_PT),
Shape::Queen => Score::new(9 * CP_PER_PT),
Shape::King => Score::new(200 * CP_PER_PT),
}
}
}