use core::{
fmt::{self, Display},
num,
};
use crate::{
ByColor, Color, Role,
util::{AppendAscii, out_of_range_error},
};
#[allow(missing_docs)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub struct Piece {
pub color: Color,
pub role: Role,
}
impl Piece {
pub const fn char(self) -> char {
match self.color {
Color::White => self.role.upper_char(),
Color::Black => self.role.char(),
}
}
pub const fn from_char(ch: char) -> Option<Piece> {
let Some(role) = Role::from_char(ch) else {
return None;
};
Some(role.of(Color::from_white(32 & ch as u8 == 0)))
}
}
#[cfg(feature = "bincode")]
impl bincode::Encode for Piece {
#[rustfmt::skip]
fn encode<E: bincode::enc::Encoder>(
&self,
encoder: &mut E,
) -> Result<(), bincode::error::EncodeError> {
u8::encode(
&match *self {
Piece { role: Role::Pawn, color: Color::White } => 0,
Piece { role: Role::Pawn, color: Color::Black } => 1,
Piece { role: Role::Knight, color: Color::White } => 2,
Piece { role: Role::Knight, color: Color::Black } => 3,
Piece { role: Role::Bishop, color: Color::White } => 4,
Piece { role: Role::Bishop, color: Color::Black } => 5,
Piece { role: Role::Rook, color: Color::White } => 6,
Piece { role: Role::Rook, color: Color::Black } => 7,
Piece { role: Role::Queen, color: Color::White } => 8,
Piece { role: Role::Queen, color: Color::Black } => 9,
Piece { role: Role::King, color: Color::White } => 10,
Piece { role: Role::King, color: Color::Black } => 11,
},
encoder,
)
}
}
#[cfg(feature = "bincode")]
impl<Config> bincode::Decode<Config> for Piece {
#[rustfmt::skip]
fn decode<D: bincode::de::Decoder>(decoder: &mut D) -> Result<Self, bincode::error::DecodeError> {
Ok(match u8::decode(decoder)? {
0 => Piece { role: Role::Pawn, color: Color::White },
1 => Piece { role: Role::Pawn, color: Color::Black },
2 => Piece { role: Role::Knight, color: Color::White },
3 => Piece { role: Role::Knight, color: Color::Black },
4 => Piece { role: Role::Bishop, color: Color::White },
5 => Piece { role: Role::Bishop, color: Color::Black },
6 => Piece { role: Role::Rook, color: Color::White },
7 => Piece { role: Role::Rook, color: Color::Black },
8 => Piece { role: Role::Queen, color: Color::White },
9 => Piece { role: Role::Queen, color: Color::Black },
10 => Piece { role: Role::King, color: Color::White },
11 => Piece { role: Role::King, color: Color::Black },
_ => return Err(bincode::error::DecodeError::Other("invalid Piece")),
})
}
}
#[cfg(feature = "bincode")]
bincode::impl_borrow_decode!(Piece);
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum CastlingMode {
Standard,
Chess960,
}
impl CastlingMode {
pub const fn from_standard(standard: bool) -> CastlingMode {
if standard {
CastlingMode::Standard
} else {
CastlingMode::Chess960
}
}
pub const fn from_chess960(chess960: bool) -> CastlingMode {
if chess960 {
CastlingMode::Chess960
} else {
CastlingMode::Standard
}
}
pub const fn is_standard(self) -> bool {
matches!(self, CastlingMode::Standard)
}
pub const fn is_chess960(self) -> bool {
matches!(self, CastlingMode::Chess960)
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum EnPassantMode {
Legal,
PseudoLegal,
Always,
}
#[cfg(test)]
mod tests {
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);
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
pub struct RemainingChecks(u32);
impl Default for RemainingChecks {
fn default() -> RemainingChecks {
RemainingChecks(3)
}
}
impl RemainingChecks {
#[track_caller]
pub const fn new(n: u32) -> RemainingChecks {
assert!(n <= 3);
RemainingChecks(n)
}
pub const fn is_zero(self) -> bool {
self.0 == 0
}
#[must_use]
pub const fn saturating_sub(self, n: u32) -> RemainingChecks {
RemainingChecks(self.0.saturating_sub(n))
}
}
#[cfg(feature = "arbitrary")]
impl arbitrary::Arbitrary<'_> for RemainingChecks {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<RemainingChecks> {
u.int_in_range::<u8>(0..=3)
.map(|n| RemainingChecks(u32::from(n)))
}
#[inline]
fn size_hint(_depth: usize) -> (usize, Option<usize>) {
(1, Some(1))
}
}
macro_rules! int_from_remaining_checks_impl {
($($t:ty)+) => {
$(impl From<RemainingChecks> for $t {
#[inline]
fn from(RemainingChecks(checks): RemainingChecks) -> $t {
checks as $t
}
})+
}
}
int_from_remaining_checks_impl! { u8 i8 u16 i16 u32 i32 u64 i64 u128 i128 usize isize }
macro_rules! try_remaining_checks_from_int_impl {
($($t:ty)+) => {
$(impl core::convert::TryFrom<$t> for RemainingChecks {
type Error = num::TryFromIntError;
#[inline]
fn try_from(value: $t) -> Result<RemainingChecks, Self::Error> {
let n = u32::try_from(value)?;
if n <= 3 {
Ok(RemainingChecks::new(n))
} else {
Err(out_of_range_error())
}
}
})+
}
}
try_remaining_checks_from_int_impl! { u8 i8 u16 i16 u32 i32 u64 i64 u128 i128 usize isize }
impl ByColor<RemainingChecks> {
pub(crate) fn append_to<W: AppendAscii>(self, f: &mut W) -> Result<(), W::Error> {
f.append_u32(self.white.0)?;
f.append_ascii('+')?;
f.append_u32(self.black.0)
}
}
impl Display for ByColor<RemainingChecks> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.append_to(f)
}
}
#[cfg(feature = "bincode")]
impl bincode::Encode for RemainingChecks {
fn encode<E: bincode::enc::Encoder>(
&self,
encoder: &mut E,
) -> Result<(), bincode::error::EncodeError> {
u8::from(*self).encode(encoder)
}
}
#[cfg(feature = "bincode")]
impl<Config> bincode::Decode<Config> for RemainingChecks {
fn decode<D: bincode::de::Decoder>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
RemainingChecks::try_from(u8::decode(decoder)?)
.map_err(|_| bincode::error::DecodeError::Other("invalid RemainingChecks"))
}
}
#[cfg(feature = "bincode")]
bincode::impl_borrow_decode!(RemainingChecks);