use core::{
fmt,
fmt::{Display, Write as _},
};
use arrayvec::ArrayVec;
use crate::{CastlingSide, Role, Square};
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum Move {
Normal {
role: Role,
from: Square,
capture: Option<Role>,
to: Square,
promotion: Option<Role>,
},
EnPassant { from: Square, to: Square },
Castle { king: Square, rook: Square },
Put { role: Role, to: Square },
}
impl Move {
pub const fn role(self) -> Role {
match self {
Move::Normal { role, .. } | Move::Put { role, .. } => role,
Move::EnPassant { .. } => Role::Pawn,
Move::Castle { .. } => Role::King,
}
}
pub const fn from(self) -> Option<Square> {
match self {
Move::Normal { from, .. } | Move::EnPassant { from, .. } => Some(from),
Move::Castle { king, .. } => Some(king),
Move::Put { .. } => None,
}
}
pub const fn to(self) -> Square {
match self {
Move::Normal { to, .. } | Move::EnPassant { to, .. } | Move::Put { to, .. } => to,
Move::Castle { rook, .. } => rook,
}
}
pub const fn capture(self) -> Option<Role> {
match self {
Move::Normal { capture, .. } => capture,
Move::EnPassant { .. } => Some(Role::Pawn),
_ => None,
}
}
pub const fn is_capture(self) -> bool {
matches!(
self,
Move::Normal {
capture: Some(_),
..
} | Move::EnPassant { .. }
)
}
pub const fn is_conversion(self) -> bool {
matches!(
self,
Move::Normal {
capture: Some(_),
..
} | Move::Normal {
promotion: Some(_),
..
} | Move::EnPassant { .. }
| Move::Put { .. }
)
}
pub const fn is_en_passant(self) -> bool {
matches!(self, Move::EnPassant { .. })
}
pub const fn is_put(self) -> bool {
matches!(self, Move::Put { .. })
}
pub const fn is_zeroing(self) -> bool {
matches!(
self,
Move::Normal {
role: Role::Pawn,
..
} | Move::Normal {
capture: Some(_),
..
} | Move::EnPassant { .. }
| Move::Put {
role: Role::Pawn,
..
}
)
}
pub fn castling_side(self) -> Option<CastlingSide> {
match self {
Move::Castle { king, rook } => Some(CastlingSide::from_king_side(king < rook)),
_ => None,
}
}
pub const fn is_castle(self) -> bool {
matches!(self, Move::Castle { .. })
}
pub const fn promotion(self) -> Option<Role> {
match self {
Move::Normal { promotion, .. } => promotion,
_ => None,
}
}
pub const fn is_promotion(self) -> bool {
matches!(
self,
Move::Normal {
promotion: Some(_),
..
}
)
}
#[must_use]
pub const fn to_mirrored(self) -> Move {
match self {
Move::Normal {
role,
from,
capture,
to,
promotion,
} => Move::Normal {
role,
from: from.flip_vertical(),
capture,
to: to.flip_vertical(),
promotion,
},
Move::EnPassant { from, to } => Move::EnPassant {
from: from.flip_vertical(),
to: to.flip_vertical(),
},
Move::Castle { king, rook } => Move::Castle {
king: king.flip_vertical(),
rook: rook.flip_vertical(),
},
Move::Put { role, to } => Move::Put {
role,
to: to.flip_vertical(),
},
}
}
}
impl Display for Move {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Move::Normal {
role,
from,
capture,
to,
promotion,
} => {
if role != Role::Pawn {
f.write_char(role.upper_char())?;
}
write!(
f,
"{}{}{}",
from,
if capture.is_some() { 'x' } else { '-' },
to
)?;
if let Some(p) = promotion {
write!(f, "={}", p.upper_char())?;
}
Ok(())
}
Move::EnPassant { from, to, .. } => write!(f, "{from}x{to}"),
Move::Castle { king, rook } => f.write_str(if king < rook { "O-O" } else { "O-O-O" }),
Move::Put { role, to } => {
if role != Role::Pawn {
f.write_char(role.upper_char())?;
}
write!(f, "@{to}")
}
}
}
}
pub type MoveList = ArrayVec<
Move,
{
cfg_select! {
feature = "variant" => 270 + 6 * 6 * 5,
_ => 270, }
},
>;
#[cfg(test)]
mod tests {
use core::mem;
use super::*;
#[test]
fn test_move_size() {
assert!(mem::size_of::<Move>() <= 8);
}
}