use self::{
Color::{Black, White},
Type::{Bishop, King, Knight, Pawn, Queen, Rook},
};
use crate::error::{Err, Result};
use std::hash::Hash;
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum Type {
Pawn,
Rook,
Knight,
Bishop,
Queen,
King,
}
impl std::fmt::Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl Type {
#[must_use]
pub const fn to_array() -> [Self; 6] {
[Pawn, Rook, Knight, Bishop, Queen, King]
}
#[must_use]
pub const fn points(self) -> u8 {
match self {
Pawn => 1,
Knight | Bishop => 3,
Rook => 5,
Queen => 9,
King => 0,
}
}
#[must_use]
pub const fn san_prefix(self) -> &'static str {
match &self {
King => "K",
Queen => "Q",
Rook => "R",
Bishop => "B",
Knight => "N",
Pawn => "",
}
}
pub fn from_san(san: &str) -> Result<Self> {
let Some(first_char) = san.chars().next() else {
return Err(Err::EmptySanError);
};
match first_char {
'O' | 'K' => Ok(King),
'Q' => Ok(Queen),
'R' => Ok(Rook),
'B' => Ok(Bishop),
'N' => Ok(Knight),
_ => Ok(Pawn),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum LinearType {
Rook,
Bishop,
Queen,
}
impl From<LinearType> for Type {
fn from(value: LinearType) -> Self {
match value {
LinearType::Bishop => Self::Bishop,
LinearType::Queen => Self::Queen,
LinearType::Rook => Self::Rook,
}
}
}
impl TryFrom<Type> for LinearType {
type Error = Err;
fn try_from(value: Type) -> std::prelude::v1::Result<Self, Self::Error> {
match value {
Type::Rook => Ok(Self::Rook),
Type::Bishop => Ok(Self::Bishop),
Type::Queen => Ok(Self::Queen),
_ => Err(Err::LinearPieceTypeError(value)),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum PromotedType {
Queen,
Rook,
Bishop,
Knight,
}
impl From<PromotedType> for Type {
fn from(value: PromotedType) -> Self {
match value {
PromotedType::Bishop => Self::Bishop,
PromotedType::Knight => Self::Knight,
PromotedType::Queen => Self::Queen,
PromotedType::Rook => Self::Rook,
}
}
}
impl TryFrom<Type> for PromotedType {
type Error = Err;
fn try_from(value: Type) -> std::prelude::v1::Result<Self, Self::Error> {
match value {
Type::Queen => Ok(Self::Queen),
Type::Knight => Ok(Self::Knight),
Type::Rook => Ok(Self::Rook),
Type::Bishop => Ok(Self::Bishop),
_ => Err(Err::PromotedTypeError(value)),
}
}
}
impl PromotedType {
#[must_use]
pub const fn all() -> [Self; 4] {
[Self::Queen, Self::Rook, Self::Bishop, Self::Knight]
}
}
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum Color {
#[default]
White,
Black,
}
impl std::fmt::Display for Color {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl Color {
#[must_use]
pub const fn other(self) -> Self {
match self {
White => Black,
Black => White,
}
}
#[must_use]
pub const fn all() -> [Self; 2] {
[White, Black]
}
pub fn from_move_number_str(s: &str) -> Result<Self> {
if s.contains("...") {
Ok(Black)
} else if s.contains('.') {
Ok(White)
} else {
Err(Err::MoveAnnotationParseError(s.into()))
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum Side {
Queenside,
Kingside,
}
impl std::fmt::Display for Side {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl Side {
#[must_use]
pub const fn all() -> [Self; 2] {
[Self::Queenside, Self::Kingside]
}
#[must_use]
pub const fn castling_san(self) -> &'static str {
match self {
Self::Kingside => "O-O",
Self::Queenside => "O-O-O",
}
}
}
#[derive(Debug, Eq, Clone, Copy)]
pub struct Piece {
pub piece_type: Type,
pub color: Color,
}
impl std::fmt::Display for Piece {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} {}", self.color, self.piece_type)
}
}
impl PartialEq for Piece {
fn eq(&self, other: &Self) -> bool {
self.piece_type == other.piece_type && self.color == other.color
}
}
impl Hash for Piece {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.piece_type.hash(state);
self.color.hash(state);
}
}
impl Piece {
#[must_use]
pub const fn new(piece_type: Type, color: Color) -> Self {
Self { piece_type, color }
}
#[must_use]
pub const fn unicode_dark_background(self) -> char {
match (self.piece_type, self.color) {
(King, Black) => '♔',
(Queen, Black) => '♕',
(Rook, Black) => '♖',
(Bishop, Black) => '♗',
(Knight, Black) => '♘',
(Pawn, Black) => '♙',
(King, White) => '♚',
(Queen, White) => '♛',
(Rook, White) => '♜',
(Bishop, White) => '♝',
(Knight, White) => '♞',
(Pawn, White) => '♟',
}
}
#[must_use]
pub const fn unicode_light_background(self) -> char {
match (self.piece_type, self.color) {
(King, White) => '♔',
(Queen, White) => '♕',
(Rook, White) => '♖',
(Bishop, White) => '♗',
(Knight, White) => '♘',
(Pawn, White) => '♙',
(King, Black) => '♚',
(Queen, Black) => '♛',
(Rook, Black) => '♜',
(Bishop, Black) => '♝',
(Knight, Black) => '♞',
(Pawn, Black) => '♟',
}
}
#[must_use]
pub const fn ascii(self) -> char {
match (self.piece_type, self.color) {
(King, White) => 'K',
(Queen, White) => 'Q',
(Rook, White) => 'R',
(Bishop, White) => 'B',
(Knight, White) => 'N',
(Pawn, White) => 'P',
(King, Black) => 'k',
(Queen, Black) => 'q',
(Rook, Black) => 'r',
(Bishop, Black) => 'b',
(Knight, Black) => 'n',
(Pawn, Black) => 'p',
}
}
#[must_use]
pub const fn points(self) -> u8 {
self.piece_type.points()
}
pub const fn from_ascii(ch: char) -> Result<Self> {
match ch {
'K' => Ok(Self::new(King, White)),
'Q' => Ok(Self::new(Queen, White)),
'R' => Ok(Self::new(Rook, White)),
'B' => Ok(Self::new(Bishop, White)),
'N' => Ok(Self::new(Knight, White)),
'P' => Ok(Self::new(Pawn, White)),
'k' => Ok(Self::new(King, Black)),
'q' => Ok(Self::new(Queen, Black)),
'r' => Ok(Self::new(Rook, Black)),
'b' => Ok(Self::new(Bishop, Black)),
'n' => Ok(Self::new(Knight, Black)),
'p' => Ok(Self::new(Pawn, Black)),
_ => Err(Err::InvalidPieceIndicatorError(ch)),
}
}
#[must_use]
pub const fn san_prefix(self) -> &'static str {
self.piece_type.san_prefix()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_san() {
for (test, solution) in [("Rb3", Rook), ("O-O-O", King), ("cxb3", Pawn)] {
assert_eq!(Type::from_san(test).unwrap(), solution);
}
}
#[test]
fn piece_to_char() {
let piece = Piece {
piece_type: Rook,
color: White,
};
assert_eq!('♜', piece.unicode_dark_background());
assert_eq!('R', piece.ascii());
assert_eq!("R", piece.san_prefix());
}
#[test]
fn partial_eq() {
let piece_1 = Piece {
piece_type: Rook,
color: White,
};
let piece_2 = Piece {
piece_type: Rook,
color: White,
};
let piece_3 = Piece {
piece_type: Pawn,
color: White,
};
assert_eq!(piece_1, piece_2);
assert_ne!(piece_1, piece_3);
}
}