// Eryn Wells use crate::display::{ASCIIDisplay, FENDisplay, UnicodeDisplay}; use chessfriend_core::Square; use std::fmt; use std::slice::Iter; #[derive(Debug, Eq, PartialEq)] pub enum TryFromError { InvalidCharacter, ZeroLengthString, } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum Color { White = 0, Black = 1, } impl Color { pub fn iter() -> impl Iterator { [Color::White, Color::Black].into_iter() } pub fn other(&self) -> Color { match self { Color::White => Color::Black, Color::Black => Color::White, } } } impl Default for Color { fn default() -> Self { Color::White } } impl fmt::Display for Color { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{}", match self { Color::White => "White", Color::Black => "Black", }, ) } } impl TryFrom for Color { type Error = TryFromError; fn try_from(value: char) -> Result { match value { 'w' | 'W' => Ok(Color::White), 'b' | 'B' => Ok(Color::Black), _ => Err(TryFromError::InvalidCharacter), } } } macro_rules! try_from_string { ($type:ty) => { try_from_string!($type, &str); try_from_string!($type, &String); }; ($type:ty, $from_type:ty) => { impl TryFrom<$from_type> for $type { type Error = TryFromError; fn try_from(value: $from_type) -> Result { let first_char = value.chars().nth(0).ok_or(TryFromError::ZeroLengthString)?; Self::try_from(first_char) } } }; } try_from_string!(Color); #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum Shape { Pawn = 0, Knight = 1, Bishop = 2, Rook = 3, Queen = 4, King = 5, } impl Shape { pub fn iter() -> Iter<'static, Shape> { const ALL_SHAPES: [Shape; 6] = [ Shape::Pawn, Shape::Knight, Shape::Bishop, Shape::Rook, Shape::Queen, Shape::King, ]; ALL_SHAPES.iter() } pub fn promotable() -> Iter<'static, Shape> { const PROMOTABLE_SHAPES: [Shape; 4] = [Shape::Queen, Shape::Rook, Shape::Bishop, Shape::Knight]; PROMOTABLE_SHAPES.iter() } fn to_ascii(&self) -> char { match self { Shape::Pawn => 'P', Shape::Knight => 'N', Shape::Bishop => 'B', Shape::Rook => 'R', Shape::Queen => 'Q', Shape::King => 'K', } } } impl TryFrom for Shape { type Error = TryFromError; fn try_from(value: char) -> Result { match value { 'P' | 'p' => Ok(Shape::Pawn), 'N' | 'n' => Ok(Shape::Knight), 'B' | 'b' => Ok(Shape::Bishop), 'R' | 'r' => Ok(Shape::Rook), 'Q' | 'q' => Ok(Shape::Queen), 'K' | 'k' => Ok(Shape::King), _ => Err(TryFromError::InvalidCharacter), } } } try_from_string!(Shape); impl Into for &Shape { fn into(self) -> char { self.to_ascii() } } impl Into for Shape { fn into(self) -> char { self.to_ascii() } } impl fmt::Display for Shape { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let self_char: char = self.into(); write!(f, "{}", self_char) } } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Piece { color: Color, shape: Shape, } macro_rules! piece_constructor { ($func_name:ident, $type:tt) => { pub fn $func_name(color: Color) -> Piece { Piece { color, shape: Shape::$type, } } }; } macro_rules! is_shape { ($func_name:ident, $shape:ident) => { pub fn $func_name(&self) -> bool { self.shape == Shape::$shape } }; } impl Piece { pub fn new(color: Color, shape: Shape) -> Piece { Piece { color, shape } } piece_constructor!(pawn, Pawn); piece_constructor!(knight, Knight); piece_constructor!(bishop, Bishop); piece_constructor!(rook, Rook); piece_constructor!(queen, Queen); piece_constructor!(king, King); pub fn color(&self) -> Color { self.color } pub fn shape(&self) -> Shape { self.shape } is_shape!(is_pawn, Pawn); is_shape!(is_knight, Knight); is_shape!(is_bishop, Bishop); is_shape!(is_rook, Rook); is_shape!(is_queen, Queen); is_shape!(is_king, King); pub fn to_ascii(&self) -> char { self.shape.to_ascii() } } impl fmt::Display for Piece { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { UnicodeDisplay::fmt(self, f) } } impl ASCIIDisplay for Piece { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", Into::::into(self.shape())) } } impl UnicodeDisplay for Piece { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{}", match (self.color, self.shape) { (Color::Black, Shape::Pawn) => '♟', (Color::Black, Shape::Knight) => '♞', (Color::Black, Shape::Bishop) => '♝', (Color::Black, Shape::Rook) => '♜', (Color::Black, Shape::Queen) => '♛', (Color::Black, Shape::King) => '♚', (Color::White, Shape::Pawn) => '♙', (Color::White, Shape::Knight) => '♘', (Color::White, Shape::Bishop) => '♗', (Color::White, Shape::Rook) => '♖', (Color::White, Shape::Queen) => '♕', (Color::White, Shape::King) => '♔', } ) } } impl FENDisplay for Piece { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let ascii = self.shape().to_ascii(); write!( f, "{}", match self.color { Color::White => ascii.to_ascii_uppercase(), Color::Black => ascii.to_ascii_lowercase(), } ) } } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct PlacedPiece { piece: Piece, square: Square, } macro_rules! is_shape { ($func_name:ident, $shape:ident) => { pub fn $func_name(&self) -> bool { self.piece().shape == Shape::$shape } }; } impl PlacedPiece { pub const fn new(piece: Piece, square: Square) -> PlacedPiece { PlacedPiece { piece, square } } #[inline] pub fn piece(&self) -> &Piece { &self.piece } #[inline] pub fn square(&self) -> Square { self.square } #[inline] pub fn color(&self) -> Color { self.piece.color } #[inline] pub fn shape(&self) -> Shape { self.piece.shape } is_shape!(is_pawn, Pawn); is_shape!(is_knight, Knight); is_shape!(is_bishop, Bishop); is_shape!(is_rook, Rook); is_shape!(is_queen, Queen); is_shape!(is_king, King); pub fn is_kingside_rook(&self) -> bool { self.is_rook() && match self.color() { Color::White => self.square == Square::H1, Color::Black => self.square == Square::H8, } } pub fn is_queenside_rook(&self) -> bool { self.is_rook() && match self.color() { Color::White => self.square == Square::A1, Color::Black => self.square == Square::A8, } } } impl fmt::Display for PlacedPiece { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}{}", self.piece, self.square) } } impl From<(&Square, &Piece)> for PlacedPiece { fn from(value: (&Square, &Piece)) -> Self { PlacedPiece::new(*value.1, *value.0) } } #[cfg(test)] mod tests { use super::*; #[test] fn shape_try_from() { assert_eq!(Shape::try_from('p'), Ok(Shape::Pawn)); assert_eq!(Shape::try_from("p"), Ok(Shape::Pawn)); } #[test] fn shape_into_char() { assert_eq!(>::into(Shape::Pawn) as char, 'P'); } }