128 lines
3 KiB
Rust
128 lines
3 KiB
Rust
|
// Eryn Wells <eryn@erynwells.me>
|
||
|
|
||
|
use crate::castle::Castle;
|
||
|
use chessfriend_core::{PlacedPiece, Shape, Square};
|
||
|
use std::fmt;
|
||
|
|
||
|
#[repr(u16)]
|
||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||
|
pub enum PromotableShape {
|
||
|
Knight = 0b00,
|
||
|
Bishop = 0b01,
|
||
|
Rook = 0b10,
|
||
|
Queen = 0b11,
|
||
|
}
|
||
|
|
||
|
#[repr(u16)]
|
||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||
|
enum Kind {
|
||
|
Quiet = 0b00,
|
||
|
DoublePush = 0b01,
|
||
|
Castle(Castle),
|
||
|
Capture(PlacedPiece) = 0b0100,
|
||
|
EnPassantCapture(PlacedPiece) = 0b0101,
|
||
|
Promotion(PromotableShape) = 0b1000,
|
||
|
CapturePromotion(PlacedPiece, PromotableShape) = 0b1100,
|
||
|
}
|
||
|
|
||
|
impl Kind {
|
||
|
fn bits(&self) -> u16 {
|
||
|
match self {
|
||
|
Self::Promotion(shape) => self.discriminant() | *shape as u16,
|
||
|
Self::CapturePromotion(_, shape) => self.discriminant() | *shape as u16,
|
||
|
Self::Castle(castle) => *castle as u16,
|
||
|
_ => self.discriminant(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Return the discriminant value. This implementation is copied from the Rust docs.
|
||
|
/// See https://doc.rust-lang.org/std/mem/fn.discriminant.html
|
||
|
fn discriminant(&self) -> u16 {
|
||
|
unsafe { *<*const _>::from(self).cast::<u16>() }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Default for Kind {
|
||
|
fn default() -> Self {
|
||
|
Self::Quiet
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// A single player's move. In chess parlance, this is a "ply".
|
||
|
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||
|
pub struct Move(pub(crate) u16);
|
||
|
|
||
|
impl Move {
|
||
|
pub fn from_square(&self) -> Square {
|
||
|
((self.0 >> 4) & 0b111111).try_into().unwrap()
|
||
|
}
|
||
|
|
||
|
pub fn to_square(&self) -> Square {
|
||
|
(self.0 >> 10).try_into().unwrap()
|
||
|
}
|
||
|
|
||
|
pub fn is_quiet(&self) -> bool {
|
||
|
self.flags() == Kind::Quiet.discriminant()
|
||
|
}
|
||
|
|
||
|
pub fn is_double_push(&self) -> bool {
|
||
|
self.flags() == Kind::DoublePush.discriminant()
|
||
|
}
|
||
|
|
||
|
pub fn is_castle(&self) -> bool {
|
||
|
self.castle().is_some()
|
||
|
}
|
||
|
|
||
|
pub fn castle(&self) -> Option<Castle> {
|
||
|
match self.flags() {
|
||
|
0b0010 => Some(Castle::KingSide),
|
||
|
0b0011 => Some(Castle::QueenSide),
|
||
|
_ => None,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn is_capture(&self) -> bool {
|
||
|
(self.0 & 0b0100) != 0
|
||
|
}
|
||
|
|
||
|
pub fn is_en_passant(&self) -> bool {
|
||
|
self.flags() == 0b0101
|
||
|
}
|
||
|
|
||
|
pub fn is_promotion(&self) -> bool {
|
||
|
(self.0 & 0b1000) != 0
|
||
|
}
|
||
|
|
||
|
pub fn promotion(&self) -> Option<Shape> {
|
||
|
if !self.is_promotion() {
|
||
|
return None;
|
||
|
}
|
||
|
|
||
|
Some(match self.special() {
|
||
|
0b00 => Shape::Knight,
|
||
|
0b01 => Shape::Bishop,
|
||
|
0b10 => Shape::Rook,
|
||
|
0b11 => Shape::Queen,
|
||
|
_ => unreachable!(),
|
||
|
})
|
||
|
}
|
||
|
|
||
|
#[inline]
|
||
|
fn flags(&self) -> u16 {
|
||
|
self.0 & 0b1111
|
||
|
}
|
||
|
|
||
|
#[inline]
|
||
|
fn special(&self) -> u16 {
|
||
|
self.0 & 0b11
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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()
|
||
|
}
|
||
|
}
|