// Eryn Wells use crate::{ castle, display::DiagramFormatter, piece_sets::{PlacePieceError, PlacePieceStrategy}, PieceSet, }; use chessfriend_bitboard::BitBoard; use chessfriend_core::{Color, Piece, Shape, Square, Wing}; pub type HalfMoveClock = u32; pub type FullMoveClock = u32; #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct Board { active_color: Color, pieces: PieceSet, castling_rights: castle::Rights, en_passant_target: Option, pub half_move_clock: HalfMoveClock, pub full_move_number: FullMoveClock, } impl Board { /// An empty board #[must_use] pub fn empty() -> Self { Board::default() } /// The starting position #[must_use] pub fn starting() -> Self { const BLACK_PIECES: [BitBoard; Shape::NUM] = [ BitBoard::new(0b0000_0000_1111_1111 << 48), BitBoard::new(0b0100_0010_0000_0000 << 48), BitBoard::new(0b0010_0100_0000_0000 << 48), BitBoard::new(0b1000_0001_0000_0000 << 48), BitBoard::new(0b0000_1000_0000_0000 << 48), BitBoard::new(0b0001_0000_0000_0000 << 48), ]; const WHITE_PIECES: [BitBoard; Shape::NUM] = [ BitBoard::new(0b1111_1111_0000_0000), BitBoard::new(0b0000_0000_0100_0010), BitBoard::new(0b0000_0000_0010_0100), BitBoard::new(0b0000_0000_1000_0001), BitBoard::new(0b0000_0000_0000_1000), BitBoard::new(0b0000_0000_0001_0000), ]; Self { pieces: PieceSet::new([WHITE_PIECES, BLACK_PIECES]), ..Default::default() } } } impl Board { #[must_use] pub fn active_color(&self) -> Color { self.active_color } pub fn set_active_color(&mut self, color: Color) { if color == self.active_color { return; } self.active_color = color; } impl Board { #[must_use] pub fn castling_rights(&self) -> castle::Rights { self.castling_rights } pub fn set_castling_rights(&mut self, rights: castle::Rights) { self.castling_rights = rights; } #[must_use] pub fn active_color_has_castling_right(&self, wing: Wing) -> bool { self.color_has_castling_right(self.active_color, wing) } #[must_use] pub fn color_has_castling_right(&self, color: Color, wing: Wing) -> bool { self.castling_rights.color_has_right(color, wing) } pub fn grant_castling_right(&mut self, color: Color, wing: Wing) { self.castling_rights.grant(color, wing); } pub fn revoke_all_castling_rights(&mut self) { self.castling_rights.revoke_all(); } pub fn revoke_castling_right(&mut self, color: Color, wing: Wing) { self.castling_rights.revoke(color, wing); } } impl Board { /// Returns a copy of the current en passant square, if one exists. #[must_use] pub fn en_passant_target(&self) -> Option { self.en_passant_target } pub fn set_en_passant_target(&mut self, square: Square) { self.set_en_passant_target_option(Some(square)); } pub fn set_en_passant_target_option(&mut self, square: Option) { self.en_passant_target = square; } pub fn clear_en_passant_target(&mut self) { self.en_passant_target = None; } } impl Board { #[must_use] pub fn get_piece(&self, square: Square) -> Option { self.pieces.get(square) } pub fn find_pieces(&self, piece: Piece) -> BitBoard { self.pieces.find_pieces(piece) } /// Place a piece on the board. /// /// ## Errors /// /// When is called with [`PlacePieceStrategy::PreserveExisting`], and a /// piece already exists on `square`, this method returns a /// [`PlacePieceError::ExistingPiece`] error. /// pub fn place_piece( &mut self, piece: Piece, square: Square, strategy: PlacePieceStrategy, ) -> Result, PlacePieceError> { self.pieces.place(piece, square, strategy) } pub fn remove_piece(&mut self, square: Square) -> Option { self.pieces.remove(square) } } impl Board { pub fn iter(&self) -> impl Iterator { self.pieces.iter() } /// A [`BitBoard`] of squares occupied by pieces of all colors. pub fn occupancy(&self) -> BitBoard { self.pieces.occpuancy() } /// A [`BitBoard`] of squares that are vacant. pub fn vacancy(&self) -> BitBoard { !self.occupancy() } pub fn friendly_occupancy(&self, color: Color) -> BitBoard { self.pieces.friendly_occupancy(color) } pub fn opposing_occupancy(&self, color: Color) -> BitBoard { self.pieces.opposing_occupancy(color) } pub fn enemies(&self, color: Color) -> BitBoard { self.pieces.opposing_occupancy(color) } /// Return a [`BitBoard`] of all pawns of a given color. pub fn pawns(&self, color: Color) -> BitBoard { self.pieces.find_pieces(Piece::pawn(color)) } pub fn knights(&self, color: Color) -> BitBoard { self.find_pieces(Piece::knight(color)) } pub fn bishops(&self, color: Color) -> BitBoard { self.find_pieces(Piece::bishop(color)) } pub fn rooks(&self, color: Color) -> BitBoard { self.find_pieces(Piece::rook(color)) } pub fn queens(&self, color: Color) -> BitBoard { self.find_pieces(Piece::queen(color)) } pub fn kings(&self, color: Color) -> BitBoard { self.find_pieces(Piece::king(color)) } } impl Board { pub fn display(&self) -> DiagramFormatter<'_> { DiagramFormatter::new(self) } } impl Board { #[must_use] pub fn unwrap_color(&self, color: Option) -> Color { color.unwrap_or(self.active_color) } } #[cfg(test)] mod tests { use super::*; use crate::test_board; use chessfriend_core::piece; #[test] fn get_piece_on_square() { let board = test_board![ Black Bishop on F7, ]; assert_eq!(board.get_piece(Square::F7), Some(piece!(Black Bishop))); } }