From 22098bbfffe1ba17048e7fbf94fbf3a3023e18e3 Mon Sep 17 00:00:00 2001 From: Paul-Nicolas Madelaine Date: Mon, 1 Dec 2025 23:39:31 +0100 Subject: [PATCH] make RawMove public --- src/lib.rs | 2 +- src/moves.rs | 10 +++- src/position.rs | 141 ++++++++++++++++++++++++++++-------------------- 3 files changed, 93 insertions(+), 60 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 55e5b43..58718ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,7 +93,7 @@ pub mod prelude { board::Piece, board::{ByCastlingSide, ByColor, ByFile, ByRank, ByRole, BySquare}, board::{CastlingSide, Color, File, Rank, Role, Square}, - position::Position, + position::{Position, RawMove}, san::San, setup::Setup, uci::UciMove, diff --git a/src/moves.rs b/src/moves.rs index c3c640d..aac0161 100644 --- a/src/moves.rs +++ b/src/moves.rs @@ -21,7 +21,7 @@ pub struct Move<'l> { impl<'l> Move<'l> { #[inline] - pub(crate) unsafe fn new_unchecked(position: &'l Position, raw: RawMove) -> Self { + pub(crate) unsafe fn new(position: &'l Position, raw: RawMove) -> Self { Self { position, raw } } @@ -83,10 +83,16 @@ impl<'l> Move<'l> { } } + /// Returns the raw data of the move. + #[inline] + pub fn raw(self) -> RawMove { + self.raw + } + /// Returns the UCI notation of the move. #[inline] pub fn to_uci(self) -> UciMove { - self.raw.uci() + self.raw.to_uci() } /// Returns the standard algebraic notation of the move. diff --git a/src/position.rs b/src/position.rs index 387a82b..df32349 100644 --- a/src/position.rs +++ b/src/position.rs @@ -293,6 +293,15 @@ impl Position { } } + /// Plays a move without checking for its legality. + /// + /// **Safety:** if `m` is not legal on `self` then this might cause undefined behavior, see + /// [`RawMove`] for details. + #[inline] + pub unsafe fn play_unchecked(&mut self, m: RawMove) { + unsafe { play_move(self, m) }; + } + pub(crate) fn move_from_uci<'l>(&'l self, uci: UciMove) -> Result, InvalidUciMove> { struct MoveGenImpl { role: Role, @@ -352,7 +361,7 @@ impl Position { .generate_moves(&mut moves) .break_value() .ok_or(InvalidUciMove::Illegal)?; - Ok(unsafe { Move::new_unchecked(position, raw) }) + Ok(unsafe { Move::new(position, raw) }) } let promotion = if role == Role::Pawn { promotion.unwrap_or(Role::Pawn) @@ -463,7 +472,7 @@ impl Position { None => Err(InvalidSan::Illegal), Some(raw) => match moves.found_other { true => Err(InvalidSan::Ambiguous), - false => Ok(unsafe { Move::new_unchecked(position, raw) }), + false => Ok(unsafe { Move::new(position, raw) }), }, } } @@ -486,12 +495,19 @@ impl Default for Position { } } +/// The raw data of a move. +/// +/// The [`Move<'l>`](crate::moves::Move) type should usually be preferred for playing moves, but +/// this type allows accessing the unsafe interface [`Position::play_unchecked`]. +/// +/// When playing this move ([`Position::play_unchecked`]) or converting to a legal move +/// ([`RawMove::to_move`]) it is the caller's responsability to ensure legality. The only w #[derive(Clone, Copy)] -pub(crate) struct RawMove { - pub kind: MoveType, - pub role: Role, - pub from: Square, - pub to: Square, +pub struct RawMove { + pub(crate) kind: MoveType, + pub(crate) role: Role, + pub(crate) from: Square, + pub(crate) to: Square, } #[derive(Clone, Copy, PartialEq, Eq)] @@ -510,16 +526,9 @@ pub(crate) enum MoveType { } impl RawMove { + /// Returns the type of piece that moves. #[inline] - pub fn from(&self) -> Square { - self.from - } - #[inline] - pub fn to(&self) -> Square { - self.to - } - #[inline] - pub fn role(&self) -> Role { + pub fn role(self) -> Role { match self.kind { MoveType::CastleShort | MoveType::CastleLong | MoveType::KingMove => Role::King, MoveType::PieceMove => self.role, @@ -531,15 +540,33 @@ impl RawMove { | MoveType::EnPassant => Role::Pawn, } } + /// Returns the origin square of the move. #[inline] - pub fn promotion(&self) -> Option { + pub fn from(self) -> Square { + self.from + } + /// Returns the target square of the move. + #[inline] + pub fn to(self) -> Square { + self.to + } + /// Returns the type of piece that the pawn is promoted to, if the move is a promotion. + #[inline] + pub fn promotion(self) -> Option { match self.kind { MoveType::PawnAdvancePromotion | MoveType::PawnAttackPromotion => Some(self.role), _ => None, } } + /// Converts the move to a [`Move<'l>`] bound to the given position, without checking for the + /// legality of the move. + /// **Safety:** #[inline] - pub fn uci(&self) -> UciMove { + pub unsafe fn to_move<'l>(self, position: &'l Position) -> Move<'l> { + unsafe { Move::new(position, self) } + } + #[inline] + pub fn to_uci(self) -> UciMove { UciMove { from: self.from(), to: self.to(), @@ -914,51 +941,51 @@ impl Position { ControlFlow::Continue(()) } +} - #[inline] - pub(crate) unsafe fn play_unchecked(&mut self, m: RawMove) { - let Self(setup) = self; +#[inline] +pub(crate) unsafe fn play_move(position: &mut Position, m: RawMove) { + let Position(setup) = position; - setup.en_passant = None; + setup.en_passant = None; - let RawMove { - kind, - from, - to, - role, - } = m; + let RawMove { + kind, + from, + to, + role, + } = m; - match kind { - MoveType::CastleShort => aux_play_castle(setup, CastlingSide::Short), - MoveType::CastleLong => aux_play_castle(setup, CastlingSide::Long), - MoveType::KingMove => aux_play_normal(setup, Role::King, from, to), - MoveType::PieceMove => aux_play_normal(setup, role, from, to), - MoveType::PawnAdvance => aux_play_pawn_advance(setup, Role::Pawn, from, to), - MoveType::PawnAttack => aux_play_normal(setup, Role::Pawn, from, to), - MoveType::PawnAdvancePromotion => aux_play_pawn_advance(setup, role, from, to), - MoveType::PawnAttackPromotion => aux_play_normal(setup, role, from, to), - MoveType::PawnDoubleAdvance => { - aux_play_pawn_advance(setup, Role::Pawn, from, to); - setup.en_passant = Some(Square::from_coords( - from.file(), - match setup.turn { - Color::White => Rank::Third, - Color::Black => Rank::Sixth, - }, - )); - } - MoveType::EnPassant => { - let direction = !setup.turn.forward(); - let x = (unsafe { to.trans_unchecked(direction) }).bitboard(); - let [p_b_q, _, _, black] = &mut setup.bitboards; - *p_b_q ^= x; - *black &= !x; - aux_play_pawn_advance(setup, Role::Pawn, from, to); - } + match kind { + MoveType::CastleShort => aux_play_castle(setup, CastlingSide::Short), + MoveType::CastleLong => aux_play_castle(setup, CastlingSide::Long), + MoveType::KingMove => aux_play_normal(setup, Role::King, from, to), + MoveType::PieceMove => aux_play_normal(setup, role, from, to), + MoveType::PawnAdvance => aux_play_pawn_advance(setup, Role::Pawn, from, to), + MoveType::PawnAttack => aux_play_normal(setup, Role::Pawn, from, to), + MoveType::PawnAdvancePromotion => aux_play_pawn_advance(setup, role, from, to), + MoveType::PawnAttackPromotion => aux_play_normal(setup, role, from, to), + MoveType::PawnDoubleAdvance => { + aux_play_pawn_advance(setup, Role::Pawn, from, to); + setup.en_passant = Some(Square::from_coords( + from.file(), + match setup.turn { + Color::White => Rank::Third, + Color::Black => Rank::Sixth, + }, + )); + } + MoveType::EnPassant => { + let direction = !setup.turn.forward(); + let x = (unsafe { to.trans_unchecked(direction) }).bitboard(); + let [p_b_q, _, _, black] = &mut setup.bitboards; + *p_b_q ^= x; + *black &= !x; + aux_play_pawn_advance(setup, Role::Pawn, from, to); } - - setup.turn = !setup.turn; } + + setup.turn = !setup.turn; } #[inline]