[position] Add move tracking to Position
Create a new MoveRecord struct that tracks the move (aka ply) and irreversible board properties. This should make it easier to unmake moves in the future.
This commit is contained in:
parent
05c62dcd99
commit
a9268ad194
4 changed files with 81 additions and 26 deletions
|
@ -1,5 +1,6 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
mod move_record;
|
||||
mod position;
|
||||
|
||||
#[macro_use]
|
||||
|
|
15
position/src/move_record.rs
Normal file
15
position/src/move_record.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use chessfriend_board::{board::HalfMoveClock, CastleRights};
|
||||
use chessfriend_core::Square;
|
||||
use chessfriend_moves::Move;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) struct MoveRecord {
|
||||
pub ply: Move,
|
||||
pub en_passant_target: Option<Square>,
|
||||
pub castling_rights: CastleRights,
|
||||
pub half_move_clock: HalfMoveClock,
|
||||
}
|
||||
|
||||
impl MoveRecord {}
|
|
@ -1,14 +1,14 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use crate::Position;
|
||||
use chessfriend_board::{movement::Movement, PlacePieceError, PlacePieceStrategy};
|
||||
use crate::{move_record::MoveRecord, Position};
|
||||
use chessfriend_board::{
|
||||
castle::CastleEvaluationError, movement::Movement, PlacePieceError, PlacePieceStrategy,
|
||||
};
|
||||
use chessfriend_core::{Color, Piece, Rank, Square, Wing};
|
||||
use chessfriend_moves::Move;
|
||||
use thiserror::Error;
|
||||
|
||||
use super::CastleEvaluationError;
|
||||
|
||||
type MakeMoveResult = Result<(), MakeMoveError>;
|
||||
type MakeMoveResult = Result<MoveRecord, MakeMoveError>;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub enum ValidateMove {
|
||||
|
@ -69,7 +69,16 @@ impl Position {
|
|||
///
|
||||
/// 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) -> MakeMoveResult {
|
||||
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(
|
||||
&mut self,
|
||||
ply: Move,
|
||||
validate: ValidateMove,
|
||||
) -> MakeMoveResult {
|
||||
self.validate_move(ply, validate)?;
|
||||
|
||||
if ply.is_quiet() {
|
||||
|
@ -85,14 +94,14 @@ impl Position {
|
|||
}
|
||||
|
||||
if let Some(wing) = ply.castle_wing() {
|
||||
return self.make_castle_move(wing);
|
||||
return self.make_castle_move(ply, wing);
|
||||
}
|
||||
|
||||
if ply.is_promotion() {
|
||||
return self.make_promotion_move(ply);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
pub fn unmake_move(&mut self, ply: &Move) -> Result<(), UnmakeMoveError> {
|
||||
|
@ -113,9 +122,11 @@ impl Position {
|
|||
|
||||
self.remove_piece(origin);
|
||||
|
||||
let record = self.register_move_record(ply);
|
||||
|
||||
self.advance_clocks(HalfMoveClock::Advance);
|
||||
|
||||
Ok(())
|
||||
Ok(record)
|
||||
}
|
||||
|
||||
fn make_double_push_move(&mut self, ply: Move) -> MakeMoveResult {
|
||||
|
@ -134,9 +145,11 @@ impl Position {
|
|||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let record = self.register_move_record(ply);
|
||||
|
||||
self.advance_clocks(HalfMoveClock::Advance);
|
||||
|
||||
Ok(())
|
||||
Ok(record)
|
||||
}
|
||||
|
||||
fn make_capture_move(&mut self, ply: Move) -> MakeMoveResult {
|
||||
|
@ -172,13 +185,15 @@ impl Position {
|
|||
self.place_piece(piece, target_square, PlacePieceStrategy::Replace)?;
|
||||
}
|
||||
|
||||
let record = self.register_move_record(ply);
|
||||
|
||||
self.advance_clocks(HalfMoveClock::Reset);
|
||||
|
||||
Ok(())
|
||||
Ok(record)
|
||||
}
|
||||
|
||||
fn make_castle_move(&mut self, wing: Wing) -> MakeMoveResult {
|
||||
self.active_color_can_castle(wing)?;
|
||||
fn make_castle_move(&mut self, ply: Move, wing: Wing) -> MakeMoveResult {
|
||||
self.board.active_color_can_castle(wing)?;
|
||||
|
||||
let active_color = self.board.active_color;
|
||||
let parameters = self.board.castling_parameters(wing);
|
||||
|
@ -191,9 +206,11 @@ impl Position {
|
|||
|
||||
self.board.castling_rights.revoke(active_color, wing);
|
||||
|
||||
let record = self.register_move_record(ply);
|
||||
|
||||
self.advance_clocks(HalfMoveClock::Advance);
|
||||
|
||||
Ok(())
|
||||
Ok(record)
|
||||
}
|
||||
|
||||
fn make_promotion_move(&mut self, ply: Move) -> MakeMoveResult {
|
||||
|
@ -219,9 +236,24 @@ impl Position {
|
|||
);
|
||||
}
|
||||
|
||||
let record = self.register_move_record(ply);
|
||||
|
||||
self.advance_clocks(HalfMoveClock::Reset);
|
||||
|
||||
Ok(())
|
||||
Ok(record)
|
||||
}
|
||||
|
||||
fn register_move_record(&mut self, ply: Move) -> MoveRecord {
|
||||
let record = MoveRecord {
|
||||
ply,
|
||||
en_passant_target: self.board.en_passant_target,
|
||||
castling_rights: self.board.castling_rights,
|
||||
half_move_clock: self.board.half_move_clock,
|
||||
};
|
||||
|
||||
self.moves.push(record.clone());
|
||||
|
||||
record
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,7 +262,7 @@ impl Position {
|
|||
self.get_piece(square).ok_or(MakeMoveError::NoPiece(square))
|
||||
}
|
||||
|
||||
fn place_piece_for_move(&mut self, piece: Piece, square: Square) -> MakeMoveResult {
|
||||
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));
|
||||
}
|
||||
|
@ -326,8 +358,10 @@ mod tests {
|
|||
use chessfriend_core::{piece, Color, Square};
|
||||
use chessfriend_moves::{Move, PromotionShape};
|
||||
|
||||
type TestResult = Result<(), MakeMoveError>;
|
||||
|
||||
#[test]
|
||||
fn make_quiet_move() -> MakeMoveResult {
|
||||
fn make_quiet_move() -> TestResult {
|
||||
let mut pos = test_position!(White Pawn on C2);
|
||||
|
||||
let ply = Move::quiet(Square::C2, Square::C3);
|
||||
|
@ -366,23 +400,26 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn make_capture_move() {
|
||||
fn make_capture_move() -> TestResult {
|
||||
let mut pos = test_position![
|
||||
White Bishop on C2,
|
||||
Black Rook on F5,
|
||||
];
|
||||
|
||||
let ply = Move::capture(Square::C2, Square::F5);
|
||||
assert_eq!(pos.make_move(ply, ValidateMove::Yes), Ok(()));
|
||||
pos.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[Color::White as usize][0], piece!(Black Rook));
|
||||
assert_eq!(pos.board.active_color, Color::Black);
|
||||
assert_eq!(pos.board.half_move_clock, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn make_en_passant_capture_move() -> MakeMoveResult {
|
||||
fn make_en_passant_capture_move() -> TestResult {
|
||||
let mut pos = test_position![
|
||||
Black Pawn on F4,
|
||||
White Pawn on E2
|
||||
|
@ -433,7 +470,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn make_promotion_move() -> MakeMoveResult {
|
||||
fn make_promotion_move() -> TestResult {
|
||||
let mut pos = test_position![
|
||||
Black Pawn on E7,
|
||||
White Pawn on F7,
|
||||
|
@ -451,7 +488,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn make_white_kingside_castle() -> MakeMoveResult {
|
||||
fn make_white_kingside_castle() -> TestResult {
|
||||
let mut pos = test_position![
|
||||
White Rook on H1,
|
||||
White King on E1,
|
||||
|
@ -474,7 +511,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn make_white_queenside_castle() -> MakeMoveResult {
|
||||
fn make_white_queenside_castle() -> TestResult {
|
||||
let mut pos = test_position![
|
||||
White King on E1,
|
||||
White Rook on A1,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use crate::move_record::MoveRecord;
|
||||
use chessfriend_bitboard::BitBoard;
|
||||
use chessfriend_board::{
|
||||
display::DiagramFormatter, fen::ToFenStr, Board, PlacePieceError, PlacePieceStrategy,
|
||||
|
@ -11,7 +12,8 @@ use std::{cell::OnceCell, fmt};
|
|||
#[derive(Clone, Debug, Eq)]
|
||||
pub struct Position {
|
||||
pub board: Board,
|
||||
pub(super) captures: [Vec<Piece>; Color::NUM],
|
||||
pub(crate) moves: Vec<MoveRecord>,
|
||||
pub(crate) captures: [Vec<Piece>; Color::NUM],
|
||||
}
|
||||
|
||||
impl Position {
|
||||
|
@ -181,6 +183,7 @@ impl Default for Position {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
board: Board::default(),
|
||||
moves: Vec::default(),
|
||||
captures: Default::default(),
|
||||
}
|
||||
}
|
||||
|
@ -202,8 +205,7 @@ impl fmt::Display for Position {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::{test_position, Position};
|
||||
use chessfriend_bitboard::bitboard;
|
||||
use chessfriend_core::{piece, Wing};
|
||||
use chessfriend_core::piece;
|
||||
|
||||
#[test]
|
||||
fn piece_on_square() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue