use crate::ToUsi;
#[repr(u8)]
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
pub enum PieceKind {
Pawn = 1,
Lance = 2,
Knight = 3,
Silver = 4,
Gold = 5,
Bishop = 6,
Rook = 7,
King = 8,
ProPawn = 9,
ProLance = 10,
ProKnight = 11,
ProSilver = 12,
ProBishop = 13,
ProRook = 14,
}
impl PieceKind {
#[must_use]
#[inline]
pub fn promote(self) -> Option<Self> {
match self {
PieceKind::Pawn => Some(PieceKind::ProPawn),
PieceKind::Lance => Some(PieceKind::ProLance),
PieceKind::Knight => Some(PieceKind::ProKnight),
PieceKind::Silver => Some(PieceKind::ProSilver),
PieceKind::Gold => None,
PieceKind::Bishop => Some(PieceKind::ProBishop),
PieceKind::Rook => Some(PieceKind::ProRook),
PieceKind::King => None,
PieceKind::ProPawn => None,
PieceKind::ProLance => None,
PieceKind::ProKnight => None,
PieceKind::ProSilver => None,
PieceKind::ProBishop => None,
PieceKind::ProRook => None,
}
}
#[must_use]
#[inline]
pub fn unpromote(self) -> Option<Self> {
match self {
PieceKind::Pawn => None,
PieceKind::Lance => None,
PieceKind::Knight => None,
PieceKind::Silver => None,
PieceKind::Gold => None,
PieceKind::Bishop => None,
PieceKind::Rook => None,
PieceKind::King => None,
PieceKind::ProPawn => Some(PieceKind::Pawn),
PieceKind::ProLance => Some(PieceKind::Lance),
PieceKind::ProKnight => Some(PieceKind::Knight),
PieceKind::ProSilver => Some(PieceKind::Silver),
PieceKind::ProBishop => Some(PieceKind::Bishop),
PieceKind::ProRook => Some(PieceKind::Rook),
}
}
pub const fn from_u8(repr: u8) -> Option<Self> {
if matches!(repr, 1..=14) {
Some(unsafe { Self::from_u8_unchecked(repr) })
} else {
None
}
}
#[no_mangle]
pub extern "C" fn PieceKind_from_u8(repr: u8) -> OptionPieceKind {
Self::from_u8(repr).into()
}
#[inline(always)]
pub const unsafe fn from_u8_unchecked(repr: u8) -> Self {
core::mem::transmute(repr)
}
#[no_mangle]
#[inline(always)]
pub unsafe extern "C" fn PieceKind_from_u8_unchecked(repr: u8) -> Self {
core::mem::transmute(repr)
}
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn PieceKind_promote(self) -> OptionPieceKind {
self.promote().into()
}
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn PieceKind_unpromote(self) -> OptionPieceKind {
self.unpromote().into()
}
#[inline]
pub const fn array_index(self) -> usize {
let result = self as usize - 1;
if result >= 14 {
unsafe { core::hint::unreachable_unchecked() };
}
result
}
pub const NUM: usize = 14;
#[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
#[cfg(feature = "experimental")]
#[inline]
pub const fn option_array_index(arg: Option<Self>) -> usize {
match arg {
Some(result) => result as usize,
None => 0,
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
#[cfg(feature = "experimental")]
pub const OPTION_NUM: usize = 15;
pub fn all() -> [Self; Self::NUM] {
[
PieceKind::Pawn,
PieceKind::Lance,
PieceKind::Knight,
PieceKind::Silver,
PieceKind::Gold,
PieceKind::Bishop,
PieceKind::Rook,
PieceKind::King,
PieceKind::ProPawn,
PieceKind::ProLance,
PieceKind::ProKnight,
PieceKind::ProSilver,
PieceKind::ProBishop,
PieceKind::ProRook,
]
}
}
impl_ord_for_fieldless_enum!(PieceKind);
impl_hash_for_fieldless_enum!(PieceKind);
impl ToUsi for PieceKind {
fn to_usi<W: core::fmt::Write>(&self, sink: &mut W) -> core::fmt::Result {
let piece_kind = *self;
if piece_kind as u8 >= PieceKind::ProPawn as u8 {
let table = b"+P+L+N+S+B+R";
let index = piece_kind as usize - PieceKind::ProPawn as usize;
debug_assert!(index < 12);
sink.write_str(unsafe {
core::str::from_utf8_unchecked(table.get_unchecked(2 * index..2 * index + 2))
})
} else {
debug_assert!(piece_kind as u8 <= PieceKind::King as u8);
let symbols = b"PLNSGBRK";
let c = *unsafe { symbols.get_unchecked(piece_kind as usize - 1) };
unsafe { crate::common::write_ascii_byte(sink, c) }
}
}
}
#[repr(transparent)]
pub struct OptionPieceKind(u8);
impl From<Option<PieceKind>> for OptionPieceKind {
#[inline(always)]
fn from(arg: Option<PieceKind>) -> Self {
Self(match arg {
Some(result) => result as u8,
None => 0,
})
}
}
impl From<OptionPieceKind> for Option<PieceKind> {
#[inline(always)]
fn from(arg: OptionPieceKind) -> Self {
if arg.0 == 0 {
None
} else {
Some(unsafe { PieceKind::from_u8_unchecked(arg.0) })
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_u8_works() {
for piece_kind in PieceKind::all() {
assert_eq!(PieceKind::from_u8(piece_kind as u8), Some(piece_kind));
}
}
#[test]
fn array_index_works() {
assert_eq!(PieceKind::all().len(), PieceKind::NUM);
for i in 0..PieceKind::NUM {
assert_eq!(PieceKind::all()[i].array_index(), i);
}
}
fn to_usi_reference<W: core::fmt::Write>(this: &PieceKind, sink: &mut W) -> core::fmt::Result {
match *this {
PieceKind::Pawn => sink.write_char('P'),
PieceKind::Lance => sink.write_char('L'),
PieceKind::Knight => sink.write_char('N'),
PieceKind::Silver => sink.write_char('S'),
PieceKind::Gold => sink.write_char('G'),
PieceKind::Bishop => sink.write_char('B'),
PieceKind::Rook => sink.write_char('R'),
PieceKind::King => sink.write_char('K'),
PieceKind::ProPawn => sink.write_str("+P"),
PieceKind::ProLance => sink.write_str("+L"),
PieceKind::ProKnight => sink.write_str("+N"),
PieceKind::ProSilver => sink.write_str("+S"),
PieceKind::ProBishop => sink.write_str("+B"),
PieceKind::ProRook => sink.write_str("+R"),
}
}
#[test]
fn to_usi_works() {
for piece_kind in PieceKind::all() {
let mut actual = String::new();
piece_kind.to_usi(&mut actual).unwrap();
let mut expected = String::new();
to_usi_reference(&piece_kind, &mut expected).unwrap();
assert_eq!(actual, expected);
}
}
}