WIP
This commit is contained in:
parent
d5cdf273c8
commit
091cc99cb3
42 changed files with 805 additions and 1662 deletions
|
@ -16,6 +16,12 @@ pub enum Error {
|
|||
InvalidEnPassantSquare,
|
||||
}
|
||||
|
||||
const MASK: u16 = 0b111_111;
|
||||
|
||||
fn build_move_bits(origin_square: Square, target_square: Square) -> u16 {
|
||||
(origin_square as u16 & MASK) << 4 | (target_square as u16 & MASK) << 10
|
||||
}
|
||||
|
||||
pub trait Style {
|
||||
fn origin_square(&self) -> Option<Square> {
|
||||
None
|
||||
|
@ -25,22 +31,18 @@ pub trait Style {
|
|||
None
|
||||
}
|
||||
|
||||
fn into_move_bits(&self) -> EncodedMoveResult {
|
||||
fn move_bits(&self) -> EncodedMoveResult {
|
||||
let origin_square = self.origin_square().ok_or(Error::MissingOriginSquare)?;
|
||||
let target_square = self.target_square().ok_or(Error::MissingTargetSquare)?;
|
||||
|
||||
Ok(self._build_move_bits(origin_square, target_square))
|
||||
Ok(build_move_bits(origin_square, target_square))
|
||||
}
|
||||
|
||||
unsafe fn into_move_bits_unchecked(&self) -> u16 {
|
||||
unsafe fn move_bits_unchecked(&self) -> u16 {
|
||||
let origin_square = self.origin_square().unwrap();
|
||||
let target_square = self.target_square().unwrap();
|
||||
|
||||
self._build_move_bits(origin_square, target_square)
|
||||
}
|
||||
|
||||
fn _build_move_bits(&self, origin_square: Square, target_square: Square) -> u16 {
|
||||
(origin_square as u16 & 0b111111) << 4 | (target_square as u16 & 0b111111) << 10
|
||||
build_move_bits(origin_square, target_square)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,12 +90,6 @@ pub struct Castle {
|
|||
castle: castle::Castle,
|
||||
}
|
||||
|
||||
impl EnPassantCapture {
|
||||
fn _build_move_bits(&self, origin_square: Square, target_square: Square) -> u16 {
|
||||
(origin_square as u16 & 0b111111) << 4 | (target_square as u16 & 0b111111) << 10
|
||||
}
|
||||
}
|
||||
|
||||
impl Style for Null {}
|
||||
|
||||
impl Style for Push {
|
||||
|
@ -137,10 +133,12 @@ impl Style for DoublePush {
|
|||
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)
|
||||
fn move_bits(&self) -> StdResult<u16, Error> {
|
||||
Ok(
|
||||
Kind::DoublePush as u16
|
||||
| (self.from as u16 & MASK) << 4
|
||||
| (self.to as u16 & MASK) << 10,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,11 +151,11 @@ impl Style for EnPassantCapture {
|
|||
self.push.to
|
||||
}
|
||||
|
||||
fn into_move_bits(&self) -> EncodedMoveResult {
|
||||
fn move_bits(&self) -> EncodedMoveResult {
|
||||
let origin_square = self.origin_square().ok_or(Error::MissingOriginSquare)?;
|
||||
let target_square = self.target_square().ok_or(Error::MissingTargetSquare)?;
|
||||
|
||||
Ok(self._build_move_bits(origin_square, target_square))
|
||||
Ok(build_move_bits(origin_square, target_square))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,7 +180,7 @@ impl Style for Promotion<Capture> {
|
|||
}
|
||||
|
||||
impl Promotion<Push> {
|
||||
fn into_move_bits(&self) -> StdResult<u16, Error> {
|
||||
fn move_bits(&self) -> StdResult<u16, Error> {
|
||||
let origin_square = self
|
||||
.style
|
||||
.origin_square()
|
||||
|
@ -194,13 +192,13 @@ impl Promotion<Push> {
|
|||
|
||||
Ok(Kind::Promotion as u16
|
||||
| self.promotion as u16
|
||||
| (origin_square & 0b111111) << 4
|
||||
| (target_square & 0b111111) << 10)
|
||||
| (origin_square & MASK << 4)
|
||||
| (target_square & MASK << 10))
|
||||
}
|
||||
}
|
||||
|
||||
impl Promotion<Capture> {
|
||||
fn into_move_bits(&self) -> StdResult<u16, Error> {
|
||||
fn move_bits(&self) -> StdResult<u16, Error> {
|
||||
let origin_square = self
|
||||
.style
|
||||
.origin_square()
|
||||
|
@ -212,25 +210,28 @@ impl Promotion<Capture> {
|
|||
|
||||
Ok(Kind::CapturePromotion as u16
|
||||
| self.promotion as u16
|
||||
| (origin_square & 0b111111) << 4
|
||||
| (target_square & 0b111111) << 10)
|
||||
| (origin_square & MASK) << 4
|
||||
| (target_square & MASK) << 10)
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder<Null> {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self { style: Null }
|
||||
Self::default()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn push(piece: &PlacedPiece) -> Builder<Push> {
|
||||
Builder {
|
||||
style: Push {
|
||||
from: Some(piece.square()),
|
||||
from: Some(piece.square),
|
||||
to: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn double_push(file: File, color: Color) -> Builder<DoublePush> {
|
||||
let (from, to) = match color {
|
||||
Color::White => (
|
||||
|
@ -248,16 +249,19 @@ impl Builder<Null> {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn castling(color: Color, castle: castle::Castle) -> Builder<Castle> {
|
||||
Builder {
|
||||
style: Castle { color, castle },
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn capturing_piece(piece: &PlacedPiece, capturing: &PlacedPiece) -> Builder<Capture> {
|
||||
Self::push(piece).capturing_piece(&capturing)
|
||||
Self::push(piece).capturing_piece(capturing)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn from(&self, square: Square) -> Builder<Push> {
|
||||
Builder {
|
||||
style: Push {
|
||||
|
@ -267,11 +271,18 @@ impl Builder<Null> {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn build(&self) -> Move {
|
||||
Move(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Builder<Null> {
|
||||
fn default() -> Self {
|
||||
Self { style: Null }
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder<Push> {
|
||||
pub fn from(&mut self, square: Square) -> &mut Self {
|
||||
self.style.from = Some(square);
|
||||
|
@ -283,6 +294,7 @@ impl Builder<Push> {
|
|||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn capturing_on(&self, square: Square) -> Builder<Capture> {
|
||||
let mut style = self.style.clone();
|
||||
style.to = Some(square);
|
||||
|
@ -295,6 +307,7 @@ impl Builder<Push> {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn capturing_en_passant_on(&self, target_square: Square) -> Builder<EnPassantCapture> {
|
||||
match EnPassant::from_target_square(target_square) {
|
||||
Some(en_passant) => {
|
||||
|
@ -312,15 +325,17 @@ impl Builder<Push> {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn capturing_piece(&self, piece: &PlacedPiece) -> Builder<Capture> {
|
||||
Builder {
|
||||
style: Capture {
|
||||
push: self.style.clone(),
|
||||
capture: Some(piece.square()),
|
||||
capture: Some(piece.square),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn promoting_to(&self, shape: PromotionShape) -> Builder<Promotion<Push>> {
|
||||
Builder {
|
||||
style: Promotion {
|
||||
|
@ -331,7 +346,7 @@ impl Builder<Push> {
|
|||
}
|
||||
|
||||
pub fn build(&self) -> Result {
|
||||
Ok(Move(Kind::Quiet as u16 | self.style.into_move_bits()?))
|
||||
Ok(Move(Kind::Quiet as u16 | self.style.move_bits()?))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -346,11 +361,12 @@ impl Builder<Castle> {
|
|||
}
|
||||
|
||||
pub fn build(&self) -> Result {
|
||||
Ok(Move(self.bits() | self.style.into_move_bits()?))
|
||||
Ok(Move(self.bits() | self.style.move_bits()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder<Capture> {
|
||||
#[must_use]
|
||||
pub fn promoting_to(self, shape: PromotionShape) -> Builder<Promotion<Capture>> {
|
||||
Builder {
|
||||
style: Promotion {
|
||||
|
@ -361,36 +377,42 @@ impl Builder<Capture> {
|
|||
}
|
||||
|
||||
pub fn build(&self) -> Result {
|
||||
Ok(Move(Kind::Capture as u16 | self.style.into_move_bits()?))
|
||||
Ok(Move(Kind::Capture as u16 | self.style.move_bits()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder<DoublePush> {
|
||||
pub fn build(&self) -> Result {
|
||||
Ok(Move(Kind::DoublePush as u16 | self.style.into_move_bits()?))
|
||||
Ok(Move(Kind::DoublePush as u16 | self.style.move_bits()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder<EnPassantCapture> {
|
||||
/// Builds an en passant move.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// This method builds without doing error checking.
|
||||
#[must_use]
|
||||
pub unsafe fn build_unchecked(&self) -> Move {
|
||||
Move(Kind::EnPassantCapture as u16 | self.style.into_move_bits_unchecked())
|
||||
Move(Kind::EnPassantCapture as u16 | self.style.move_bits_unchecked())
|
||||
}
|
||||
|
||||
pub fn build(&self) -> Result {
|
||||
Ok(Move(
|
||||
Kind::EnPassantCapture as u16 | self.style.into_move_bits()?,
|
||||
Kind::EnPassantCapture as u16 | self.style.move_bits()?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder<Promotion<Push>> {
|
||||
pub fn build(&self) -> Result {
|
||||
Ok(Move(self.style.into_move_bits()?))
|
||||
Ok(Move(self.style.move_bits()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder<Promotion<Capture>> {
|
||||
pub fn build(&self) -> Result {
|
||||
Ok(Move(self.style.into_move_bits()?))
|
||||
Ok(Move(self.style.move_bits()?))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use chessfriend_core::Shape;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum Kind {
|
||||
Quiet = 0b0000,
|
||||
DoublePush = 0b0001,
|
||||
|
@ -13,6 +14,12 @@ pub(crate) enum Kind {
|
|||
CapturePromotion = 0b1100,
|
||||
}
|
||||
|
||||
impl Kind {
|
||||
fn is_reversible(self) -> bool {
|
||||
(self as u16) & 0b1100 == 0
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u16)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum PromotionShape {
|
||||
|
@ -32,3 +39,17 @@ impl From<PromotionShape> for Shape {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Shape> for PromotionShape {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: Shape) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Shape::Knight => Ok(PromotionShape::Knight),
|
||||
Shape::Bishop => Ok(PromotionShape::Bishop),
|
||||
Shape::Rook => Ok(PromotionShape::Rook),
|
||||
Shape::Queen => Ok(PromotionShape::Queen),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,19 +5,29 @@ use chessfriend_board::castle::Castle;
|
|||
use chessfriend_core::{Rank, Shape, Square};
|
||||
use std::fmt;
|
||||
|
||||
/// A single player's move. In chess parlance, this is a "ply".
|
||||
/// A single player's move. In game theory parlance, this is a "ply".
|
||||
///
|
||||
/// ## TODO
|
||||
///
|
||||
/// - Rename this class `Ply`.
|
||||
///
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||
pub struct Move(pub(crate) u16);
|
||||
|
||||
impl Move {
|
||||
#[must_use]
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
pub fn origin_square(&self) -> Square {
|
||||
((self.0 >> 4) & 0b111111).try_into().unwrap()
|
||||
((self.0 >> 4) & 0b111_111).try_into().unwrap()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
pub fn target_square(&self) -> Square {
|
||||
(self.0 >> 10).try_into().unwrap()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn capture_square(&self) -> Option<Square> {
|
||||
if self.is_en_passant() {
|
||||
let target_square = self.target_square();
|
||||
|
@ -35,18 +45,22 @@ impl Move {
|
|||
None
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_quiet(&self) -> bool {
|
||||
self.flags() == Kind::Quiet as u16
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_double_push(&self) -> bool {
|
||||
self.flags() == Kind::DoublePush as u16
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_castle(&self) -> bool {
|
||||
self.castle().is_some()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn castle(&self) -> Option<Castle> {
|
||||
match self.flags() {
|
||||
0b0010 => Some(Castle::KingSide),
|
||||
|
@ -55,18 +69,22 @@ impl Move {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_capture(&self) -> bool {
|
||||
(self.0 & 0b0100) != 0
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_en_passant(&self) -> bool {
|
||||
self.flags() == 0b0101
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_promotion(&self) -> bool {
|
||||
(self.0 & 0b1000) != 0
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn promotion(&self) -> Option<Shape> {
|
||||
if !self.is_promotion() {
|
||||
return None;
|
||||
|
@ -80,18 +98,22 @@ impl Move {
|
|||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Move {
|
||||
#[inline]
|
||||
fn flags(&self) -> u16 {
|
||||
fn flags(self) -> u16 {
|
||||
self.0 & 0b1111
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn special(&self) -> u16 {
|
||||
fn special(self) -> u16 {
|
||||
self.0 & 0b11
|
||||
}
|
||||
}
|
||||
|
||||
fn _transfer_char(&self) -> char {
|
||||
impl Move {
|
||||
fn _transfer_char(self) -> char {
|
||||
if self.is_capture() || self.is_en_passant() {
|
||||
'x'
|
||||
} else {
|
||||
|
@ -103,22 +125,19 @@ impl Move {
|
|||
impl fmt::Display for Move {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(castle) = self.castle() {
|
||||
match castle {
|
||||
Castle::KingSide => return write!(f, "0-0"),
|
||||
Castle::QueenSide => return write!(f, "0-0-0"),
|
||||
}
|
||||
return match castle {
|
||||
Castle::KingSide => write!(f, "0-0"),
|
||||
Castle::QueenSide => write!(f, "0-0-0"),
|
||||
};
|
||||
}
|
||||
|
||||
write!(
|
||||
f,
|
||||
"{}{}{}",
|
||||
self.origin_square(),
|
||||
self._transfer_char(),
|
||||
self.target_square()
|
||||
)?;
|
||||
let origin = self.origin_square();
|
||||
let target = self.target_square();
|
||||
let transfer_char = self._transfer_char();
|
||||
write!(f, "{origin}{transfer_char}{target}")?;
|
||||
|
||||
if let Some(promotion) = self.promotion() {
|
||||
write!(f, "={}", promotion)?;
|
||||
write!(f, "={promotion}")?;
|
||||
} else if self.is_en_passant() {
|
||||
write!(f, " e.p.")?;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use chessfriend_board::castle::Castle;
|
||||
use chessfriend_core::{piece, Color, File, Shape, Square};
|
||||
use chessfriend_moves::{testing::*, Builder, Castle, PromotionShape};
|
||||
use chessfriend_moves::{testing::*, Builder, PromotionShape};
|
||||
|
||||
macro_rules! assert_flag {
|
||||
($move:expr, $left:expr, $right:expr, $desc:expr) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue