diff --git a/board/src/piece_sets.rs b/board/src/piece_sets.rs index fee1a3f..f7435e5 100644 --- a/board/src/piece_sets.rs +++ b/board/src/piece_sets.rs @@ -1,9 +1,14 @@ // Eryn Wells -use chessfriend_bitboard::BitBoard; -use chessfriend_core::{Color, Piece, PlacedPiece, Square}; +mod bitboards; +mod mailbox; -#[derive(Debug, Eq, PartialEq)] +use bitboards::{ByColor, ByColorAndShape}; +use chessfriend_bitboard::BitBoard; +use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square}; +use mailbox::Mailbox; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum PlacePieceStrategy { Replace, PreserveExisting, @@ -11,7 +16,7 @@ pub enum PlacePieceStrategy { #[derive(Debug, Eq, PartialEq)] pub enum PlacePieceError { - ExisitingPiece, + ExisitingPiece(PlacedPiece), } impl Default for PlacePieceStrategy { @@ -20,20 +25,17 @@ impl Default for PlacePieceStrategy { } } +/// The internal data structure of a [Board] that efficiently manages the +/// placement of pieces on the board. #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] -pub(crate) struct PieceBitBoards { +pub(crate) struct PieceSet { by_color: ByColor, by_color_and_shape: ByColorAndShape, + mailbox: Mailbox, } -#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] -struct ByColor(BitBoard, [BitBoard; 2]); - -#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] -struct ByColorAndShape([[BitBoard; 6]; 2]); - -impl PieceBitBoards { - pub(super) fn new(pieces: [[BitBoard; 6]; 2]) -> Self { +impl PieceSet { + pub(crate) fn new(pieces: [[BitBoard; Shape::NUM]; Color::NUM]) -> Self { use std::ops::BitOr; let white_pieces = pieces[Color::White as usize] @@ -45,130 +47,99 @@ impl PieceBitBoards { let all_pieces = white_pieces | black_pieces; + let mut mailbox = Mailbox::default(); + for c in Color::into_iter() { + for s in Shape::into_iter() { + let bitboard = pieces[c as usize][s as usize]; + for square in bitboard.occupied_squares() { + mailbox.set(square, Piece::new(c, s)); + } + } + } + Self { - by_color: ByColor(all_pieces, [white_pieces, black_pieces]), - by_color_and_shape: ByColorAndShape(pieces), + by_color: ByColor::new(all_pieces, [white_pieces, black_pieces]), + by_color_and_shape: ByColorAndShape::new(pieces), + mailbox, } } - /// A BitBoard representing all the pieces currently on the board. Other + /// A [`BitBoard`] representing all the pieces currently on the board. Other /// engines might refer to this concept as 'occupancy'. - pub(crate) fn all_pieces(&self) -> &BitBoard { + pub(crate) fn all_pieces(&self) -> BitBoard { self.by_color.all() } - pub(crate) fn all_pieces_of_color(&self, color: Color) -> &BitBoard { + pub(crate) fn all_pieces_of_color(&self, color: Color) -> BitBoard { self.by_color.bitboard(color) } - pub(super) fn bitboard_for_color(&self, color: Color) -> &BitBoard { + pub(super) fn bitboard_for_color(&self, color: Color) -> BitBoard { self.by_color.bitboard(color) } - pub(super) fn bitboard_for_color_mut(&mut self, color: Color) -> &mut BitBoard { - self.by_color.bitboard_mut(color) - } - - pub(super) fn bitboard_for_piece(&self, piece: &Piece) -> &BitBoard { + pub(crate) fn bitboard_for_piece(&self, piece: Piece) -> BitBoard { self.by_color_and_shape.bitboard_for_piece(piece) } - pub(super) fn bitboard_for_piece_mut(&mut self, piece: &Piece) -> &mut BitBoard { - self.by_color_and_shape.bitboard_for_piece_mut(piece) + pub(crate) fn get(&self, square: Square) -> Option { + self.mailbox.get(square) } - pub(super) fn place_piece(&mut self, piece: &PlacedPiece) -> Result<(), PlacePieceError> { - self.place_piece_with_strategy(piece, PlacePieceStrategy::default()) - } - - pub(super) fn place_piece_with_strategy( + pub(crate) fn place_piece_on_square( &mut self, - piece: &PlacedPiece, - strategy: PlacePieceStrategy, - ) -> Result<(), PlacePieceError> { - let color = piece.color(); - let square = piece.square(); + piece: Piece, + square: Square, + ) -> Result { + self.place_piece_on_square_with_strategy(piece, square, PlacePieceStrategy::default()) + } - if strategy == PlacePieceStrategy::PreserveExisting - && self.by_color.bitboard(color).is_set(piece.square()) - { - return Err(PlacePieceError::ExisitingPiece); + pub(crate) fn place_piece_on_square_with_strategy( + &mut self, + piece: Piece, + square: Square, + strategy: PlacePieceStrategy, + ) -> Result { + let color = piece.color(); + + if strategy == PlacePieceStrategy::PreserveExisting { + if let Some(existing_piece) = self.mailbox.get(square) { + return Err(PlacePieceError::ExisitingPiece(PlacedPiece::new( + existing_piece, + square, + ))); + } } - self.by_color_and_shape.set_square(square, piece.piece()); + let piece: Piece = piece.into(); + self.by_color_and_shape.set_square(square, piece); self.by_color.set_square(square, color); + self.mailbox.set(square, piece); - Ok(()) + Ok(PlacedPiece::new(piece, square)) } - pub(super) fn remove_piece(&mut self, piece: &PlacedPiece) { - let color = piece.color(); - let square = piece.square(); + pub(crate) fn remove_piece_from_square(&mut self, square: Square) -> Option { + if let Some(piece) = self.mailbox.get(square) { + self.by_color_and_shape.clear_square(square, piece.into()); + self.by_color.clear_square(square, piece.color()); + self.mailbox.clear(square); - self.by_color_and_shape.clear_square(square, piece.piece()); - self.by_color.clear_square(square, color); - } - - pub(super) fn move_piece(&mut self, piece: &Piece, from_square: Square, to_square: Square) { - let color = piece.color(); - - self.by_color_and_shape.clear_square(from_square, piece); - self.by_color.clear_square(from_square, color); - self.by_color_and_shape.set_square(to_square, piece); - self.by_color.set_square(to_square, color); + Some(PlacedPiece::new(piece, square)) + } else { + None + } } } -impl FromIterator for PieceBitBoards { +impl FromIterator for PieceSet { fn from_iter>(iter: T) -> Self { - let mut pieces: Self = Default::default(); + let mut pieces: Self = Self::default(); for piece in iter { - let _ = pieces.place_piece(&piece); + let _ = pieces.place_piece_on_square(piece.piece(), piece.square()); } pieces } } - -impl ByColor { - fn all(&self) -> &BitBoard { - &self.0 - } - - pub(super) fn bitboard(&self, color: Color) -> &BitBoard { - &self.1[color as usize] - } - - pub(super) fn bitboard_mut(&mut self, color: Color) -> &mut BitBoard { - &mut self.1[color as usize] - } - - fn set_square(&mut self, square: Square, color: Color) { - self.0.set_square(square); - self.1[color as usize].set_square(square) - } - - fn clear_square(&mut self, square: Square, color: Color) { - self.0.clear_square(square); - self.1[color as usize].clear_square(square); - } -} - -impl ByColorAndShape { - fn bitboard_for_piece(&self, piece: &Piece) -> &BitBoard { - &self.0[piece.color() as usize][piece.shape() as usize] - } - - fn bitboard_for_piece_mut(&mut self, piece: &Piece) -> &mut BitBoard { - &mut self.0[piece.color() as usize][piece.shape() as usize] - } - - fn set_square(&mut self, square: Square, piece: &Piece) { - self.bitboard_for_piece_mut(piece).set_square(square); - } - - fn clear_square(&mut self, square: Square, piece: &Piece) { - self.bitboard_for_piece_mut(piece).clear_square(square); - } -} diff --git a/board/src/piece_sets/bitboards.rs b/board/src/piece_sets/bitboards.rs new file mode 100644 index 0000000..5c09432 --- /dev/null +++ b/board/src/piece_sets/bitboards.rs @@ -0,0 +1,56 @@ +use chessfriend_bitboard::BitBoard; +use chessfriend_core::{Color, Piece, Shape, Square}; + +/// A collection of bitboards that organize pieces by color. +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub(super) struct ByColor(BitBoard, [BitBoard; Color::NUM]); + +/// A collection of bitboards that organize pieces first by color and then by piece type. +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub(super) struct ByColorAndShape([[BitBoard; Shape::NUM]; Color::NUM]); + +impl ByColor { + pub(super) fn new(all_pieces: BitBoard, bitboards_by_color: [BitBoard; Color::NUM]) -> Self { + ByColor(all_pieces, bitboards_by_color) + } + + pub(crate) fn all(&self) -> BitBoard { + self.0 + } + + pub(crate) fn bitboard(&self, color: Color) -> BitBoard { + self.1[color as usize] + } + + pub(super) fn set_square(&mut self, square: Square, color: Color) { + self.0.set(square); + self.1[color as usize].set(square); + } + + pub(super) fn clear_square(&mut self, square: Square, color: Color) { + self.0.clear(square); + self.1[color as usize].clear(square); + } +} + +impl ByColorAndShape { + pub(super) fn new(bitboards: [[BitBoard; Shape::NUM]; Color::NUM]) -> Self { + Self(bitboards) + } + + pub(super) fn bitboard_for_piece(&self, piece: Piece) -> BitBoard { + self.0[piece.color() as usize][piece.shape() as usize] + } + + pub(super) fn bitboard_for_piece_mut(&mut self, piece: Piece) -> &mut BitBoard { + &mut self.0[piece.color() as usize][piece.shape() as usize] + } + + pub(super) fn set_square(&mut self, square: Square, piece: Piece) { + self.bitboard_for_piece_mut(piece).set(square); + } + + pub(super) fn clear_square(&mut self, square: Square, piece: Piece) { + self.bitboard_for_piece_mut(piece).clear(square); + } +} diff --git a/board/src/piece_sets/mailbox.rs b/board/src/piece_sets/mailbox.rs new file mode 100644 index 0000000..c551fb6 --- /dev/null +++ b/board/src/piece_sets/mailbox.rs @@ -0,0 +1,59 @@ +// Eryn Wells + +use chessfriend_core::{Piece, PlacedPiece, Square}; +use std::iter::FromIterator; + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub(super) struct Mailbox([Option; Square::NUM]); + +impl Mailbox { + pub(super) fn new() -> Self { + Self::default() + } + + pub(super) fn get(&self, square: Square) -> Option { + self.0[square as usize] + } + + pub(super) fn set(&mut self, square: Square, piece: Piece) { + self.0[square as usize] = Some(piece); + } + + pub(super) fn clear(&mut self, square: Square) { + self.0[square as usize] = None; + } +} + +impl Default for Mailbox { + fn default() -> Self { + Self([None; Square::NUM]) + } +} + +impl From<[Option; Square::NUM]> for Mailbox { + fn from(value: [Option; Square::NUM]) -> Self { + Mailbox(value) + } +} + +impl FromIterator for Mailbox { + fn from_iter>(iter: T) -> Self { + let mut mailbox = Self::new(); + for placed_piece in iter { + mailbox.set(placed_piece.square(), placed_piece.piece()); + } + + mailbox + } +} + +impl FromIterator<(Square, Piece)> for Mailbox { + fn from_iter>(iter: T) -> Self { + let mut mailbox = Self::new(); + for (square, piece) in iter { + mailbox.set(square, piece); + } + + mailbox + } +}