// Eryn Wells use crate::builder::Builder; use crate::defs::Kind; use chessfriend_core::{Rank, Shape, Square, Wing}; use std::fmt; /// A single player's move. In game theory parlance, this is a "ply". /// /// ## TODO /// /// - Rename this class `Ply`. /// #[derive(Clone, Copy, Eq, Hash, PartialEq)] pub struct Move(pub(crate) u16); impl Move { #[must_use] #[allow(clippy::missing_panics_doc)] pub fn origin_square(&self) -> Square { ((self.0 >> 4) & 0b111_111).try_into().unwrap() } #[must_use] #[allow(clippy::missing_panics_doc)] pub fn target_square(&self) -> Square { (self.0 >> 10).try_into().unwrap() } #[must_use] pub fn capture_square(&self) -> Option { if self.is_en_passant() { let target_square = self.target_square(); return Some(match target_square.rank() { Rank::THREE => Square::from_file_rank(target_square.file(), Rank::FOUR), Rank::SIX => Square::from_file_rank(target_square.file(), Rank::FIVE), _ => unreachable!(), }); } if self.is_capture() { return Some(self.target_square()); } None } #[must_use] pub fn is_quiet(&self) -> bool { self.flags() == Kind::Quiet as u16 } #[must_use] pub fn is_double_push(&self) -> bool { self.flags() == Kind::DoublePush as u16 } #[must_use] pub fn is_castle(&self) -> bool { (self.0 & 0b0010) != 0 } #[must_use] pub fn castle(&self) -> Option { match self.flags() { 0b0010 => Some(Wing::KingSide), 0b0011 => Some(Wing::QueenSide), _ => None, } } #[must_use] pub fn is_capture(&self) -> bool { (self.0 & Kind::Capture as u16) != 0 } #[must_use] pub fn is_en_passant(&self) -> bool { self.flags() == Kind::EnPassantCapture as u16 } #[must_use] pub fn is_promotion(&self) -> bool { (self.0 & Kind::Promotion as u16) != 0 } #[must_use] pub fn promotion(&self) -> Option { if !self.is_promotion() { return None; } Some(match self.special() { 0b00 => Shape::Knight, 0b01 => Shape::Bishop, 0b10 => Shape::Rook, 0b11 => Shape::Queen, _ => unreachable!(), }) } } impl Move { #[inline] fn flags(self) -> u16 { self.0 & 0b1111 } #[inline] fn special(self) -> u16 { self.0 & 0b11 } } impl Move { fn transfer_char(self) -> char { if self.is_capture() || self.is_en_passant() { 'x' } else { '-' } } } const KINGSIDE_CASTLE_STR: &str = "0-0"; const QUEENSIDE_CASTLE_STR: &str = "0-0-0"; impl fmt::Display for Move { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(castle) = self.castle() { return match castle { Wing::KingSide => write!(f, "{KINGSIDE_CASTLE_STR}"), Wing::QueenSide => write!(f, "{QUEENSIDE_CASTLE_STR}"), }; } let origin = self.origin_square(); let target = self.target_square(); let transfer_char = self.transfer_char(); write!(f, "{origin}{transfer_char}{target}")?; if let Some(promotion) = self.promotion() { write!(f, "={promotion}")?; } else if self.is_en_passant() { write!(f, " e.p.")?; } Ok(()) } } impl fmt::Debug for Move { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Move") .field(&format_args!("{:08b}", &self.0)) .finish() } }