use std::fmt;
use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
use arrayvec::ArrayVec;
use shakmaty::{Chess, Color, Outcome, Piece};
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct TableType {
pub ext: &'static str,
pub magic: [u8; 4],
}
pub trait Syzygy {
const TBW: TableType;
const TBZ: TableType;
const PAWNLESS_TBW: Option<TableType> = None;
const PAWNLESS_TBZ: Option<TableType> = None;
const ONE_KING: bool;
const CONNECTED_KINGS: bool;
const CAPTURES_COMPULSORY: bool;
const MAX_PIECES: usize = 6;
}
impl Syzygy for Chess {
const TBW: TableType = TableType { ext: "rtbw", magic: [0x71, 0xe8, 0x23, 0x5d] };
const TBZ: TableType = TableType { ext: "rtbz", magic: [0xd7, 0x66, 0x0c, 0xa5] };
const ONE_KING: bool = true;
const CONNECTED_KINGS: bool = false;
const CAPTURES_COMPULSORY: bool = false;
const MAX_PIECES: usize = 7;
}
#[cfg(feature = "variant")]
impl Syzygy for shakmaty::variant::Atomic {
const TBW: TableType = TableType { ext: "atbw", magic: [0x55, 0x8d, 0xa4, 0x49] };
const TBZ: TableType = TableType { ext: "atbz", magic: [0x91, 0xa9, 0x5e, 0xeb] };
const ONE_KING: bool = true;
const CONNECTED_KINGS: bool = true;
const CAPTURES_COMPULSORY: bool = false;
}
#[cfg(feature = "variant")]
impl Syzygy for shakmaty::variant::Antichess {
const TBW: TableType = TableType { ext: "gtbw", magic: [0xbc, 0x55, 0xbc, 0x21] };
const TBZ: TableType = TableType { ext: "gtbz", magic: [0xd6, 0xf5, 0x1b, 0x50] };
const PAWNLESS_TBW: Option<TableType> = Some(TableType { ext: "stbw", magic: [0x7b, 0xf6, 0x93, 0x15] });
const PAWNLESS_TBZ: Option<TableType> = Some(TableType { ext: "stbz", magic: [0xe4, 0xcf, 0xe7, 0x23] });
const ONE_KING: bool = false;
const CONNECTED_KINGS: bool = true;
const CAPTURES_COMPULSORY: bool = true;
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(i8)]
pub enum Wdl {
Loss = -2,
BlessedLoss = -1,
Draw = 0,
CursedWin = 1,
Win = 2,
}
impl Wdl {
pub fn from_outcome(outcome: Outcome, pov: Color) -> Wdl {
match outcome {
Outcome::Draw => Wdl::Draw,
Outcome::Decisive { winner } if winner == pov => Wdl::Win,
_ => Wdl::Loss,
}
}
pub fn from_dtz_after_zeroing(dtz: Dtz) -> Wdl {
match dtz.0 {
n if -100 <= n && n <= -1 => Wdl::Loss,
n if n < -100 => Wdl::BlessedLoss,
0 => Wdl::Draw,
n if 100 < n => Wdl::CursedWin,
_ => Wdl::Win,
}
}
pub(crate) fn decisive(self) -> Option<DecisiveWdl> {
Some(match self {
Wdl::Loss => DecisiveWdl::Loss,
Wdl::BlessedLoss => DecisiveWdl::BlessedLoss,
Wdl::Draw => return None,
Wdl::CursedWin => DecisiveWdl::CursedWin,
Wdl::Win => DecisiveWdl::Win,
})
}
}
impl Neg for Wdl {
type Output = Wdl;
fn neg(self) -> Wdl {
match self {
Wdl::Loss => Wdl::Win,
Wdl::BlessedLoss => Wdl::CursedWin,
Wdl::Draw => Wdl::Draw,
Wdl::CursedWin => Wdl::BlessedLoss,
Wdl::Win => Wdl::Loss,
}
}
}
impl From<DecisiveWdl> for Wdl {
fn from(wdl: DecisiveWdl) -> Wdl {
match wdl {
DecisiveWdl::Loss => Wdl::Loss,
DecisiveWdl::BlessedLoss => Wdl::BlessedLoss,
DecisiveWdl::CursedWin => Wdl::CursedWin,
DecisiveWdl::Win => Wdl::Win,
}
}
}
macro_rules! from_wdl_impl {
($wdl:ty, $($t:ty)+) => {
$(impl From<$wdl> for $t {
#[inline]
fn from(wdl: $wdl) -> $t {
wdl as $t
}
})+
}
}
from_wdl_impl! { Wdl, i8 i16 i32 i64 i128 isize }
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum DecisiveWdl {
Loss = -2,
BlessedLoss = -1,
CursedWin = 1,
Win = 2,
}
impl Neg for DecisiveWdl {
type Output = DecisiveWdl;
fn neg(self) -> DecisiveWdl {
match self {
DecisiveWdl::Loss => DecisiveWdl::Win,
DecisiveWdl::BlessedLoss => DecisiveWdl::CursedWin,
DecisiveWdl::CursedWin => DecisiveWdl::BlessedLoss,
DecisiveWdl::Win => DecisiveWdl::Loss,
}
}
}
from_wdl_impl! { DecisiveWdl, i8 i16 i32 i64 i128 isize }
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Dtz(pub i32);
impl Dtz {
pub fn before_zeroing<T: Into<Wdl>>(wdl: T) -> Dtz {
match wdl.into() {
Wdl::Loss => Dtz(-1),
Wdl::BlessedLoss => Dtz(-101),
Wdl::Draw => Dtz(0),
Wdl::CursedWin => Dtz(101),
Wdl::Win => Dtz(1),
}
}
pub fn add_plies(self, plies: i32) -> Dtz {
let new_dtz = self.0.signum() * (self.0.abs() + plies);
debug_assert!(self.0.signum() == new_dtz.signum());
Dtz(new_dtz)
}
}
macro_rules! from_dtz_impl {
($($t:ty)+) => {
$(impl From<Dtz> for $t {
#[inline]
fn from(wdl: Dtz) -> $t {
wdl.0.into()
}
})+
}
}
from_dtz_impl! { i32 i64 i128 }
macro_rules! dtz_from_impl {
($($t:ty)+) => {
$(impl From<$t> for Dtz {
#[inline]
fn from(dtz: $t) -> Dtz {
Dtz(i32::from(dtz))
}
})+
}
}
dtz_from_impl! { u8 i8 u16 i16 i32 }
impl Neg for Dtz {
type Output = Dtz;
#[inline]
fn neg(self) -> Dtz {
Dtz(-self.0)
}
}
impl Add for Dtz {
type Output = Dtz;
#[inline]
fn add(self, other: Dtz) -> Dtz {
Dtz(self.0 + other.0)
}
}
impl AddAssign for Dtz {
#[inline]
fn add_assign(&mut self, other: Dtz) {
self.0 += other.0;
}
}
impl Sub for Dtz {
type Output = Dtz;
#[inline]
fn sub(self, other: Dtz) -> Dtz {
Dtz(self.0 - other.0)
}
}
impl SubAssign for Dtz {
#[inline]
fn sub_assign(&mut self, other: Dtz) {
self.0 -= other.0;
}
}
pub const MAX_PIECES: usize = 7;
pub type Pieces = ArrayVec<Piece, MAX_PIECES>;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Metric {
Wdl,
Dtz,
}
impl fmt::Display for Metric {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Metric::Wdl => f.write_str("wdl"),
Metric::Dtz => f.write_str("dtz"),
}
}
}