[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.
This commit is contained in:
parent
ecde338602
commit
40e8e055f9
7 changed files with 202 additions and 163 deletions
18
board/src/board_provider.rs
Normal file
18
board/src/board_provider.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
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<MoveRecord, MakeMoveError>;
|
||||
|
@ -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<Piece, MakeMoveError>;
|
||||
|
||||
fn advance_clocks(&mut self, half_move_clock: HalfMoveClock);
|
||||
}
|
||||
|
||||
impl<T: BoardProvider> 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<MoveRecord, MakeMoveError> {
|
||||
if ply.is_quiet() {
|
||||
self.validate_move(ply, validate)?;
|
||||
return self.make_quiet_move(ply);
|
||||
|
@ -105,20 +117,28 @@ impl Position {
|
|||
}
|
||||
}
|
||||
|
||||
impl Position {
|
||||
impl<T: BoardProvider> 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<Piece>) -> 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<Piece, MakeMoveError> {
|
||||
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<Piece, MakeMoveError> {
|
||||
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));
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
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
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use crate::position::MakeMoveError;
|
||||
use chessfriend_moves::BuildMoveError;
|
||||
use chessfriend_moves::{BuildMoveError, MakeMoveError};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_move_list {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue