// Eryn Wells use crate::{ castle, display::DiagramFormatter, piece_sets::PlacePieceError, EnPassant, MoveCounter, PieceSet, }; use chessfriend_bitboard::BitBoard; use chessfriend_core::{Color, Piece, PlacedPiece, Shape, Square}; use std::iter::Iterator; #[derive(Clone, Debug, Eq)] pub struct Board { pieces: PieceSet, en_passant: Option, pub move_counter: MoveCounter, pub castling_rights: castle::Rights, } 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() } } #[must_use] pub fn player_to_move(&self) -> Color { self.move_counter.active_color } /// Returns `true` if the player has the right to castle on the given side /// of the board. /// /// A player retains the right to castle on a particular side of the board /// as long as they have not moved their king, or the rook on that side of /// the board. #[must_use] pub fn player_has_right_to_castle(&self, color: Color, castle: Castle) -> bool { self.flags.player_has_right_to_castle(color, castle) } /// The rook to use for a castling move. #[must_use] pub fn rook_for_castle(&self, player: Color, castle: Castle) -> Option { let square = castle.parameters(player).rook_origin_square(); self.piece_on_square(square) } /// Returns `true` if the player is able to castle on the given side of the board. /// /// The following requirements must be met: /// /// 1. The player must still have the right to castle on that side of the /// board. The king and rook involved in the castle must not have moved. /// 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 fn player_can_castle(&self, player: Color, castle: Castle) -> bool { if !self .castling_rights .player_has_right_to_castle(player, castle.into()) { return false; } let castling_parameters = castle.parameters(player); let all_pieces = self.all_pieces_bitboard(); if !(all_pieces & castling_parameters.clear_squares()).is_empty() { return false; } let danger_squares = self.king_danger(player); if !(danger_squares & castling_parameters.check_squares()).is_empty() { return false; } true } /// A [`BitBoard`] representing the set of squares containing a piece. This /// set is the inverse of [`Board::empty_squares`]. #[must_use] pub fn occupied_squares(&self) -> BitBoard { self.pieces.all_pieces() } #[must_use] pub fn friendly_pieces_bitboard(&self) -> BitBoard { self.pieces.all_pieces_of_color(self.player_to_move) } #[must_use] pub fn opposing_pieces_bitboard(&self) -> BitBoard { self.pieces.all_pieces_of_color(self.player_to_move.other()) } #[must_use] pub fn all_pieces(&self) -> (BitBoard, BitBoard) { ( self.friendly_pieces_bitboard(), self.opposing_pieces_bitboard(), ) } /// A [BitBoard] representing the set of squares containing a piece. This /// set is the inverse of [`Board::occupied_squares`]. #[must_use] pub fn empty_squares(&self) -> BitBoard { !self.occupied_squares() } #[must_use] pub fn piece_on_square(&self, square: Square) -> Option { self.pieces .get(square) .map(|piece| PlacedPiece::new(piece, square)) } #[must_use] pub fn iter_all_pieces(&self) -> impl Iterator + '_ { self.pieces.iter() } #[must_use] pub fn iter_pieces_of_color(&self, color: Color) -> impl Iterator + '_ { self.pieces .iter() .filter(move |piece| piece.color() == color) } #[must_use] pub fn has_en_passant_square(&self) -> bool { self.en_passant.is_some() } #[must_use] pub fn en_passant(&self) -> Option { self.en_passant } fn king_bitboard(&self, player: Color) -> BitBoard { self.pieces.bitboard_for_piece(Piece::king(player)) } pub(crate) fn king_square(&self, player: Color) -> Square { self.king_bitboard(player) .occupied_squares() .next() .unwrap() } #[must_use] pub fn display(&self) -> DiagramFormatter<'_> { DiagramFormatter::new(self) } #[must_use] pub fn bitboard_for_color(&self, color: Color) -> BitBoard { self.pieces.bitboard_for_color(color) } #[must_use] pub fn bitboard_for_piece(&self, piece: Piece) -> BitBoard { self.pieces.bitboard_for_piece(piece) } } impl Default for Board { fn default() -> Self { Self { castling_rights: castle::Rights::default(), pieces: PieceSet::default(), en_passant: None, move_counter: MoveCounter::default(), } } } impl PartialEq for Board { fn eq(&self, other: &Self) -> bool { self.pieces == other.pieces && self.castling_rights == other.castling_rights && self.en_passant == other.en_passant && self.move_counter == other.move_counter } } #[cfg(test)] mod tests { use super::*; use crate::test_board; use chessfriend_core::piece; #[test] fn piece_on_square() { let pos = test_board![ Black Bishop on F7, ]; let piece = pos.piece_on_square(Square::F7); assert_eq!(piece, Some(piece!(Black Bishop on F7))); } #[test] fn piece_in_starting_position() { let board = test_board!(starting); assert_eq!( board.piece_on_square(Square::H1), Some(piece!(White Rook on H1)) ); assert_eq!( board.piece_on_square(Square::A8), Some(piece!(Black Rook on A8)) ); } #[test] fn king_not_on_starting_square_cannot_castle() { let board = test_board!(White King on E4); assert!(!board .castling_rights .player_has_right_to_castle(Color::White, Castle::KingSide)); assert!(!board .castling_rights .player_has_right_to_castle(Color::White, Castle::QueenSide)); } #[test] fn king_on_starting_square_can_castle() { let board = test_board!( White King on E1, White Rook on A1, White Rook on H1 ); assert!(board .castling_rights .player_has_right_to_castle(Color::White, Castle::KingSide)); assert!(board .castling_rights .player_has_right_to_castle(Color::White, Castle::QueenSide)); } #[test] fn rook_for_castle() { let board = test_board![ White King on E1, White Rook on H1, White Rook on A1, ]; assert_eq!( board.rook_for_castle(Color::White, Castle::KingSide), Some(piece!(White Rook on H1)) ); assert_eq!( board.rook_for_castle(Color::White, Castle::QueenSide), Some(piece!(White Rook on A1)) ); } }