[explorer, moves, position] Implement bespoke make_move and unmake_move methods on Position

Instead of inheriting the MakeMove and UnmakeMove traits by being a BoardProvider,
implement bespoke versions of these two methods. This gives Position a chance to
do some of its own work (tracking captures, move records, etc) when making a move.

Pass the move record by reference to the unmake_move() method. Saves a copy.

Export the Result types for MakeMove and UnmakeMove.
This commit is contained in:
Eryn Wells 2025-06-01 19:02:53 -07:00
parent 724a98c2e2
commit 8f42a4c94e
5 changed files with 66 additions and 45 deletions

View file

@ -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};

View file

@ -8,7 +8,7 @@ use chessfriend_board::{
use chessfriend_core::{Color, Piece, Rank, Square, Wing};
use thiserror::Error;
type MakeMoveResult = Result<MoveRecord, MakeMoveError>;
pub type MakeMoveResult = Result<MoveRecord, MakeMoveError>;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum ValidateMove {

View file

@ -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<T: BoardProvider> 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)));