use std::fmt;
use std::char;
use std::ops;
use std::num;
use crate::square::{Square, File, Rank};
pub use self::Color::{Black, White};
pub use self::Role::{Bishop, King, Knight, Pawn, Queen, Rook};
#[allow(missing_docs)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub enum Color {
Black = 0,
White = 1,
}
impl Color {
pub fn from_char(ch: char) -> Option<Color> {
match ch {
'w' => Some(Color::White),
'b' => Some(Color::Black),
_ => None,
}
}
#[inline]
pub fn from_white(white: bool) -> Color {
if white { Color::White } else { Color::Black }
}
#[inline]
pub fn from_black(black: bool) -> Color {
if black { Color::Black } else { Color::White }
}
#[inline]
pub fn fold<T>(self, white: T, black: T) -> T {
match self {
Color::White => white,
Color::Black => black
}
}
#[inline]
pub fn is_white(self) -> bool { self == Color::White }
#[inline]
pub fn is_black(self) -> bool { self == Color::Black }
#[inline]
pub fn backrank(self) -> Rank { self.fold(Rank::First, Rank::Eighth) }
pub fn char(self) -> char { self.fold('w', 'b') }
#[inline]
pub fn pawn(self) -> Piece { Pawn.of(self) }
#[inline]
pub fn knight(self) -> Piece { Knight.of(self) }
#[inline]
pub fn bishop(self) -> Piece { Bishop.of(self) }
#[inline]
pub fn rook(self) -> Piece { Rook.of(self) }
#[inline]
pub fn queen(self) -> Piece { Queen.of(self) }
#[inline]
pub fn king(self) -> Piece { King.of(self) }
}
impl ops::Not for Color {
type Output = Color;
#[inline]
fn not(self) -> Color {
self.fold(Color::Black, Color::White)
}
}
impl ops::BitXor<bool> for Color {
type Output = Color;
#[inline]
fn bitxor(self, flip: bool) -> Color {
Color::from_white(self.is_white() ^ flip)
}
}
#[allow(missing_docs)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
pub enum Role {
Pawn = 1,
Knight = 2,
Bishop = 3,
Rook = 4,
Queen = 5,
King = 6,
}
impl Role {
pub fn from_char(ch: char) -> Option<Role> {
match ch {
'P' | 'p' => Some(Role::Pawn),
'N' | 'n' => Some(Role::Knight),
'B' | 'b' => Some(Role::Bishop),
'R' | 'r' => Some(Role::Rook),
'Q' | 'q' => Some(Role::Queen),
'K' | 'k' => Some(Role::King),
_ => None,
}
}
#[inline]
pub fn of(self, color: Color) -> Piece {
Piece { color, role: self }
}
pub fn char(self) -> char {
match self {
Role::Pawn => 'p',
Role::Knight => 'n',
Role::Bishop => 'b',
Role::Rook => 'r',
Role::Queen => 'q',
Role::King => 'k',
}
}
pub fn upper_char(self) -> char {
match self {
Role::Pawn => 'P',
Role::Knight => 'N',
Role::Bishop => 'B',
Role::Rook => 'R',
Role::Queen => 'Q',
Role::King => 'K',
}
}
}
macro_rules! int_from_role_impl {
($($t:ty)+) => {
$(impl From<Role> for $t {
#[inline]
fn from(role: Role) -> $t {
role as $t
}
})+
}
}
int_from_role_impl! { u8 i8 u16 i16 u32 i32 u64 i64 u128 i128 usize isize }
macro_rules! nonzero_int_from_role_impl {
($($t:ty)+) => {
$(impl From<Role> for $t {
#[inline]
fn from(role: Role) -> $t {
<$t>::new(role.into()).expect("nonzero role discriminant")
}
})+
}
}
nonzero_int_from_role_impl! {
num::NonZeroU8 num::NonZeroI8
num::NonZeroU16 num::NonZeroI16
num::NonZeroU32 num::NonZeroI32
num::NonZeroU64 num::NonZeroI64
num::NonZeroU128 num::NonZeroI128
num::NonZeroUsize num::NonZeroIsize
}
macro_rules! try_role_from_int_impl {
($($t:ty)+) => {
$(impl std::convert::TryFrom<$t> for Role {
type Error = crate::errors::TryFromIntError;
#[inline]
fn try_from(value: $t) -> Result<Role, Self::Error> {
Ok(match value {
1 => Role::Pawn,
2 => Role::Knight,
3 => Role::Bishop,
4 => Role::Rook,
5 => Role::Queen,
6 => Role::King,
_ => return Err(crate::errors::TryFromIntError),
})
}
})+
}
}
try_role_from_int_impl! { u8 i8 u16 i16 u32 i32 u64 i64 u128 i128 usize isize }
pub const ROLES: [Role; 6] = [Pawn, Knight, Bishop, Rook, Queen, King];
#[allow(missing_docs)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub struct Piece {
pub color: Color,
pub role: Role,
}
impl Piece {
pub fn char(self) -> char {
self.color.fold(self.role.upper_char(), self.role.char())
}
pub fn from_char(ch: char) -> Option<Piece> {
Role::from_char(ch).map(|role| {
role.of(Color::from_white(32 & ch as u8 == 0))
})
}
}
#[derive(Clone, Eq, PartialEq, Debug)]
#[repr(align(4))]
pub enum Move {
Normal {
role: Role,
from: Square,
capture: Option<Role>,
to: Square,
promotion: Option<Role>,
},
EnPassant { from: Square, to: Square },
Castle { king: Square, rook: Square },
Put { role: Role, to: Square },
}
impl Move {
pub fn role(&self) -> Role {
match *self {
Move::Normal { role, .. } | Move::Put { role, .. } => role,
Move::EnPassant { .. } => Role::Pawn,
Move::Castle { .. } => Role::King,
}
}
pub fn from(&self) -> Option<Square> {
match *self {
Move::Normal { from, .. } | Move::EnPassant { from, .. } => Some(from),
Move::Castle { king, .. } => Some(king),
Move::Put { .. } => None,
}
}
pub fn to(&self) -> Square {
match *self {
Move::Normal { to, .. } | Move::EnPassant { to, .. } | Move::Put { to, .. } => to,
Move::Castle { rook, .. } => rook,
}
}
pub fn capture(&self) -> Option<Role> {
match *self {
Move::Normal { capture, .. } => capture,
Move::EnPassant { .. } => Some(Pawn),
_ => None,
}
}
pub fn is_capture(&self) -> bool {
matches!(*self, Move::Normal { capture: Some(_), .. } | Move::EnPassant { .. })
}
pub fn is_en_passant(&self) -> bool {
matches!(*self, Move::EnPassant { .. })
}
pub fn is_zeroing(&self) -> bool {
matches!(*self, Move::Normal { role: Role::Pawn, ..} | Move::Normal { capture: Some(_), .. } | Move::EnPassant { .. } | Move::Put { role: Role::Pawn, .. })
}
pub fn castling_side(&self) -> Option<CastlingSide> {
match *self {
Move::Castle { king, rook } if king < rook => Some(CastlingSide::KingSide),
Move::Castle { .. } => Some(CastlingSide::QueenSide),
_ => None,
}
}
pub fn is_castle(&self) -> bool {
matches!(*self, Move::Castle { .. })
}
pub fn promotion(&self) -> Option<Role> {
match *self {
Move::Normal { promotion, .. } => promotion,
_ => None,
}
}
pub fn is_promotion(&self) -> bool {
matches!(*self, Move::Normal { promotion: Some(_), .. })
}
}
impl fmt::Display for Move {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Move::Normal { role, from, capture, to, promotion } => {
if role != Role::Pawn {
write!(f, "{}", role.upper_char())?;
}
write!(f, "{}{}{}", from, if capture.is_some() { 'x' } else { '-' }, to)?;
if let Some(p) = promotion {
write!(f, "={}", p.upper_char())?;
}
Ok(())
},
Move::EnPassant { from, to, .. } => {
write!(f, "{}x{}", from, to)
},
Move::Castle { king, rook } => {
if king < rook {
write!(f, "O-O")
} else {
write!(f, "O-O-O")
}
},
Move::Put { role, to } => {
if role != Role::Pawn {
write!(f, "{}", role.upper_char())?;
}
write!(f, "@{}", to)
},
}
}
}
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
pub struct RemainingChecks {
pub white: u8,
pub black: u8,
}
impl Default for RemainingChecks {
fn default() -> RemainingChecks {
RemainingChecks { white: 3, black: 3 }
}
}
impl RemainingChecks {
pub fn by_color(&self, color: Color) -> u8 {
color.fold(self.white, self.black)
}
pub fn by_color_mut(&mut self, color: Color) -> &mut u8 {
color.fold(&mut self.white, &mut self.black)
}
pub fn decrement(&mut self, color: Color) {
*self.by_color_mut(color) = self.by_color(color).saturating_sub(1);
}
}
impl fmt::Display for RemainingChecks {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}+{}", self.white, self.black)
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub enum CastlingSide {
KingSide = 0,
QueenSide = 1,
}
impl CastlingSide {
pub fn is_queen_side(self) -> bool {
match self {
CastlingSide::KingSide => false,
CastlingSide::QueenSide => true,
}
}
pub fn is_king_side(self) -> bool {
!self.is_queen_side()
}
pub fn from_queen_side(queen_side: bool) -> CastlingSide {
if queen_side { CastlingSide::QueenSide } else { CastlingSide::KingSide }
}
pub fn from_king_side(king_side: bool) -> CastlingSide {
if king_side { CastlingSide::KingSide } else { CastlingSide::QueenSide }
}
pub fn king_to_file(self) -> File {
match self {
CastlingSide::KingSide => File::G,
CastlingSide::QueenSide => File::C,
}
}
pub fn rook_to_file(self) -> File {
match self {
CastlingSide::KingSide => File::F,
CastlingSide::QueenSide => File::D,
}
}
pub fn king_to(self, color: Color) -> Square {
Square::from_coords(self.king_to_file(), color.backrank())
}
pub fn rook_to(self, color: Color) -> Square {
Square::from_coords(self.rook_to_file(), color.backrank())
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub enum CastlingMode {
Standard,
Chess960,
}
impl CastlingMode {
pub fn from_standard(standard: bool) -> CastlingMode {
if standard { CastlingMode::Standard } else { CastlingMode::Chess960 }
}
pub fn from_chess960(chess960: bool) -> CastlingMode {
if chess960 { CastlingMode::Chess960 } else { CastlingMode::Standard }
}
pub fn is_standard(self) -> bool {
self == CastlingMode::Standard
}
pub fn is_chess960(self) -> bool {
self == CastlingMode::Chess960
}
}
#[cfg(test)]
mod tests {
use std::mem;
use super::*;
#[test]
fn test_role_order() {
assert!(Role::Pawn < Role::Knight);
assert!(Role::Knight < Role::Bishop);
assert!(Role::Bishop < Role::Rook);
assert!(Role::Rook < Role::Queen);
assert!(Role::Queen < Role::King);
}
#[test]
fn test_size() {
assert!(mem::size_of::<Move>() <= 8);
}
}