diff --git a/explorer/src/main.rs b/explorer/src/main.rs index 7fe6fcb..4a86a00 100644 --- a/explorer/src/main.rs +++ b/explorer/src/main.rs @@ -1,9 +1,11 @@ // Eryn Wells +mod make_command; + use chessfriend_board::{fen::FromFenStr, Board}; use chessfriend_core::{Color, Piece, Shape, Square}; -use chessfriend_moves::{Builder as MoveBuilder, GeneratedMove}; -use chessfriend_position::{fen::ToFenStr, PlacePieceStrategy, Position, ValidateMove}; +use chessfriend_moves::{Builder as MoveBuilder, GeneratedMove, MakeMove, ValidateMove}; +use chessfriend_position::{fen::ToFenStr, PlacePieceStrategy, Position}; use clap::{Arg, Command}; use rustyline::error::ReadlineError; diff --git a/moves/src/lib.rs b/moves/src/lib.rs index 048d4a8..5488215 100644 --- a/moves/src/lib.rs +++ b/moves/src/lib.rs @@ -13,7 +13,7 @@ mod unmake_move; pub use builder::{Builder, Error as BuildMoveError, Result as BuildMoveResult}; pub use defs::{Kind, PromotionShape}; pub use generators::GeneratedMove; -pub use make_move::{MakeMove, MakeMoveError, ValidateMove}; +pub use make_move::{MakeMove, MakeMoveError, MakeMoveResult, ValidateMove}; pub use moves::Move; pub use record::MoveRecord; -pub use unmake_move::{UnmakeMove, UnmakeMoveError}; +pub use unmake_move::{UnmakeMove, UnmakeMoveError, UnmakeMoveResult}; diff --git a/moves/src/make_move.rs b/moves/src/make_move.rs index 77c7fc5..ed79fa4 100644 --- a/moves/src/make_move.rs +++ b/moves/src/make_move.rs @@ -8,7 +8,7 @@ use chessfriend_board::{ use chessfriend_core::{Color, Piece, Rank, Square, Wing}; use thiserror::Error; -type MakeMoveResult = Result; +pub type MakeMoveResult = Result; #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub enum ValidateMove { diff --git a/moves/src/unmake_move.rs b/moves/src/unmake_move.rs index 11c95aa..368ba6d 100644 --- a/moves/src/unmake_move.rs +++ b/moves/src/unmake_move.rs @@ -5,7 +5,7 @@ use chessfriend_board::{Board, BoardProvider, PlacePieceError, PlacePieceStrateg use chessfriend_core::{Piece, Square}; use thiserror::Error; -type UnmakeMoveResult = Result<(), UnmakeMoveError>; +pub type UnmakeMoveResult = Result<(), UnmakeMoveError>; #[derive(Debug, Error, Eq, PartialEq)] pub enum UnmakeMoveError { @@ -42,7 +42,7 @@ pub trait UnmakeMove { /// Returns one of [`UnmakeMoveError`] indicating why the move cannot be /// unmade. /// - fn unmake_move(&mut self, record: MoveRecord) -> UnmakeMoveResult; + fn unmake_move(&mut self, record: &MoveRecord) -> UnmakeMoveResult; } trait UnmakeMoveInternal { @@ -53,17 +53,17 @@ trait UnmakeMoveInternal { } impl UnmakeMove for T { - fn unmake_move(&mut self, record: MoveRecord) -> UnmakeMoveResult { + fn unmake_move(&mut self, record: &MoveRecord) -> UnmakeMoveResult { let ply = record.ply; if ply.is_quiet() || ply.is_double_push() { - self.unmake_quiet_move(&record)?; + self.unmake_quiet_move(record)?; } else if ply.is_capture() || ply.is_en_passant() { - self.unmake_capture_move(&record)?; + self.unmake_capture_move(record)?; } else if ply.is_promotion() { - self.unmake_promotion_move(&record)?; + self.unmake_promotion_move(record)?; } else if ply.is_castle() { - self.unmake_castle_move(&record)?; + self.unmake_castle_move(record)?; } else { unreachable!(); } @@ -216,7 +216,7 @@ mod tests { ); // Unmake the move - initial_board.unmake_move(record)?; + initial_board.unmake_move(&record)?; // Verify we're back to the initial state assert_eq!( @@ -240,8 +240,7 @@ mod tests { assert_eq!(board.get_piece(Square::C3), Some(piece!(White Pawn))); assert_eq!(board.active_color, Color::Black); - // Unmake the move - board.unmake_move(record)?; + board.unmake_move(&record)?; // Verify original state restored assert_eq!(board.get_piece(Square::C2), Some(piece!(White Pawn))); @@ -264,8 +263,7 @@ mod tests { assert_eq!(board.en_passant_target, Some(Square::E3)); assert_eq!(board.active_color, Color::Black); - // Unmake the move - board.unmake_move(record)?; + board.unmake_move(&record)?; // Verify original state restored assert_eq!(board.get_piece(Square::E2), Some(piece!(White Pawn))); @@ -292,8 +290,7 @@ mod tests { assert_eq!(record.captured_piece, Some(piece!(Black Rook))); assert_eq!(board.active_color, Color::Black); - // Unmake the move - board.unmake_move(record)?; + board.unmake_move(&record)?; // Verify original state restored assert_eq!(board.get_piece(Square::C2), Some(piece!(White Bishop))); @@ -328,8 +325,7 @@ mod tests { ); assert_eq!(record.captured_piece, Some(piece!(White Pawn))); - // Unmake the en passant capture - board.unmake_move(record)?; + board.unmake_move(&record)?; // Verify state before en passant is restored assert_eq!(board.get_piece(Square::F4), Some(piece!(Black Pawn))); @@ -358,8 +354,7 @@ mod tests { assert_eq!(board.get_piece(Square::F8), Some(piece!(White Queen))); assert_eq!(board.active_color, Color::Black); - // Unmake the promotion - board.unmake_move(record)?; + board.unmake_move(&record)?; // Verify original pawn is restored assert_eq!(board.get_piece(Square::F7), Some(piece!(White Pawn))); @@ -384,8 +379,7 @@ mod tests { assert_eq!(board.get_piece(Square::G8), Some(piece!(White Queen))); assert_eq!(record.captured_piece, Some(piece!(Black Rook))); - // Unmake the promotion capture - board.unmake_move(record)?; + board.unmake_move(&record)?; // Verify original state restored assert_eq!(board.get_piece(Square::F7), Some(piece!(White Pawn))); @@ -416,8 +410,7 @@ mod tests { .castling_rights .color_has_right(Color::White, Wing::KingSide)); - // Unmake the castle - board.unmake_move(record)?; + board.unmake_move(&record)?; // Verify original state restored assert_eq!(board.get_piece(Square::E1), Some(piece!(White King))); @@ -451,8 +444,7 @@ mod tests { .castling_rights .color_has_right(Color::White, Wing::QueenSide)); - // Unmake the castle - board.unmake_move(record)?; + board.unmake_move(&record)?; // Verify original state restored assert_eq!(board.get_piece(Square::E1), Some(piece!(White King))); @@ -484,8 +476,7 @@ mod tests { assert_eq!(board.get_piece(Square::G8), Some(piece!(Black King))); assert_eq!(board.get_piece(Square::F8), Some(piece!(Black Rook))); - // Unmake the castle - board.unmake_move(record)?; + board.unmake_move(&record)?; // Verify original state restored assert_eq!(board.get_piece(Square::E8), Some(piece!(Black King))); diff --git a/position/src/position.rs b/position/src/position.rs index 8e32030..4839095 100644 --- a/position/src/position.rs +++ b/position/src/position.rs @@ -1,21 +1,20 @@ // Eryn Wells mod captures; -mod unmake_move; use chessfriend_moves::{ generators::{ AllPiecesMoveGenerator, BishopMoveGenerator, KingMoveGenerator, KnightMoveGenerator, PawnMoveGenerator, QueenMoveGenerator, RookMoveGenerator, }, - GeneratedMove, MakeMove, Move, MoveRecord, ValidateMove, + GeneratedMove, MakeMove, MakeMoveError, Move, MoveRecord, UnmakeMove, UnmakeMoveError, + UnmakeMoveResult, ValidateMove, }; use captures::CapturesList; use chessfriend_bitboard::BitBoard; use chessfriend_board::{ - display::DiagramFormatter, fen::ToFenStr, Board, BoardProvider, PlacePieceError, - PlacePieceStrategy, + display::DiagramFormatter, fen::ToFenStr, Board, PlacePieceError, PlacePieceStrategy, }; use chessfriend_core::{Color, Piece, Shape, Square}; use std::fmt; @@ -127,6 +126,45 @@ impl Position { } } +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() @@ -141,16 +179,6 @@ impl ToFenStr for Position { } } -impl BoardProvider for Position { - fn board(&self) -> &Board { - &self.board - } - - fn board_mut(&mut self) -> &mut Board { - &mut self.board - } -} - impl PartialEq for Position { fn eq(&self, other: &Self) -> bool { self.board == other.board