// Eryn Wells use super::{flags::Flags, piece_sets::PieceBitBoards, Pieces}; use crate::{ move_generator::Moves, piece::{Color, Piece, PlacedPiece, Shape}, sight::Sight, BitBoard, Move, Square, }; use std::cell::OnceCell; /// 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, } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Position { color_to_move: Color, flags: Flags, pieces: PieceBitBoards, en_passant_square: Option, sight: [OnceCell; 2], } impl Position { pub fn empty() -> Position { Position { color_to_move: Color::White, flags: Default::default(), pieces: PieceBitBoards::default(), en_passant_square: None, sight: [OnceCell::new(), OnceCell::new()], } } /// Return a starting position. pub fn starting() -> Self { 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), ]; Self { color_to_move: Color::White, flags: Flags::default(), pieces: PieceBitBoards::new([white_pieces, black_pieces]), en_passant_square: None, sight: [OnceCell::new(), OnceCell::new()], } } pub fn player_to_move(&self) -> Color { self.color_to_move } /// 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) } pub fn moves(&self) -> Moves { Moves::new(self, self.color_to_move) } /// Return a BitBoard representing the set of squares containing a piece. #[inline] pub(crate) fn occupied_squares(&self) -> &BitBoard { &self.pieces.all_pieces() } #[inline] pub(crate) fn friendly_pieces(&self) -> &BitBoard { self.pieces.all_pieces_of_color(self.color_to_move) } #[inline] pub(crate) fn opposing_pieces(&self) -> &BitBoard { self.pieces.all_pieces_of_color(self.color_to_move.other()) } pub(crate) fn all_pieces(&self) -> (&BitBoard, &BitBoard) { (self.friendly_pieces(), self.opposing_pieces()) } /// 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 fn piece_on_square(&self, sq: Square) -> Option { for color in Color::iter() { for shape in Shape::iter() { let piece = Piece::new(color, *shape); if self.pieces.bitboard_for_piece(piece).is_set(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 } pub(crate) fn sight_of_player(&self, color: Color) -> BitBoard { *self.sight[color as usize].get_or_init(|| { self.pieces(color).fold(BitBoard::empty(), |acc, pp| { acc | pp.sight_in_position(&self) }) }) } pub(crate) fn is_king_in_check(&self) -> bool { let sight_of_opposing_player = self.sight_of_player(self.color_to_move.other()); sight_of_opposing_player.is_set(self.king_square()) } fn king_square(&self) -> Square { self.pieces .bitboard_for_piece(Piece::king(self.color_to_move)) .occupied_squares() .next() .unwrap() } pub(crate) fn move_is_legal(&self, mv: Move) -> bool { true } } // crate::position methods impl Position { pub(super) fn new( player_to_move: Color, flags: Flags, pieces: PieceBitBoards, en_passant_square: Option, ) -> Self { Self { color_to_move: player_to_move, flags, en_passant_square, pieces, sight: [OnceCell::new(), OnceCell::new()], } } pub(super) fn flags(&self) -> Flags { self.flags } } // crate methods impl Position { pub(crate) fn bitboard_for_color(&self, color: Color) -> &BitBoard { self.pieces.bitboard_for_color(color) } pub(crate) fn bitboard_for_piece(&self, piece: Piece) -> &BitBoard { self.pieces.bitboard_for_piece(piece) } } #[cfg(test)] impl Position { pub(crate) fn test_set_en_passant_square(&mut self, square: Square) { self.en_passant_square = Some(square); } } impl Default for Position { fn default() -> Self { Self::empty() } } #[cfg(test)] mod tests { use crate::position; #[test] fn king_is_in_check() { let pos = position![ White King on E1, Black Rook on E8, ]; assert!(pos.is_king_in_check()); } #[test] fn king_is_not_in_check() { let pos = position![ White King on F1, Black Rook on E8, ]; assert!(!pos.is_king_in_check()); } }