From 40e8e055f9211ef9e8e97d2007ef0fa26d26e762 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 31 May 2025 19:01:20 -0700 Subject: [PATCH] [board, moves, position] Move make_move routines to moves crate Declare a MakeMove trait and export it from chessfriend_moves. Declare a BoardProvider trait that both Board and Position implement. Implement the MakeMove trait for all types that implement BoardProvider, and move all the move making code to the moves crate. This change makes it possible to make moves directly on a Board, rather than requiring a Position. The indirection of declaring and implementing the trait in the moves crate is required because chessfriend_board is a dependency of chessfriend_moves. So, it would be a layering violation for Board to implement make_move() directly. The board crate cannot link the moves crate because that would introduce a circular dependency. --- board/src/board_provider.rs | 18 + board/src/lib.rs | 2 + moves/src/lib.rs | 2 + .../src/position => moves/src}/make_move.rs | 321 +++++++++--------- position/src/lib.rs | 2 +- position/src/position.rs | 17 +- position/src/testing.rs | 3 +- 7 files changed, 202 insertions(+), 163 deletions(-) create mode 100644 board/src/board_provider.rs rename {position/src/position => moves/src}/make_move.rs (55%) diff --git a/board/src/board_provider.rs b/board/src/board_provider.rs new file mode 100644 index 0000000..a653bfc --- /dev/null +++ b/board/src/board_provider.rs @@ -0,0 +1,18 @@ +// Eryn Wells + +use crate::Board; + +pub trait BoardProvider { + fn board(&self) -> &Board; + fn board_mut(&mut self) -> &mut Board; +} + +impl BoardProvider for Board { + fn board(&self) -> &Board { + self + } + + fn board_mut(&mut self) -> &mut Board { + self + } +} diff --git a/board/src/lib.rs b/board/src/lib.rs index 1df6832..451bc23 100644 --- a/board/src/lib.rs +++ b/board/src/lib.rs @@ -9,10 +9,12 @@ pub mod macros; pub mod movement; pub mod sight; +mod board_provider; mod check; mod piece_sets; pub use board::Board; +pub use board_provider::BoardProvider; pub use castle::Parameters as CastleParameters; pub use castle::Rights as CastleRights; pub use piece_sets::{PlacePieceError, PlacePieceStrategy}; diff --git a/moves/src/lib.rs b/moves/src/lib.rs index fd74a25..89cd4b6 100644 --- a/moves/src/lib.rs +++ b/moves/src/lib.rs @@ -5,11 +5,13 @@ pub mod testing; mod builder; mod defs; +mod make_move; mod moves; mod record; 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 moves::Move; pub use record::MoveRecord; diff --git a/position/src/position/make_move.rs b/moves/src/make_move.rs similarity index 55% rename from position/src/position/make_move.rs rename to moves/src/make_move.rs index cfb380a..06278ac 100644 --- a/position/src/position/make_move.rs +++ b/moves/src/make_move.rs @@ -1,11 +1,11 @@ // Eryn Wells -use crate::Position; +use crate::{Move, MoveRecord}; use chessfriend_board::{ - castle::CastleEvaluationError, movement::Movement, Board, PlacePieceError, PlacePieceStrategy, + castle::CastleEvaluationError, movement::Movement, Board, BoardProvider, PlacePieceError, + PlacePieceStrategy, }; use chessfriend_core::{Color, Piece, Rank, Square, Wing}; -use chessfriend_moves::{Move, MoveRecord}; use thiserror::Error; type MakeMoveResult = Result; @@ -60,23 +60,35 @@ pub enum MakeMoveError { PromotionRequired(Square), } -impl Position { +pub trait MakeMove { + fn make_move(&mut self, ply: Move, validate: ValidateMove) -> MakeMoveResult; +} + +trait MakeMoveInternal { + fn make_quiet_move(&mut self, ply: Move) -> MakeMoveResult; + fn make_double_push_move(&mut self, ply: Move) -> MakeMoveResult; + fn make_capture_move(&mut self, ply: Move) -> MakeMoveResult; + fn make_castle_move(&mut self, ply: Move, wing: Wing) -> MakeMoveResult; + fn make_promotion_move(&mut self, ply: Move) -> MakeMoveResult; + + fn validate_move(&self, ply: Move, validate: ValidateMove) -> Result<(), MakeMoveError>; + fn validate_active_piece(&self, ply: Move) -> Result; + + fn advance_clocks(&mut self, half_move_clock: HalfMoveClock); +} + +impl MakeMove for T { /// Make a move in the position. /// /// ## Errors /// /// If `validate` is [`ValidateMove::Yes`], perform validation of move correctness prior to /// applying the move. See [`Position::validate_move`]. - pub fn make_move(&mut self, ply: Move, validate: ValidateMove) -> Result<(), MakeMoveError> { - self.make_move_internal(ply, validate)?; - Ok(()) - } - - pub(crate) fn make_move_internal( + fn make_move( &mut self, ply: Move, validate: ValidateMove, - ) -> MakeMoveResult { + ) -> Result { if ply.is_quiet() { self.validate_move(ply, validate)?; return self.make_quiet_move(ply); @@ -105,20 +117,28 @@ impl Position { } } -impl Position { +impl MakeMoveInternal for T { fn make_quiet_move(&mut self, ply: Move) -> MakeMoveResult { + let board = self.board_mut(); + let origin = ply.origin_square(); - let piece = self + let piece = board .get_piece(origin) .ok_or(MakeMoveError::NoPiece(origin))?; let target = ply.target_square(); - self.place_piece_for_move(piece, target)?; + if piece.is_pawn() && target.rank().is_promotable_rank() { + return Err(MakeMoveError::PromotionRequired(target)); + } - self.remove_piece(origin); + board + .place_piece(piece, target, PlacePieceStrategy::PreserveExisting) + .map_err(MakeMoveError::PlacePieceError)?; - let record = self.register_move_record(ply, None); + board.remove_piece(origin); + + let record = MoveRecord::new(board, ply, None); self.advance_clocks(HalfMoveClock::Advance); @@ -126,22 +146,25 @@ impl Position { } fn make_double_push_move(&mut self, ply: Move) -> MakeMoveResult { + let board = self.board_mut(); + let origin = ply.origin_square(); - let piece = self - .board + let piece = board .remove_piece(origin) .ok_or(MakeMoveError::NoPiece(origin))?; let target = ply.target_square(); - self.place_piece_for_move(piece, target)?; + board + .place_piece(piece, target, PlacePieceStrategy::PreserveExisting) + .map_err(MakeMoveError::PlacePieceError)?; - self.board.en_passant_target = match target.rank() { + board.en_passant_target = match target.rank() { Rank::FOUR => Some(Square::from_file_rank(target.file(), Rank::THREE)), Rank::FIVE => Some(Square::from_file_rank(target.file(), Rank::SIX)), _ => unreachable!(), }; - let record = self.register_move_record(ply, None); + let record = MoveRecord::new(board, ply, None); self.advance_clocks(HalfMoveClock::Advance); @@ -152,11 +175,14 @@ impl Position { let origin_square = ply.origin_square(); let target_square = ply.target_square(); - let piece = self.get_piece_for_move(origin_square)?; + let board = self.board_mut(); + + let piece = board + .get_piece(origin_square) + .ok_or(MakeMoveError::NoPiece(origin_square))?; if ply.is_en_passant() { - let en_passant_square = self - .board + let en_passant_square = board .en_passant_target .ok_or(MakeMoveError::NoCaptureSquare)?; if target_square != en_passant_square { @@ -164,24 +190,23 @@ impl Position { } } + let board = self.board_mut(); + let capture_square = ply.capture_square().ok_or(MakeMoveError::NoCaptureSquare)?; - let captured_piece = self + let captured_piece = board .remove_piece(capture_square) .ok_or(MakeMoveError::NoCapturePiece(capture_square))?; - // Register the capture - self.captures.push(piece.color, captured_piece); - - self.remove_piece(origin_square).unwrap(); + board.remove_piece(origin_square).unwrap(); if let Some(promotion_shape) = ply.promotion_shape() { let promoted_piece = Piece::new(piece.color, promotion_shape); - self.place_piece(promoted_piece, target_square, PlacePieceStrategy::Replace)?; + board.place_piece(promoted_piece, target_square, PlacePieceStrategy::Replace)?; } else { - self.place_piece(piece, target_square, PlacePieceStrategy::Replace)?; + board.place_piece(piece, target_square, PlacePieceStrategy::Replace)?; } - let record = self.register_move_record(ply, Some(captured_piece)); + let record = MoveRecord::new(board, ply, Some(captured_piece)); self.advance_clocks(HalfMoveClock::Reset); @@ -189,20 +214,22 @@ impl Position { } fn make_castle_move(&mut self, ply: Move, wing: Wing) -> MakeMoveResult { - self.board.color_can_castle(wing, None)?; + let board = self.board_mut(); - let active_color = self.board.active_color; + board.color_can_castle(wing, None)?; + + let active_color = board.active_color; let parameters = Board::castling_parameters(wing, active_color); - let king = self.board.remove_piece(parameters.origin.king).unwrap(); - self.place_piece(king, parameters.target.king, PlacePieceStrategy::default())?; + let king = board.remove_piece(parameters.origin.king).unwrap(); + board.place_piece(king, parameters.target.king, PlacePieceStrategy::default())?; - let rook = self.board.remove_piece(parameters.origin.rook).unwrap(); - self.place_piece(rook, parameters.target.rook, PlacePieceStrategy::default())?; + let rook = board.remove_piece(parameters.origin.rook).unwrap(); + board.place_piece(rook, parameters.target.rook, PlacePieceStrategy::default())?; - self.board.castling_rights.revoke(active_color, wing); + board.castling_rights.revoke(active_color, wing); - let record = self.register_move_record(ply, None); + let record = MoveRecord::new(board, ply, None); self.advance_clocks(HalfMoveClock::Advance); @@ -210,9 +237,13 @@ impl Position { } fn make_promotion_move(&mut self, ply: Move) -> MakeMoveResult { + let board = self.board_mut(); + let origin = ply.origin_square(); - let piece = self.get_piece_for_move(origin)?; + let piece = board + .get_piece(origin) + .ok_or(MakeMoveError::NoPiece(origin))?; if !piece.is_pawn() { return Err(MakeMoveError::InvalidPiece(piece)); } @@ -223,68 +254,37 @@ impl Position { } if let Some(promotion_shape) = ply.promotion_shape() { - self.remove_piece(origin); + board.remove_piece(origin); let promoted_piece = Piece::new(piece.color, promotion_shape); - self.place_piece(promoted_piece, target, PlacePieceStrategy::PreserveExisting)?; + board.place_piece(promoted_piece, target, PlacePieceStrategy::PreserveExisting)?; } else { unreachable!( "Cannot make a promotion move with a ply that has no promotion shape: {ply:?}", ); } - let record = self.register_move_record(ply, None); + let record = MoveRecord::new(board, ply, None); self.advance_clocks(HalfMoveClock::Reset); Ok(record) } - fn register_move_record(&mut self, ply: Move, capture: Option) -> MoveRecord { - let record = MoveRecord::new(&self.board, ply, capture); - self.moves.push(record.clone()); - - record - } -} - -impl Position { - fn get_piece_for_move(&mut self, square: Square) -> Result { - self.get_piece(square).ok_or(MakeMoveError::NoPiece(square)) - } - - fn place_piece_for_move(&mut self, piece: Piece, square: Square) -> Result<(), MakeMoveError> { - if piece.is_pawn() && square.rank().is_promotable_rank() { - return Err(MakeMoveError::PromotionRequired(square)); - } - - self.place_piece(piece, square, PlacePieceStrategy::PreserveExisting) - .map_err(MakeMoveError::PlacePieceError) - } -} - -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -enum HalfMoveClock { - Reset, - #[default] - Advance, -} - -impl Position { fn advance_clocks(&mut self, half_move_clock: HalfMoveClock) { + let board = self.board_mut(); + match half_move_clock { - HalfMoveClock::Reset => self.board.half_move_clock = 0, - HalfMoveClock::Advance => self.board.half_move_clock += 1, + HalfMoveClock::Reset => board.half_move_clock = 0, + HalfMoveClock::Advance => board.half_move_clock += 1, } - self.board.active_color = self.board.active_color.next(); + board.active_color = board.active_color.next(); - if self.board.active_color == Color::White { - self.board.full_move_number += 1; + if board.active_color == Color::White { + board.full_move_number += 1; } } -} -impl Position { fn validate_move(&self, ply: Move, validate: ValidateMove) -> Result<(), MakeMoveError> { if validate == ValidateMove::No { return Ok(()); @@ -292,13 +292,14 @@ impl Position { let active_piece = self.validate_active_piece(ply)?; + let board = self.board(); let origin_square = ply.origin_square(); let target_square = ply.target_square(); // Pawns can see squares they can't move to. So, calculating valid // squares requires a concept that includes Sight, but adds pawn pushes. // In ChessFriend, that concept is Movement. - let movement = active_piece.movement(origin_square, &self.board); + let movement = active_piece.movement(origin_square, board); if !movement.contains(target_square) { return Err(MakeMoveError::NoMove { piece: active_piece, @@ -310,7 +311,7 @@ impl Position { // TODO: En Passant capture. if let Some(capture_square) = ply.capture_square() { - if let Some(captured_piece) = self.board.get_piece(capture_square) { + if let Some(captured_piece) = board.get_piece(capture_square) { if captured_piece.color == active_piece.color { return Err(MakeMoveError::InvalidCapture(capture_square)); } @@ -325,12 +326,13 @@ impl Position { fn validate_active_piece(&self, ply: Move) -> Result { let origin_square = ply.origin_square(); - let active_piece = self - .board + let board = self.board(); + + let active_piece = board .get_piece(origin_square) .ok_or(MakeMoveError::NoPiece(origin_square))?; - if active_piece.color != self.board.active_color { + if active_piece.color != board.active_color { return Err(MakeMoveError::NonActiveColor { piece: active_piece, square: origin_square, @@ -341,159 +343,167 @@ impl Position { } } +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +enum HalfMoveClock { + Reset, + #[default] + Advance, +} + #[cfg(test)] mod tests { use super::*; - use crate::{test_position, ValidateMove}; + use crate::{Move, PromotionShape}; + use chessfriend_board::test_board; use chessfriend_core::{piece, Color, Square}; - use chessfriend_moves::{Move, PromotionShape}; type TestResult = Result<(), MakeMoveError>; #[test] fn make_quiet_move() -> TestResult { - let mut pos = test_position!(White Pawn on C2); + let mut board = test_board!(White Pawn on C2); let ply = Move::quiet(Square::C2, Square::C3); - pos.make_move(ply, ValidateMove::Yes)?; + board.make_move(ply, ValidateMove::Yes)?; - assert_eq!(pos.get_piece(Square::C2), None); - assert_eq!(pos.get_piece(Square::C3), Some(piece!(White Pawn))); - assert_eq!(pos.board.active_color, Color::Black); - assert_eq!(pos.board.half_move_clock, 1); + assert_eq!(board.get_piece(Square::C2), None); + assert_eq!(board.get_piece(Square::C3), Some(piece!(White Pawn))); + assert_eq!(board.active_color, Color::Black); + assert_eq!(board.half_move_clock, 1); - pos.board.active_color = Color::White; + board.active_color = Color::White; let ply = Move::quiet(Square::C3, Square::C4); - pos.make_move(ply, ValidateMove::Yes)?; + board.make_move(ply, ValidateMove::Yes)?; - assert_eq!(pos.get_piece(Square::C3), None); - assert_eq!(pos.get_piece(Square::C4), Some(piece!(White Pawn))); - assert_eq!(pos.board.active_color, Color::Black); - assert_eq!(pos.board.half_move_clock, 2); + assert_eq!(board.get_piece(Square::C3), None); + assert_eq!(board.get_piece(Square::C4), Some(piece!(White Pawn))); + assert_eq!(board.active_color, Color::Black); + assert_eq!(board.half_move_clock, 2); Ok(()) } #[test] fn make_invalid_quiet_pawn_move() { - let mut pos = test_position!(White Pawn on C2); + let mut board = test_board!(White Pawn on C2); let ply = Move::quiet(Square::C2, Square::D2); - let result = pos.make_move(ply, ValidateMove::Yes); + let result = board.make_move(ply, ValidateMove::Yes); assert!(result.is_err()); - assert_eq!(pos.get_piece(Square::C2), Some(piece!(White Pawn))); - assert_eq!(pos.get_piece(Square::D2), None); - assert_eq!(pos.board.active_color, Color::White); - assert_eq!(pos.board.half_move_clock, 0); + assert_eq!(board.get_piece(Square::C2), Some(piece!(White Pawn))); + assert_eq!(board.get_piece(Square::D2), None); + assert_eq!(board.active_color, Color::White); + assert_eq!(board.half_move_clock, 0); } #[test] fn make_capture_move() -> TestResult { - let mut pos = test_position![ + let mut board = test_board![ White Bishop on C2, Black Rook on F5, ]; let ply = Move::capture(Square::C2, Square::F5); - pos.make_move(ply, ValidateMove::Yes)?; + let result = board.make_move(ply, ValidateMove::Yes)?; - assert_eq!(pos.get_piece(Square::C2), None); - assert_eq!(pos.get_piece(Square::F5), Some(piece!(White Bishop))); - assert_eq!(pos.captures.last(Color::White), Some(&piece!(Black Rook))); - assert_eq!(pos.board.active_color, Color::Black); - assert_eq!(pos.board.half_move_clock, 0); + assert_eq!(result.captured_piece, Some(piece!(Black Rook))); + + assert_eq!(board.get_piece(Square::C2), None); + assert_eq!(board.get_piece(Square::F5), Some(piece!(White Bishop))); + assert_eq!(board.active_color, Color::Black); + assert_eq!(board.half_move_clock, 0); Ok(()) } #[test] fn make_en_passant_capture_move() -> TestResult { - let mut pos = test_position![ + let mut board = test_board![ Black Pawn on F4, White Pawn on E2 ]; let ply = Move::double_push(Square::E2, Square::E4); - pos.make_move(ply, ValidateMove::Yes)?; + board.make_move(ply, ValidateMove::Yes)?; - assert_eq!(pos.get_piece(Square::E2), None); - assert_eq!(pos.get_piece(Square::E4), Some(piece!(White Pawn))); + assert_eq!(board.get_piece(Square::E2), None); + assert_eq!(board.get_piece(Square::E4), Some(piece!(White Pawn))); assert_eq!( - pos.board.en_passant_target, + board.en_passant_target, Some(Square::E3), "en passant square not set" ); - assert_eq!(pos.board.active_color, Color::Black); - assert_eq!(pos.board.half_move_clock, 1); + assert_eq!(board.active_color, Color::Black); + assert_eq!(board.half_move_clock, 1); let ply = Move::en_passant_capture(Square::F4, Square::E3); - pos.make_move(ply, ValidateMove::Yes)?; + let result = board.make_move(ply, ValidateMove::Yes)?; - assert_eq!(pos.get_piece(Square::F4), None); - assert_eq!(pos.get_piece(Square::E3), Some(piece!(Black Pawn))); + assert_eq!(result.captured_piece, Some(piece!(White Pawn))); + + assert_eq!(board.get_piece(Square::F4), None); + assert_eq!(board.get_piece(Square::E3), Some(piece!(Black Pawn))); assert_eq!( - pos.get_piece(Square::E4), + board.get_piece(Square::E4), None, "capture target pawn not removed" ); - assert_eq!(pos.captures.last(Color::Black), Some(&piece!(White Pawn))); Ok(()) } #[test] fn make_last_rank_quiet_move_without_promotion() { - let mut pos = test_position!( + let mut board = test_board!( White Pawn on A7 ); let ply = Move::quiet(Square::A7, Square::A8); - let result = pos.make_move(ply, ValidateMove::Yes); + let result = board.make_move(ply, ValidateMove::Yes); assert!(result.is_err()); - assert_eq!(pos.board.active_color, Color::White); - assert_eq!(pos.get_piece(Square::A7), Some(piece!(White Pawn))); - assert_eq!(pos.get_piece(Square::A8), None); - assert_eq!(pos.board.half_move_clock, 0); + assert_eq!(board.active_color, Color::White); + assert_eq!(board.get_piece(Square::A7), Some(piece!(White Pawn))); + assert_eq!(board.get_piece(Square::A8), None); + assert_eq!(board.half_move_clock, 0); } #[test] fn make_promotion_move() -> TestResult { - let mut pos = test_position![ + let mut board = test_board![ Black Pawn on E7, White Pawn on F7, ]; let ply = Move::promotion(Square::F7, Square::F8, PromotionShape::Queen); - pos.make_move(ply, ValidateMove::Yes)?; + board.make_move(ply, ValidateMove::Yes)?; - assert_eq!(pos.get_piece(Square::F7), None); - assert_eq!(pos.get_piece(Square::F8), Some(piece!(White Queen))); - assert_eq!(pos.board.active_color, Color::Black); - assert_eq!(pos.board.half_move_clock, 0); + assert_eq!(board.get_piece(Square::F7), None); + assert_eq!(board.get_piece(Square::F8), Some(piece!(White Queen))); + assert_eq!(board.active_color, Color::Black); + assert_eq!(board.half_move_clock, 0); Ok(()) } #[test] fn make_white_kingside_castle() -> TestResult { - let mut pos = test_position![ + let mut board = test_board![ White Rook on H1, White King on E1, ]; let ply = Move::castle(Wing::KingSide); - pos.make_move(ply, ValidateMove::Yes)?; + board.make_move(ply, ValidateMove::Yes)?; - assert_eq!(pos.board.active_color, Color::Black); - assert_eq!(pos.get_piece(Square::E1), None); - assert_eq!(pos.get_piece(Square::H1), None); - assert_eq!(pos.get_piece(Square::G1), Some(piece!(White King))); - assert_eq!(pos.get_piece(Square::F1), Some(piece!(White Rook))); - assert!(!pos - .board + assert_eq!(board.active_color, Color::Black); + assert_eq!(board.get_piece(Square::E1), None); + assert_eq!(board.get_piece(Square::H1), None); + assert_eq!(board.get_piece(Square::G1), Some(piece!(White King))); + assert_eq!(board.get_piece(Square::F1), Some(piece!(White Rook))); + assert!(!board .castling_rights .color_has_right(Color::White, Wing::KingSide)); @@ -502,21 +512,20 @@ mod tests { #[test] fn make_white_queenside_castle() -> TestResult { - let mut pos = test_position![ + let mut board = test_board![ White King on E1, White Rook on A1, ]; let ply = Move::castle(Wing::QueenSide); - pos.make_move(ply, ValidateMove::Yes)?; + board.make_move(ply, ValidateMove::Yes)?; - assert_eq!(pos.board.active_color, Color::Black); - assert_eq!(pos.get_piece(Square::E1), None); - assert_eq!(pos.get_piece(Square::A1), None); - assert_eq!(pos.get_piece(Square::C1), Some(piece!(White King))); - assert_eq!(pos.get_piece(Square::D1), Some(piece!(White Rook))); - assert!(!pos - .board + assert_eq!(board.active_color, Color::Black); + assert_eq!(board.get_piece(Square::E1), None); + assert_eq!(board.get_piece(Square::A1), None); + assert_eq!(board.get_piece(Square::C1), Some(piece!(White King))); + assert_eq!(board.get_piece(Square::D1), Some(piece!(White Rook))); + assert!(!board .castling_rights .color_has_right(Color::White, Wing::QueenSide)); diff --git a/position/src/lib.rs b/position/src/lib.rs index 0307e41..931c507 100644 --- a/position/src/lib.rs +++ b/position/src/lib.rs @@ -11,4 +11,4 @@ mod testing; pub use chessfriend_board::{fen, PlacePieceError, PlacePieceStrategy}; pub use chessfriend_moves::GeneratedMove; -pub use position::{Position, ValidateMove}; +pub use position::Position; diff --git a/position/src/position.rs b/position/src/position.rs index 1e0a829..8e32030 100644 --- a/position/src/position.rs +++ b/position/src/position.rs @@ -1,7 +1,6 @@ // Eryn Wells mod captures; -mod make_move; mod unmake_move; use chessfriend_moves::{ @@ -9,14 +8,14 @@ use chessfriend_moves::{ AllPiecesMoveGenerator, BishopMoveGenerator, KingMoveGenerator, KnightMoveGenerator, PawnMoveGenerator, QueenMoveGenerator, RookMoveGenerator, }, - GeneratedMove, Move, MoveRecord, + GeneratedMove, MakeMove, Move, MoveRecord, ValidateMove, }; -pub use make_move::ValidateMove; use captures::CapturesList; use chessfriend_bitboard::BitBoard; use chessfriend_board::{ - display::DiagramFormatter, fen::ToFenStr, Board, PlacePieceError, PlacePieceStrategy, + display::DiagramFormatter, fen::ToFenStr, Board, BoardProvider, PlacePieceError, + PlacePieceStrategy, }; use chessfriend_core::{Color, Piece, Shape, Square}; use std::fmt; @@ -142,6 +141,16 @@ 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 diff --git a/position/src/testing.rs b/position/src/testing.rs index 1439dee..2b1be8e 100644 --- a/position/src/testing.rs +++ b/position/src/testing.rs @@ -1,7 +1,6 @@ // Eryn Wells -use crate::position::MakeMoveError; -use chessfriend_moves::BuildMoveError; +use chessfriend_moves::{BuildMoveError, MakeMoveError}; #[macro_export] macro_rules! assert_move_list {