From f69c7d4c9626535c38a414e72817eb652da7fb32 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 3 Feb 2024 15:17:02 -0800 Subject: [PATCH] Move a bunch of stuff from the position::move module over to a new chessfriend_moves crate --- Cargo.lock | 3 +- Cargo.toml | 1 + moves/Cargo.toml | 10 ++++ moves/src/castle.rs | 110 ++++++++++++++++++++++++++++++++++++++ moves/src/lib.rs | 8 +++ moves/src/moves.rs | 127 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 257 insertions(+), 2 deletions(-) create mode 100644 moves/Cargo.toml create mode 100644 moves/src/castle.rs create mode 100644 moves/src/lib.rs create mode 100644 moves/src/moves.rs diff --git a/Cargo.lock b/Cargo.lock index 6e580bf..7251e3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,12 +74,11 @@ name = "chessfriend_core" version = "0.1.0" [[package]] -name = "chessfriend_move_generator" +name = "chessfriend_moves" version = "0.1.0" dependencies = [ "chessfriend_bitboard", "chessfriend_core", - "chessfriend_position", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b490c8e..490ec43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "bitboard", "core", "explorer", + "moves", "position", ] resolver = "2" diff --git a/moves/Cargo.toml b/moves/Cargo.toml new file mode 100644 index 0000000..3c9e6cf --- /dev/null +++ b/moves/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "chessfriend_moves" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +chessfriend_core = { path = "../core" } +chessfriend_bitboard = { path = "../bitboard" } diff --git a/moves/src/castle.rs b/moves/src/castle.rs new file mode 100644 index 0000000..a8e95d1 --- /dev/null +++ b/moves/src/castle.rs @@ -0,0 +1,110 @@ +// Eryn Wells + +use chessfriend_bitboard::BitBoard; +use chessfriend_core::{Color, Square}; + +#[repr(u16)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Castle { + KingSide = 0b10, + QueenSide = 0b11, +} + +pub(crate) struct CastlingParameters { + clear_squares: BitBoard, + check_squares: BitBoard, +} + +#[derive(Debug)] +pub(crate) struct Squares { + pub king: Square, + pub rook: Square, +} + +impl Castle { + pub const ALL: [Castle; 2] = [Castle::KingSide, Castle::QueenSide]; + + const STARTING_SQUARES: [[Squares; 2]; 2] = [ + [ + Squares { + king: Square::E1, + rook: Square::H1, + }, + Squares { + king: Square::E1, + rook: Square::A1, + }, + ], + [ + Squares { + king: Square::E8, + rook: Square::H8, + }, + Squares { + king: Square::E8, + rook: Square::A8, + }, + ], + ]; + + const TARGET_SQUARES: [[Squares; 2]; 2] = [ + [ + Squares { + king: Square::G1, + rook: Square::F1, + }, + Squares { + king: Square::C1, + rook: Square::D1, + }, + ], + [ + Squares { + king: Square::G8, + rook: Square::F8, + }, + Squares { + king: Square::C8, + rook: Square::D8, + }, + ], + ]; + + pub(crate) fn starting_squares(&self, color: Color) -> &'static Squares { + &Castle::STARTING_SQUARES[color as usize][self.into_index()] + } + + pub(crate) fn target_squares(&self, color: Color) -> &'static Squares { + &Castle::TARGET_SQUARES[color as usize][self.into_index()] + } + + pub(crate) fn into_index(&self) -> usize { + match self { + Castle::KingSide => 0, + Castle::QueenSide => 1, + } + } + + pub(crate) fn parameters(&self) -> CastlingParameters { + match self { + Castle::KingSide => CastlingParameters { + clear_squares: BitBoard::new(0b01100000), + check_squares: BitBoard::new(0b01110000), + }, + Castle::QueenSide => CastlingParameters { + clear_squares: BitBoard::new(0b00001110), + check_squares: BitBoard::new(0b00011100), + }, + } + } +} + +impl CastlingParameters { + pub fn clear_squares(&self) -> &BitBoard { + &self.clear_squares + } + + pub fn check_squares(&self) -> &BitBoard { + &self.check_squares + } +} diff --git a/moves/src/lib.rs b/moves/src/lib.rs new file mode 100644 index 0000000..564c897 --- /dev/null +++ b/moves/src/lib.rs @@ -0,0 +1,8 @@ +// Eryn Wells + +mod builder; +mod castle; +mod moves; + +pub use builder::Builder; +pub use moves::Move; diff --git a/moves/src/moves.rs b/moves/src/moves.rs new file mode 100644 index 0000000..8d33b04 --- /dev/null +++ b/moves/src/moves.rs @@ -0,0 +1,127 @@ +// Eryn Wells + +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::() } + } +} + +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 { + 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 { + 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() + } +}