use core::fmt::Debug;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct PieceMove(u16);
impl Debug for PieceMove {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if *self == PieceMove::NULL {
write!(f, "NULL")
} else {
let to_file = (self.to_square() % 8 + b'a') as char; let to_rank = (self.to_square() / 8 + b'1') as char; let from_file = (self.from_square() % 8 + b'a') as char; let from_rank = (self.from_square() / 8 + b'1') as char; write!(
f,
"PieceMove({}{} -> {}{}{} - {:?})",
from_file,
from_rank,
to_file,
to_rank,
if self.is_capture() { " (Capture)" } else { "" },
self.promotion_type()
)
}
}
}
const FROM_SQUARE_MASK: u16 = 0x3F; const TO_SQUARE_MASK: u16 = 0x3F; const FROM_SQUARE_SHIFT: u8 = 0;
const TO_SQUARE_SHIFT: u8 = 6;
const PROMOTION_TYPE_MASK: u16 = 0x3; const PROMOTION_TYPE_SHIFT: u8 = 12;
const IS_PROMOTION_FLAG: u16 = 1 << 14; const IS_CAPTURE_FLAG: u16 = 1 << 15;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PromotionType {
Queen = 0,
Rook = 1,
Bishop = 2,
Knight = 3,
}
impl PieceMove {
pub const NULL: PieceMove = PieceMove(0);
pub fn new(from: u8, to: u8, is_capture: bool, promotion_type: Option<PromotionType>) -> Self {
debug_assert!(from < 64, "From square must be between 0 and 63");
debug_assert!(to < 64, "To square must be between 0 and 63");
debug_assert!(
from != to,
"From and to squares must be different for a valid move"
);
let mut move_value: u16 = 0;
move_value |= (from as u16) << FROM_SQUARE_SHIFT; move_value |= (to as u16) << TO_SQUARE_SHIFT;
if is_capture {
move_value |= IS_CAPTURE_FLAG; }
if let Some(promo_type) = promotion_type {
move_value |= IS_PROMOTION_FLAG; move_value |= ((promo_type as u16) & PROMOTION_TYPE_MASK) << PROMOTION_TYPE_SHIFT; }
PieceMove(move_value)
}
pub fn new_castling(king_from: u8, king_to: u8) -> Self {
PieceMove::new(king_from, king_to, false, None) }
pub fn new_two_square_advance(from: u8, to: u8) -> Self {
PieceMove::new(from, to, false, None)
}
pub fn new_en_passant(from: u8, to: u8) -> Self {
PieceMove::new(from, to, true, None) }
#[inline] pub fn from_square(&self) -> u8 {
((self.0 >> FROM_SQUARE_SHIFT) & FROM_SQUARE_MASK) as u8
}
#[inline]
pub fn to_square(&self) -> u8 {
((self.0 >> TO_SQUARE_SHIFT) & TO_SQUARE_MASK) as u8
}
#[inline]
pub fn is_capture(&self) -> bool {
(self.0 & IS_CAPTURE_FLAG) != 0
}
#[inline]
pub fn is_promotion(&self) -> bool {
(self.0 & IS_PROMOTION_FLAG) != 0
}
#[inline]
pub fn promotion_type(&self) -> Option<PromotionType> {
if self.is_promotion() {
match ((self.0 >> PROMOTION_TYPE_SHIFT) & PROMOTION_TYPE_MASK) as u8 {
0 => Some(PromotionType::Queen),
1 => Some(PromotionType::Rook),
2 => Some(PromotionType::Bishop),
3 => Some(PromotionType::Knight),
_ => unreachable!("Invalid promotion type bits"), }
} else {
None
}
}
pub fn is_two_square_advance(from: u8, to: u8, is_white_pawn_move: bool) -> bool {
if is_white_pawn_move {
(from / 8 == 1) && (to / 8 == 3) && (from % 8 == to % 8)
} else {
(from / 8 == 6) && (to / 8 == 4) && (from % 8 == to % 8)
}
}
pub fn is_kingside_castling(from: u8, to: u8, is_white_king_move: bool) -> bool {
if is_white_king_move {
from == 4 && to == 6 } else {
from == 60 && to == 62 }
}
pub fn is_queenside_castling(from: u8, to: u8, is_white_king_move: bool) -> bool {
if is_white_king_move {
from == 4 && to == 2 } else {
from == 60 && to == 58 }
}
#[inline]
pub fn is_en_passant(&self) -> bool {
if !self.is_capture() {
return false;
}
let from = self.from_square() as i8;
let to = self.to_square() as i8;
let diff = (from - to).abs();
diff == 7 || diff == 9
}
}
impl Default for PieceMove {
fn default() -> Self {
PieceMove::NULL
}
}