use arrayvec::ArrayVec;
use crate::{file::File, role::PromotableRole, side::Side, square::Square};
const ORIG_SHIFT: u16 = 0;
const DEST_SHIFT: u16 = 6;
const PROMO_SHIFT: u16 = 12;
const FLAG_SHIFT: u16 = 14;
const SQ_MASK: u16 = 0b11_1111;
const PROMO_MASK: u16 = 0b11;
const FLAG_MASK: u16 = 0b11;
const FLAG_NORMAL: u16 = 0;
const FLAG_PROMOTION: u16 = 1;
const FLAG_ENPASSANT: u16 = 2;
const FLAG_CASTLE: u16 = 3;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Move(u16);
impl Move {
pub const fn normal(orig: Square, dest: Square) -> Self {
Self::pack(orig, dest, 0, FLAG_NORMAL)
}
pub const fn castle(king_from: Square, king_to: Square) -> Self {
Self::pack(king_from, king_to, 0, FLAG_CASTLE)
}
pub const fn enpassant(orig: Square, dest: Square) -> Self {
Self::pack(orig, dest, 0, FLAG_ENPASSANT)
}
pub const fn promotion(orig: Square, dest: Square, role: PromotableRole) -> Self {
Self::pack(orig, dest, promo_bits(role), FLAG_PROMOTION)
}
const fn pack(orig: Square, dest: Square, promo: u16, flag: u16) -> Self {
let bits = ((orig.0 as u16) << ORIG_SHIFT)
| ((dest.0 as u16) << DEST_SHIFT)
| (promo << PROMO_SHIFT)
| (flag << FLAG_SHIFT);
Self(bits)
}
#[inline]
pub const fn orig(self) -> Square {
Square::new(((self.0 >> ORIG_SHIFT) & SQ_MASK) as u8)
}
#[inline]
pub const fn dest(self) -> Square {
Square::new(((self.0 >> DEST_SHIFT) & SQ_MASK) as u8)
}
#[inline]
pub fn promo_role(self) -> Option<PromotableRole> {
if self.flag() == FLAG_PROMOTION {
Some(promo_from_bits((self.0 >> PROMO_SHIFT) & PROMO_MASK))
} else {
None
}
}
#[inline]
pub fn is_promotion(self) -> bool {
self.flag() == FLAG_PROMOTION
}
#[inline]
pub fn is_enpassant(self) -> bool {
self.flag() == FLAG_ENPASSANT
}
#[inline]
pub fn is_castle(self) -> bool {
self.flag() == FLAG_CASTLE
}
#[inline]
pub fn castle_side(self) -> Option<Side> {
if !self.is_castle() {
return None;
}
Some(if self.dest().file() == File::G {
Side::King
} else {
Side::Queen
})
}
#[inline]
fn flag(self) -> u16 {
(self.0 >> FLAG_SHIFT) & FLAG_MASK
}
}
const fn promo_bits(role: PromotableRole) -> u16 {
match role {
PromotableRole::Knight => 0,
PromotableRole::Bishop => 1,
PromotableRole::Rook => 2,
PromotableRole::Queen => 3,
}
}
const fn promo_from_bits(bits: u16) -> PromotableRole {
match bits & PROMO_MASK {
0 => PromotableRole::Knight,
1 => PromotableRole::Bishop,
2 => PromotableRole::Rook,
_ => PromotableRole::Queen,
}
}
pub type MoveList = ArrayVec<Move, 270>;