Implement a whole new move crate
This commit is contained in:
parent
0bedf2aa9f
commit
c55b7c4877
7 changed files with 512 additions and 148 deletions
|
@ -1,9 +1,35 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
use crate::{castle, Move};
|
use crate::{castle, defs::Kind, Move, PromotionShape};
|
||||||
use chessfriend_core::{PlacedPiece, Square};
|
use chessfriend_core::{Color, File, PlacedPiece, Rank, Square};
|
||||||
|
use std::result::Result as StdResult;
|
||||||
|
|
||||||
pub trait Style {}
|
pub type Result = std::result::Result<Move, Error>;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum Error {
|
||||||
|
MissingOriginSquare,
|
||||||
|
MissingTargetSquare,
|
||||||
|
MissingCaptureSquare,
|
||||||
|
InvalidEnPassantSquare,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Style {
|
||||||
|
fn origin_square(&self) -> Option<Square> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_square(&self) -> Option<Square> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_move_bits(&self) -> StdResult<u16, Error> {
|
||||||
|
let origin_square = self.origin_square().ok_or(Error::MissingOriginSquare)? as u16;
|
||||||
|
let target_square = self.target_square().ok_or(Error::MissingTargetSquare)? as u16;
|
||||||
|
|
||||||
|
Ok((origin_square & 0b111111) << 4 | (target_square & 0b111111) << 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub struct Builder<S: Style> {
|
pub struct Builder<S: Style> {
|
||||||
|
@ -19,67 +45,241 @@ pub struct Push {
|
||||||
to: Option<Square>,
|
to: Option<Square>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub struct DoublePush {
|
||||||
|
from: Square,
|
||||||
|
to: Square,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub struct Capture {
|
pub struct Capture {
|
||||||
push: Push,
|
push: Push,
|
||||||
capture: Option<Square>,
|
capture: Option<Square>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub struct EnPassantCapture {
|
||||||
|
push: Push,
|
||||||
|
capture: Option<Square>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Promotion<S> {
|
||||||
|
style: S,
|
||||||
|
promotion: PromotionShape,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Castle {
|
pub struct Castle {
|
||||||
castle: castle::Castle,
|
castle: castle::Castle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Style for Null {}
|
impl Style for Null {}
|
||||||
impl Style for Push {}
|
|
||||||
impl Style for Capture {}
|
impl Style for Push {
|
||||||
|
fn origin_square(&self) -> Option<Square> {
|
||||||
|
self.from
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_square(&self) -> Option<Square> {
|
||||||
|
self.to
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Style for Capture {
|
||||||
|
fn origin_square(&self) -> Option<Square> {
|
||||||
|
self.push.from
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_square(&self) -> Option<Square> {
|
||||||
|
self.push.to
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Style for Castle {}
|
impl Style for Castle {}
|
||||||
|
|
||||||
|
impl Style for DoublePush {
|
||||||
|
fn origin_square(&self) -> Option<Square> {
|
||||||
|
Some(self.from)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_square(&self) -> Option<Square> {
|
||||||
|
Some(self.to)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_move_bits(&self) -> StdResult<u16, Error> {
|
||||||
|
Ok(Kind::DoublePush as u16
|
||||||
|
| (self.from as u16 & 0b111111) << 4
|
||||||
|
| (self.to as u16 & 0b111111) << 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Style for EnPassantCapture {
|
||||||
|
fn origin_square(&self) -> Option<Square> {
|
||||||
|
self.push.from
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_square(&self) -> Option<Square> {
|
||||||
|
self.push.to
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_move_bits(&self) -> StdResult<u16, Error> {
|
||||||
|
let origin_square = self.origin_square().ok_or(Error::MissingOriginSquare)? as u16;
|
||||||
|
let target_square = self.target_square().ok_or(Error::MissingTargetSquare)? as u16;
|
||||||
|
|
||||||
|
Ok((origin_square & 0b111111) << 4 | (target_square & 0b111111) << 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Style for Promotion<Push> {
|
||||||
|
fn origin_square(&self) -> Option<Square> {
|
||||||
|
self.style.from
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_square(&self) -> Option<Square> {
|
||||||
|
self.style.to
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Style for Promotion<Capture> {
|
||||||
|
fn origin_square(&self) -> Option<Square> {
|
||||||
|
self.style.push.from
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_square(&self) -> Option<Square> {
|
||||||
|
self.style.push.to
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Promotion<Push> {
|
||||||
|
fn into_move_bits(&self) -> StdResult<u16, Error> {
|
||||||
|
let origin_square = self
|
||||||
|
.style
|
||||||
|
.origin_square()
|
||||||
|
.ok_or(Error::MissingOriginSquare)? as u16;
|
||||||
|
let target_square = self
|
||||||
|
.style
|
||||||
|
.target_square()
|
||||||
|
.ok_or(Error::MissingTargetSquare)? as u16;
|
||||||
|
|
||||||
|
Ok(Kind::Promotion as u16
|
||||||
|
| self.promotion as u16
|
||||||
|
| (origin_square & 0b111111) << 4
|
||||||
|
| (target_square & 0b111111) << 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Promotion<Capture> {
|
||||||
|
fn into_move_bits(&self) -> StdResult<u16, Error> {
|
||||||
|
let origin_square = self
|
||||||
|
.style
|
||||||
|
.origin_square()
|
||||||
|
.ok_or(Error::MissingOriginSquare)? as u16;
|
||||||
|
let target_square = self
|
||||||
|
.style
|
||||||
|
.target_square()
|
||||||
|
.ok_or(Error::MissingTargetSquare)? as u16;
|
||||||
|
|
||||||
|
Ok(Kind::CapturePromotion as u16
|
||||||
|
| self.promotion as u16
|
||||||
|
| (origin_square & 0b111111) << 4
|
||||||
|
| (target_square & 0b111111) << 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Builder<Null> {
|
impl Builder<Null> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { style: Null }
|
Self { style: Null }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn piece(piece: &PlacedPiece) -> Builder<Push> {
|
pub fn push(piece: &PlacedPiece, to: Square) -> Builder<Push> {
|
||||||
Builder {
|
Builder {
|
||||||
style: Push {
|
style: Push {
|
||||||
from: Some(piece.square()),
|
from: Some(piece.square()),
|
||||||
to: None,
|
to: Some(to),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn double_push(file: File, color: Color) -> Builder<DoublePush> {
|
||||||
|
let (from, to) = match color {
|
||||||
|
Color::White => (
|
||||||
|
Square::from_file_rank(file, Rank::TWO),
|
||||||
|
Square::from_file_rank(file, Rank::FOUR),
|
||||||
|
),
|
||||||
|
Color::Black => (
|
||||||
|
Square::from_file_rank(file, Rank::SEVEN),
|
||||||
|
Square::from_file_rank(file, Rank::FIVE),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
Builder {
|
||||||
|
style: DoublePush { from, to },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn castling(castle: castle::Castle) -> Builder<Castle> {
|
pub fn castling(castle: castle::Castle) -> Builder<Castle> {
|
||||||
Builder {
|
Builder {
|
||||||
style: Castle { castle },
|
style: Castle { castle },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn capturing_on(piece: &PlacedPiece, to: Square) -> Builder<Capture> {
|
||||||
|
Self::push(piece, to).capturing_on(to)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn capturing_piece(piece: &PlacedPiece, capturing: &PlacedPiece) -> Builder<Capture> {
|
||||||
|
Self::push(piece, capturing.square()).capturing_piece(&capturing)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from(self, square: Square) -> Builder<Push> {
|
||||||
|
Builder {
|
||||||
|
style: Push {
|
||||||
|
from: Some(square),
|
||||||
|
to: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(&self) -> Move {
|
pub fn build(&self) -> Move {
|
||||||
Move(0)
|
Move(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Builder<Push> {
|
impl Builder<Push> {
|
||||||
pub fn from(mut self, square: Square) -> Self {
|
pub fn from(&mut self, square: Square) -> &mut Self {
|
||||||
self.style.from = Some(square);
|
self.style.from = Some(square);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to(mut self, square: Square) -> Self {
|
pub fn to(&mut self, square: Square) -> &mut Self {
|
||||||
self.style.to = Some(square);
|
self.style.to = Some(square);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn capturing(self, square: Square) -> Builder<Capture> {
|
pub fn capturing_on(self, square: Square) -> Builder<Capture> {
|
||||||
|
let mut style = self.style;
|
||||||
|
style.to = Some(square);
|
||||||
|
|
||||||
Builder {
|
Builder {
|
||||||
style: Capture {
|
style: Capture {
|
||||||
|
push: style,
|
||||||
|
capture: Some(square),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn capturing_en_passant_on(self, square: Square) -> Builder<EnPassantCapture> {
|
||||||
|
let mut style = self.style;
|
||||||
|
style.to = Some(square);
|
||||||
|
|
||||||
|
Builder {
|
||||||
|
style: EnPassantCapture {
|
||||||
push: self.style,
|
push: self.style,
|
||||||
capture: Some(square),
|
capture: Some(square),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn capturing_piece(self, piece: PlacedPiece) -> Builder<Capture> {
|
pub fn capturing_piece(self, piece: &PlacedPiece) -> Builder<Capture> {
|
||||||
Builder {
|
Builder {
|
||||||
style: Capture {
|
style: Capture {
|
||||||
push: self.style,
|
push: self.style,
|
||||||
|
@ -87,19 +287,73 @@ impl Builder<Push> {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn promoting_to(self, shape: PromotionShape) -> Builder<Promotion<Push>> {
|
||||||
|
Builder {
|
||||||
|
style: Promotion {
|
||||||
|
style: self.style,
|
||||||
|
promotion: shape,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(&self) -> Result {
|
||||||
|
Ok(Move(Kind::Quiet as u16 | self.style.into_move_bits()?))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Builder<Castle> {
|
impl Builder<Castle> {
|
||||||
|
fn bits(&self) -> u16 {
|
||||||
|
let bits = match self.style.castle {
|
||||||
|
castle::Castle::KingSide => Kind::KingSideCastle,
|
||||||
|
castle::Castle::QueenSide => Kind::QueenSideCastle,
|
||||||
|
};
|
||||||
|
|
||||||
|
bits as u16
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(&self) -> Move {
|
pub fn build(&self) -> Move {
|
||||||
Move(self.style.into_bits())
|
Move(self.bits())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Castle {
|
impl Builder<Capture> {
|
||||||
fn into_bits(&self) -> u16 {
|
pub fn promoting_to(self, shape: PromotionShape) -> Builder<Promotion<Capture>> {
|
||||||
match self.castle {
|
Builder {
|
||||||
castle::Castle::KingSide => 0b10,
|
style: Promotion {
|
||||||
castle::Castle::QueenSide => 0b11,
|
style: self.style,
|
||||||
|
promotion: shape,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build(&self) -> Result {
|
||||||
|
Ok(Move(Kind::Capture as u16 | self.style.into_move_bits()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Builder<DoublePush> {
|
||||||
|
pub fn build(&self) -> Result {
|
||||||
|
Ok(Move(Kind::DoublePush as u16 | self.style.into_move_bits()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Builder<EnPassantCapture> {
|
||||||
|
pub unsafe fn build_unchecked(&self) -> Result {
|
||||||
|
Ok(Move(
|
||||||
|
Kind::EnPassantCapture as u16 | self.style.into_move_bits()?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Builder<Promotion<Push>> {
|
||||||
|
pub fn build(&self) -> Result {
|
||||||
|
Ok(Move(self.style.into_move_bits()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Builder<Promotion<Capture>> {
|
||||||
|
pub fn build(&self) -> Result {
|
||||||
|
Ok(Move(self.style.into_move_bits()?))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,26 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
use chessfriend_bitboard::BitBoard;
|
use chessfriend_bitboard::BitBoard;
|
||||||
use chessfriend_core::{Color, Square};
|
use chessfriend_core::Square;
|
||||||
|
|
||||||
#[repr(u16)]
|
#[repr(u8)]
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Castle {
|
pub enum Castle {
|
||||||
KingSide = 0b10,
|
KingSide = 0,
|
||||||
QueenSide = 0b11,
|
QueenSide = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct CastlingParameters {
|
pub(crate) struct CastlingParameters {
|
||||||
|
/// Origin squares of the king and rook.
|
||||||
|
origin_squares: Squares,
|
||||||
|
|
||||||
|
/// Target or destination squares for the king and rook.
|
||||||
|
target_squares: Squares,
|
||||||
|
|
||||||
|
/// The set of squares that must be clear of any pieces in order to perform this castle.
|
||||||
clear_squares: BitBoard,
|
clear_squares: BitBoard,
|
||||||
|
|
||||||
|
/// The set of squares that must not be attacked in order to perform this castle.
|
||||||
check_squares: BitBoard,
|
check_squares: BitBoard,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,87 +33,59 @@ pub(crate) struct Squares {
|
||||||
impl Castle {
|
impl Castle {
|
||||||
pub const ALL: [Castle; 2] = [Castle::KingSide, Castle::QueenSide];
|
pub const ALL: [Castle; 2] = [Castle::KingSide, Castle::QueenSide];
|
||||||
|
|
||||||
const STARTING_SQUARES: [[Squares; 2]; 2] = [
|
/// Parameters for each castling move, organized by color and board-side.
|
||||||
|
const PARAMETERS: [[CastlingParameters; 2]; 2] = [
|
||||||
[
|
[
|
||||||
Squares {
|
CastlingParameters {
|
||||||
king: Square::E1,
|
origin_squares: Squares {
|
||||||
rook: Square::H1,
|
king: Square::E1,
|
||||||
},
|
rook: Square::H1,
|
||||||
Squares {
|
},
|
||||||
king: Square::E1,
|
target_squares: Squares {
|
||||||
rook: Square::A1,
|
king: Square::G1,
|
||||||
},
|
rook: Square::F1,
|
||||||
],
|
},
|
||||||
[
|
|
||||||
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),
|
clear_squares: BitBoard::new(0b01100000),
|
||||||
check_squares: BitBoard::new(0b01110000),
|
check_squares: BitBoard::new(0b01110000),
|
||||||
},
|
},
|
||||||
Castle::QueenSide => CastlingParameters {
|
CastlingParameters {
|
||||||
|
origin_squares: Squares {
|
||||||
|
king: Square::E1,
|
||||||
|
rook: Square::A1,
|
||||||
|
},
|
||||||
|
target_squares: Squares {
|
||||||
|
king: Square::C1,
|
||||||
|
rook: Square::D1,
|
||||||
|
},
|
||||||
clear_squares: BitBoard::new(0b00001110),
|
clear_squares: BitBoard::new(0b00001110),
|
||||||
check_squares: BitBoard::new(0b00011100),
|
check_squares: BitBoard::new(0b00011100),
|
||||||
},
|
},
|
||||||
}
|
],
|
||||||
}
|
[
|
||||||
}
|
CastlingParameters {
|
||||||
|
origin_squares: Squares {
|
||||||
impl CastlingParameters {
|
king: Square::E8,
|
||||||
pub fn clear_squares(&self) -> &BitBoard {
|
rook: Square::H8,
|
||||||
&self.clear_squares
|
},
|
||||||
}
|
target_squares: Squares {
|
||||||
|
king: Square::G8,
|
||||||
pub fn check_squares(&self) -> &BitBoard {
|
rook: Square::F8,
|
||||||
&self.check_squares
|
},
|
||||||
}
|
clear_squares: BitBoard::new(0b01100000 << 8 * 7),
|
||||||
|
check_squares: BitBoard::new(0b01110000 << 8 * 7),
|
||||||
|
},
|
||||||
|
CastlingParameters {
|
||||||
|
origin_squares: Squares {
|
||||||
|
king: Square::E8,
|
||||||
|
rook: Square::A8,
|
||||||
|
},
|
||||||
|
target_squares: Squares {
|
||||||
|
king: Square::C8,
|
||||||
|
rook: Square::D8,
|
||||||
|
},
|
||||||
|
clear_squares: BitBoard::new(0b00001110 << 8 * 7),
|
||||||
|
check_squares: BitBoard::new(0b00011100 << 8 * 7),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
34
moves/src/defs.rs
Normal file
34
moves/src/defs.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
|
use chessfriend_core::Shape;
|
||||||
|
|
||||||
|
pub(crate) enum Kind {
|
||||||
|
Quiet = 0b00,
|
||||||
|
DoublePush = 0b01,
|
||||||
|
KingSideCastle = 0b10,
|
||||||
|
QueenSideCastle = 0b11,
|
||||||
|
Capture = 0b0100,
|
||||||
|
EnPassantCapture = 0b0101,
|
||||||
|
Promotion = 0b1000,
|
||||||
|
CapturePromotion = 0b1100,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u16)]
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum PromotionShape {
|
||||||
|
Knight = 0b00,
|
||||||
|
Bishop = 0b01,
|
||||||
|
Rook = 0b10,
|
||||||
|
Queen = 0b11,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PromotionShape> for Shape {
|
||||||
|
fn from(value: PromotionShape) -> Self {
|
||||||
|
match value {
|
||||||
|
PromotionShape::Knight => Shape::Knight,
|
||||||
|
PromotionShape::Bishop => Shape::Bishop,
|
||||||
|
PromotionShape::Rook => Shape::Rook,
|
||||||
|
PromotionShape::Queen => Shape::Queen,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,10 @@
|
||||||
|
|
||||||
mod builder;
|
mod builder;
|
||||||
mod castle;
|
mod castle;
|
||||||
|
mod defs;
|
||||||
mod moves;
|
mod moves;
|
||||||
|
|
||||||
pub use builder::Builder;
|
pub use builder::{Builder, Error as BuilderError};
|
||||||
|
pub use castle::Castle;
|
||||||
|
pub use defs::PromotionShape;
|
||||||
pub use moves::Move;
|
pub use moves::Move;
|
||||||
|
|
|
@ -1,72 +1,45 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
use crate::castle::Castle;
|
use crate::{castle::Castle, defs::Kind};
|
||||||
use chessfriend_core::{PlacedPiece, Shape, Square};
|
use chessfriend_core::{Rank, Shape, Square};
|
||||||
use std::fmt;
|
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".
|
/// A single player's move. In chess parlance, this is a "ply".
|
||||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||||
pub struct Move(pub(crate) u16);
|
pub struct Move(pub(crate) u16);
|
||||||
|
|
||||||
impl Move {
|
impl Move {
|
||||||
pub fn from_square(&self) -> Square {
|
pub fn origin_square(&self) -> Square {
|
||||||
((self.0 >> 4) & 0b111111).try_into().unwrap()
|
((self.0 >> 4) & 0b111111).try_into().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_square(&self) -> Square {
|
pub fn target_square(&self) -> Square {
|
||||||
(self.0 >> 10).try_into().unwrap()
|
(self.0 >> 10).try_into().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn capture_square(&self) -> Option<Square> {
|
||||||
|
if self.is_capture() {
|
||||||
|
return Some(self.target_square());
|
||||||
|
}
|
||||||
|
|
||||||
|
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!(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_quiet(&self) -> bool {
|
pub fn is_quiet(&self) -> bool {
|
||||||
self.flags() == Kind::Quiet.discriminant()
|
self.flags() == Kind::Quiet as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_double_push(&self) -> bool {
|
pub fn is_double_push(&self) -> bool {
|
||||||
self.flags() == Kind::DoublePush.discriminant()
|
self.flags() == Kind::DoublePush as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_castle(&self) -> bool {
|
pub fn is_castle(&self) -> bool {
|
||||||
|
|
104
moves/tests/flags.rs
Normal file
104
moves/tests/flags.rs
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
|
use chessfriend_core::{piece, Color, File, Shape, Square};
|
||||||
|
use chessfriend_moves::{Builder, BuilderError, Castle, PromotionShape};
|
||||||
|
|
||||||
|
macro_rules! assert_flag {
|
||||||
|
($move:expr, $left:expr, $right:expr, $desc:expr) => {
|
||||||
|
assert_eq!($left, $right, "{:?} -> {}", $move, stringify!($desc))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! assert_flags {
|
||||||
|
($move:expr, $quiet:expr, $double_push:expr, $en_passant:expr, $capture:expr, $castle:expr, $promotion:expr) => {
|
||||||
|
assert_flag!($move, $move.is_quiet(), $quiet, "is_quiet");
|
||||||
|
assert_flag!(
|
||||||
|
$move,
|
||||||
|
$move.is_double_push(),
|
||||||
|
$double_push,
|
||||||
|
"is_double_push"
|
||||||
|
);
|
||||||
|
assert_flag!($move, $move.is_en_passant(), $en_passant, "is_en_passant");
|
||||||
|
assert_flag!($move, $move.is_capture(), $capture, "is_capture");
|
||||||
|
assert_flag!($move, $move.is_castle(), $castle, "is_castle");
|
||||||
|
assert_flag!($move, $move.is_promotion(), $promotion, "is_promotion");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn move_flags_quiet() -> Result<(), BuilderError> {
|
||||||
|
let mv = Builder::push(&piece!(White Pawn on A4), Square::A5).build()?;
|
||||||
|
assert_flags!(mv, true, false, false, false, false, false);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn move_flags_double_push() -> Result<(), BuilderError> {
|
||||||
|
let mv = Builder::double_push(File::C, Color::White).build()?;
|
||||||
|
assert_flags!(mv, false, true, false, false, false, false);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn move_flags_capture() -> Result<(), BuilderError> {
|
||||||
|
let mv = Builder::new()
|
||||||
|
.from(Square::A4)
|
||||||
|
.capturing_on(Square::B5)
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
assert_flags!(mv, false, false, false, true, false, false);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn move_flags_en_passant_capture() -> Result<(), BuilderError> {
|
||||||
|
let mv = unsafe {
|
||||||
|
Builder::new()
|
||||||
|
.from(Square::A5)
|
||||||
|
.capturing_en_passant_on(Square::B4)
|
||||||
|
.build_unchecked()?
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_flags!(mv, false, false, true, true, false, false);
|
||||||
|
assert_eq!(mv.origin_square(), Square::A5);
|
||||||
|
assert_eq!(mv.target_square(), Square::B4);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn move_flags_promotion() -> Result<(), BuilderError> {
|
||||||
|
let mv = Builder::push(&piece!(White Pawn on H7), Square::H8)
|
||||||
|
.promoting_to(PromotionShape::Queen)
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
assert_flags!(mv, false, false, false, false, false, true);
|
||||||
|
assert_eq!(mv.promotion(), Some(Shape::Queen));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn move_flags_capture_promotion() -> Result<(), BuilderError> {
|
||||||
|
let mv = Builder::push(&piece!(White Pawn on H7), Square::H8)
|
||||||
|
.capturing_piece(&piece!(Black Knight on G8))
|
||||||
|
.promoting_to(PromotionShape::Queen)
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
assert_flags!(mv, false, false, false, true, false, true);
|
||||||
|
assert_eq!(mv.promotion(), Some(Shape::Queen));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn move_flags_castle() -> Result<(), BuilderError> {
|
||||||
|
let mv = Builder::castling(Castle::KingSide).build();
|
||||||
|
|
||||||
|
assert_flags!(mv, false, false, false, false, true, false);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
15
moves/tests/pushes.rs
Normal file
15
moves/tests/pushes.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
|
use chessfriend_core::{piece, Square};
|
||||||
|
use chessfriend_moves::{Builder, BuilderError};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pawn_push() -> Result<(), BuilderError> {
|
||||||
|
let mv = Builder::push(&piece!(White Pawn on A3), Square::A4).build()?;
|
||||||
|
|
||||||
|
assert!(mv.is_quiet());
|
||||||
|
assert_eq!(mv.origin_square(), Square::A3);
|
||||||
|
assert_eq!(mv.target_square(), Square::A4);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue