// Eryn Wells use super::{flags::Flags, Pieces}; use crate::{ moves::Moves, piece::{Color, Piece, PiecePlacementError, PlacedPiece, Shape}, BitBoard, Square, }; use std::fmt; use std::fmt::Write; /// A lateral side of the board relative to the player. Kingside is the side the /// player's king starts on. Queenside is the side of the board the player's /// queen starts on. pub(crate) enum BoardSide { King, Queen, } #[macro_export] macro_rules! position { [$($color:ident $shape:ident on $square:ident,)*] => { Position::from_iter([ $(piece!($color $shape on $square)),* ].into_iter()) }; } #[derive(Clone, Eq, Hash, PartialEq)] pub struct Position { color_to_move: Color, /// Position flags indicating various bits of game state. The flags are as /// follows: /// /// 0. white can castle king-side /// 1. white can castle queen-side /// 2. black can castle king-side /// 3. black can castle queen-side flags: Flags, /// Composite bitboards for all the pieces of a particular color. pieces_per_color: [BitBoard; 2], /// Bitboards representing positions of particular piece types per color. pieces_per_type: [[BitBoard; 6]; 2], en_passant_square: Option, } impl Position { pub fn empty() -> Position { Position { color_to_move: Color::White, flags: Default::default(), pieces_per_color: [BitBoard::empty(); 2], pieces_per_type: [ [ BitBoard::empty(), BitBoard::empty(), BitBoard::empty(), BitBoard::empty(), BitBoard::empty(), BitBoard::empty(), ], [ BitBoard::empty(), BitBoard::empty(), BitBoard::empty(), BitBoard::empty(), BitBoard::empty(), BitBoard::empty(), ], ], en_passant_square: None, } } /// Return a starting position. pub fn starting() -> Position { let white_pieces = [ BitBoard::new(0x00FF000000000000), BitBoard::new(0x4200000000000000), BitBoard::new(0x2400000000000000), BitBoard::new(0x8100000000000000), BitBoard::new(0x1000000000000000), BitBoard::new(0x0800000000000000), ]; let black_pieces = [ BitBoard::new(0xFF00), BitBoard::new(0x0042), BitBoard::new(0x0024), BitBoard::new(0x0081), BitBoard::new(0x0010), BitBoard::new(0x0008), ]; Position { color_to_move: Color::White, flags: Default::default(), pieces_per_color: [ white_pieces.iter().fold(BitBoard::empty(), |a, b| a | *b), black_pieces.iter().fold(BitBoard::empty(), |a, b| a | *b), ], pieces_per_type: [white_pieces, black_pieces], en_passant_square: None, } } /// Returns true if the player has the right to castle on the given side of /// the board. /// /// The right to castle on a particular side of the board is retained as /// long as the player has not moved their king, or the rook on that side of /// the board. /// /// The following requirements must also be met: /// /// 1. The spaces between the king and rook must be clear /// 2. The king must not be in check /// 3. In the course of castling on that side, the king must not pass /// through a square that an enemy piece can see pub(crate) fn player_has_right_to_castle(&self, color: Color, side: BoardSide) -> bool { self.flags.player_has_right_to_castle(color, side) } fn set_player_has_right_to_castle_flag(&mut self, color: Color, side: BoardSide) { self.flags.set_player_has_right_to_castle_flag(color, side); } fn clear_player_has_right_to_castle_flag(&mut self, color: Color, side: BoardSide) { self.flags .clear_player_has_right_to_castle_flag(color, side); } pub fn place_piece(&mut self, piece: Piece, square: Square) -> Result<(), PiecePlacementError> { let type_bb = self.bitboard_for_piece_mut(piece); if type_bb.has_piece_at(square) { return Err(PiecePlacementError::ExistsOnSquare); } type_bb.place_piece_at(square); let color_bb = &mut self.bitboard_for_color_mut(piece.color()); color_bb.place_piece_at(square); Ok(()) } pub fn move_generator(&self, color: Color) -> Moves { Moves::new(self, color) } /// Return a BitBoard representing the set of squares containing a piece. #[inline] pub(crate) fn occupied_squares(&self) -> BitBoard { self.pieces_per_color[Color::White as usize] | self.pieces_per_color[Color::Black as usize] } #[inline] pub(crate) fn friendly_pieces(&self) -> BitBoard { self.bitboard_for_color(self.color_to_move) } #[inline] pub(crate) fn opposing_pieces(&self) -> BitBoard { self.bitboard_for_color(self.color_to_move.other()) } /// Return a BitBoard representing the set of squares containing a piece. /// This set is the inverse of `occupied_squares`. #[inline] pub(crate) fn empty_squares(&self) -> BitBoard { !self.occupied_squares() } pub(crate) fn bitboard_for_piece(&self, piece: Piece) -> BitBoard { self.pieces_per_type[piece.color() as usize][piece.shape() as usize] } fn bitboard_for_piece_mut(&mut self, piece: Piece) -> &mut BitBoard { &mut self.pieces_per_type[piece.color() as usize][piece.shape() as usize] } pub(crate) fn bitboard_for_color(&self, color: Color) -> BitBoard { self.pieces_per_color[color as usize] } fn bitboard_for_color_mut(&mut self, color: Color) -> &mut BitBoard { &mut self.pieces_per_color[color as usize] } pub fn piece_on_square(&self, sq: Square) -> Option { for color in Color::iter() { for shape in Shape::iter() { let piece = Piece::new(color, *shape); let bb = self.bitboard_for_piece(piece); if bb.has_piece_at(sq) { return Some(PlacedPiece::new(piece, sq)); } } } None } pub fn pieces(&self, color: Color) -> Pieces { Pieces::new(&self, color) } pub fn en_passant_square(&self) -> Option { self.en_passant_square } } impl Default for Position { fn default() -> Self { Self::empty() } } impl FromIterator for Position { fn from_iter>(iter: T) -> Self { let mut position = Position::empty(); for placed_piece in iter { _ = position.place_piece(placed_piece.piece(), placed_piece.square()); } position } } impl fmt::Debug for Position { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut output = String::new(); output.push_str("Position {\n"); write!(output, " color_to_move: {:?},\n", self.color_to_move)?; output.push_str(" pieces_per_color: [\n"); for bb in self.pieces_per_color { write!(output, " {bb:?},\n")?; } output.push_str(" ],\n"); output.push_str(" pieces_per_type: [\n"); for color_bbs in self.pieces_per_type { output.push_str(" [\n"); for bb in color_bbs { write!(output, " {bb:?},\n")?; } output.push_str(" ],\n"); } output.push_str(" ],\n}"); write!(f, "{}", output) } } #[cfg(test)] mod tests { use super::*; use crate::piece::Shape; #[test] fn place_piece() { let mut position = Position::empty(); let piece = Piece::new(Color::White, Shape::Queen); let square = Square::E4; position .place_piece(piece, square) .expect("Unable to place white queen on e4"); assert_eq!( position.bitboard_for_color(piece.color()).clone(), BitBoard::new(1 << 28) ); assert_eq!( position.bitboard_for_piece(piece).clone(), BitBoard::new(1 << 28) ); position .place_piece(piece, square) .expect_err("Placed white queen on e4 a second time?!"); } }