// Eryn Wells mod captures; use chessfriend_moves::{ generators::{ AllPiecesMoveGenerator, BishopMoveGenerator, KingMoveGenerator, KnightMoveGenerator, PawnMoveGenerator, QueenMoveGenerator, RookMoveGenerator, }, GeneratedMove, MakeMove, MakeMoveError, Move, MoveRecord, UnmakeMove, UnmakeMoveError, UnmakeMoveResult, ValidateMove, }; use captures::CapturesList; use chessfriend_bitboard::BitBoard; use chessfriend_board::{ display::DiagramFormatter, fen::ToFenStr, Board, PlacePieceError, PlacePieceStrategy, }; use chessfriend_core::{Color, Piece, Shape, Square}; use std::fmt; #[must_use] #[derive(Clone, Debug, Default, Eq)] pub struct Position { pub board: Board, pub(crate) moves: Vec, pub(crate) captures: CapturesList, } impl Position { pub fn empty() -> Self { Position::default() } /// Return a starting position. pub fn starting() -> Self { Self { board: Board::starting(), ..Default::default() } } pub fn new(board: Board) -> Self { Self { board, ..Default::default() } } } impl Position { /// Place a piece on the board. /// /// ## Errors /// /// See [`chessfriend_board::Board::place_piece`]. pub fn place_piece( &mut self, piece: Piece, square: Square, strategy: PlacePieceStrategy, ) -> Result<(), PlacePieceError> { self.board.place_piece(piece, square, strategy) } #[must_use] pub fn get_piece(&self, square: Square) -> Option { self.board.get_piece(square) } pub fn remove_piece(&mut self, square: Square) -> Option { self.board.remove_piece(square) } } impl Position { pub fn sight(&self, square: Square) -> BitBoard { self.board.sight(square) } pub fn movement(&self, square: Square) -> BitBoard { self.board.movement(square) } } impl Position { pub fn all_moves(&self, color: Option) -> AllPiecesMoveGenerator { AllPiecesMoveGenerator::new(&self.board, color) } #[must_use] pub fn moves_for_piece( &self, square: Square, ) -> Option>> { self.get_piece(square) .map(|piece| Self::generator(&self.board, piece)) } #[must_use] fn generator(board: &Board, piece: Piece) -> Box> { match piece.shape { Shape::Pawn => Box::new(PawnMoveGenerator::new(board, Some(piece.color))), Shape::Knight => Box::new(KnightMoveGenerator::new(board, Some(piece.color))), Shape::Bishop => Box::new(BishopMoveGenerator::new(board, Some(piece.color))), Shape::Rook => Box::new(RookMoveGenerator::new(board, Some(piece.color))), Shape::Queen => Box::new(QueenMoveGenerator::new(board, Some(piece.color))), Shape::King => Box::new(KingMoveGenerator::new(board, Some(piece.color))), } } } impl Position { pub fn active_sight(&self) -> BitBoard { self.board.active_sight() } /// A [`BitBoard`] of all squares the given color can see. pub fn friendly_sight(&self, color: Color) -> BitBoard { self.board.friendly_sight(color) } /// A [`BitBoard`] of all squares visible by colors that oppose the given color. pub fn active_color_opposing_sight(&self) -> BitBoard { self.board.active_color_opposing_sight() } } impl Position { /// Make a move on the board and record it in the move list. /// /// ## Errors /// /// Returns one of [`MakeMoveError`] if the move cannot be made. /// pub fn make_move(&mut self, ply: Move, validate: ValidateMove) -> Result<(), MakeMoveError> { let record = self.board.make_move(ply, validate)?; if let Some(captured_piece) = record.captured_piece { self.captures.push(record.color, captured_piece); } self.moves.push(record.clone()); Ok(()) } /// Unmake the last move made on the board and remove its record from the /// move list. /// /// ## Errors /// /// Returns one of [`UnmakeMoveError`] if the move cannot be made. /// pub fn unmake_last_move(&mut self) -> UnmakeMoveResult { let last_move_record = self.moves.pop().ok_or(UnmakeMoveError::NoMove)?; let unmake_result = self.board.unmake_move(&last_move_record); if unmake_result.is_err() { self.moves.push(last_move_record); } unmake_result } } impl Position { pub fn display(&self) -> DiagramFormatter { self.board.display() } } impl ToFenStr for Position { type Error = ::Error; fn to_fen_str(&self) -> Result { self.board.to_fen_str() } } impl PartialEq for Position { fn eq(&self, other: &Self) -> bool { self.board == other.board } } impl fmt::Display for Position { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.board.display())?; if !self.captures.is_empty() { write!(f, "\n\n{}", self.captures)?; } Ok(()) } } #[cfg(test)] mod tests { use super::*; use crate::{test_position, Position}; use chessfriend_core::piece; #[test] fn piece_on_square() { let pos = test_position![ Black Bishop on F7, ]; let piece = pos.board.get_piece(Square::F7); assert_eq!(piece, Some(piece!(Black Bishop))); } #[test] fn piece_in_starting_position() { let pos = test_position!(starting); assert_eq!(pos.board.get_piece(Square::H1), Some(piece!(White Rook))); assert_eq!(pos.board.get_piece(Square::A8), Some(piece!(Black Rook))); } }