use crate::bitboard::*;
macro_rules! container {
($v:vis, $a:ident, $b:ident, $n:literal) => {
#[doc = "Container with values for each [`"]
#[doc = stringify!($a)]
#[doc = "`]."]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
$v struct $b<T>(pub(crate) [T; $n]);
#[allow(dead_code)]
impl<T> $b<T> {
#[inline]
pub const fn new(values: [T; $n]) -> Self {
Self(values)
}
#[inline]
pub const fn get(&self, k: $a) -> &T {
&self.0[k.index() as usize]
}
#[inline]
pub const fn get_mut(&mut self, k: $a) -> &mut T {
&mut self.0[k.index() as usize]
}
}
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(u8)]
pub enum Color {
#[default]
White,
Black,
}
container!(pub(crate), Color, ByColor, 2);
impl Color {
#[inline]
pub const fn all() -> [Self; 2] {
[Self::White, Self::Black]
}
#[inline]
pub const fn new(index: u8) -> Option<Self> {
match index {
0 => Some(Self::White),
1 => Some(Self::Black),
_ => None,
}
}
#[inline]
pub const fn index(self) -> u8 {
self as u8
}
#[inline]
pub(crate) const fn home_rank(self) -> Rank {
match self {
Self::White => Rank::First,
Self::Black => Rank::Eighth,
}
}
#[inline]
pub(crate) const fn forward(self) -> Direction {
match self {
Self::White => Direction::North,
Self::Black => Direction::South,
}
}
}
impl core::ops::Not for Color {
type Output = Self;
#[inline]
fn not(self) -> Self::Output {
match self {
Self::Black => Self::White,
Self::White => Self::Black,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum File {
A,
B,
C,
D,
E,
F,
G,
H,
}
container!(pub(crate), File, ByFile, 8);
impl File {
#[inline]
pub const fn all() -> [Self; 8] {
[
Self::A,
Self::B,
Self::C,
Self::D,
Self::E,
Self::F,
Self::G,
Self::H,
]
}
#[inline]
pub const fn new(index: u8) -> Option<Self> {
match index {
0 => Some(Self::A),
1 => Some(Self::B),
2 => Some(Self::C),
3 => Some(Self::D),
4 => Some(Self::E),
5 => Some(Self::F),
6 => Some(Self::G),
7 => Some(Self::H),
_ => None,
}
}
#[inline]
pub const fn index(self) -> u8 {
self as u8
}
#[inline]
pub const fn to_ascii(self) -> u8 {
self.index() + b'a'
}
#[inline]
pub const fn from_ascii(file: u8) -> Option<Self> {
if file >= b'a' && file <= b'h' {
Some(Self::new(file - b'a').unwrap())
} else {
None
}
}
#[inline]
pub const fn to_char(self) -> char {
self.to_ascii() as char
}
#[inline]
pub const fn from_char(file: char) -> Option<Self> {
if file as u32 <= 255 {
Self::from_ascii(file as u8)
} else {
None
}
}
#[inline]
pub const fn bitboard(self) -> Bitboard {
Bitboard(0x0101010101010101 << self.index())
}
}
impl core::fmt::Display for File {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.to_char())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Rank {
First,
Second,
Third,
Fourth,
Fifth,
Sixth,
Seventh,
Eighth,
}
container!(pub(crate), Rank, ByRank, 8);
impl Rank {
#[inline]
pub const fn all() -> [Self; 8] {
[
Self::First,
Self::Second,
Self::Third,
Self::Fourth,
Self::Fifth,
Self::Sixth,
Self::Seventh,
Self::Eighth,
]
}
#[inline]
pub const fn new(index: u8) -> Option<Self> {
match index {
0 => Some(Self::First),
1 => Some(Self::Second),
2 => Some(Self::Third),
3 => Some(Self::Fourth),
4 => Some(Self::Fifth),
5 => Some(Self::Sixth),
6 => Some(Self::Seventh),
7 => Some(Self::Eighth),
_ => None,
}
}
#[inline]
pub const fn index(self) -> u8 {
self as u8
}
#[inline]
pub const fn to_ascii(self) -> u8 {
self.index() + b'1'
}
#[inline]
pub const fn from_ascii(rank: u8) -> Option<Self> {
if rank >= b'1' && rank <= b'8' {
Some(Self::new(rank - b'1').unwrap())
} else {
None
}
}
#[inline]
pub const fn to_char(self) -> char {
self.to_ascii() as char
}
#[inline]
pub const fn from_char(rank: char) -> Option<Self> {
if rank as u32 <= 255 {
Self::from_ascii(rank as u8)
} else {
None
}
}
#[inline]
pub const fn bitboard(self) -> Bitboard {
Bitboard(0xFF << ((self as u64) << 3))
}
#[inline]
pub const fn mirror(self) -> Self {
Self::new(7 & !self.index()).unwrap()
}
}
impl core::fmt::Display for Rank {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.to_char())
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Square(u8);
container!(pub(crate), Square, BySquare, 64);
impl Square {
#[inline]
pub const fn all() -> [Self; 64] {
let mut res = [Self(0); 64];
let mut i = 0;
while i < 64 {
res[i as usize] = Self(i);
i += 1;
}
res
}
#[inline]
pub const fn new(index: u8) -> Option<Self> {
if index < 64 { Some(Self(index)) } else { None }
}
#[inline]
pub const unsafe fn new_unchecked(index: u8) -> Self {
debug_assert!(index < 64);
Self(index)
}
#[inline]
pub const fn index(self) -> u8 {
self.0
}
#[inline]
pub const fn from_ascii(s: &[u8]) -> Option<Self> {
match s {
[f, r] => {
let Some(file) = File::from_ascii(*f) else {
return None;
};
let Some(rank) = Rank::from_ascii(*r) else {
return None;
};
Some(Square::from_coords(file, rank))
}
_ => None,
}
}
#[inline]
pub const fn from_coords(file: File, rank: Rank) -> Self {
Self::new(((rank.index()) << 3) | file as u8).unwrap()
}
#[inline]
pub const fn file(self) -> File {
File::new(self.index() & 7).unwrap()
}
#[inline]
pub const fn rank(self) -> Rank {
Rank::new(self.index() >> 3).unwrap()
}
#[inline]
pub const fn bitboard(self) -> Bitboard {
Bitboard(1 << self.index())
}
#[inline]
pub const fn mirror(self) -> Self {
Self::new(self.index() ^ 0b111000).unwrap()
}
#[inline]
pub(crate) const fn trans(self, direction: Direction) -> Option<Self> {
self.bitboard().trans(direction).first()
}
#[inline]
pub(crate) const fn trans_unchecked(self, direction: Direction) -> Self {
let i = self.index();
let j = match direction {
Direction::East => i + 1,
Direction::NorthEast => i + 9,
Direction::North => i + 8,
Direction::NorthWest => i + 7,
Direction::SouthEast => i - 7,
Direction::South => i - 8,
Direction::SouthWest => i - 9,
Direction::West => i - 1,
};
Self::new(j & 63).unwrap()
}
}
impl core::fmt::Display for Square {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
use core::fmt::Write;
f.write_char(self.file().to_char())?;
f.write_char(self.rank().to_char())?;
Ok(())
}
}
impl core::fmt::Debug for Square {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{self}")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ParseSquareError;
impl core::fmt::Display for ParseSquareError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("invalid square syntax")
}
}
impl core::error::Error for ParseSquareError {}
impl core::str::FromStr for Square {
type Err = ParseSquareError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_ascii(s.as_bytes()).ok_or(ParseSquareError)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Role {
Pawn = 1,
Knight,
Bishop,
Rook,
Queen,
King,
}
impl Role {
#[inline]
pub const fn all() -> [Self; 6] {
[
Self::Pawn,
Self::Knight,
Self::Bishop,
Self::Rook,
Self::Queen,
Self::King,
]
}
#[inline]
pub const fn new(index: u8) -> Option<Self> {
match index {
0 => None,
1 => Some(Self::Pawn),
2 => Some(Self::Knight),
3 => Some(Self::Bishop),
4 => Some(Self::Rook),
5 => Some(Self::Queen),
6 => Some(Self::King),
_ => None,
}
}
#[inline]
pub(crate) const fn to_char_uppercase(self) -> char {
match self {
Self::Pawn => 'P',
Self::Knight => 'N',
Self::Bishop => 'B',
Self::Rook => 'R',
Self::Queen => 'Q',
Self::King => 'K',
}
}
#[inline]
pub(crate) const fn to_char_lowercase(self) -> char {
match self {
Self::Pawn => 'p',
Self::Knight => 'n',
Self::Bishop => 'b',
Self::Rook => 'r',
Self::Queen => 'q',
Self::King => 'k',
}
}
#[inline]
pub(crate) const fn from_ascii(x: u8) -> Option<Self> {
Some(match x {
b'p' | b'P' => Self::Pawn,
b'n' | b'N' => Self::Knight,
b'b' | b'B' => Self::Bishop,
b'r' | b'R' => Self::Rook,
b'q' | b'Q' => Self::Queen,
b'k' | b'K' => Self::King,
_ => return None,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct ByRole<T>(pub(crate) [T; 6]);
impl<T> ByRole<T> {
#[inline]
pub const fn new(values: [T; 6]) -> Self {
Self(values)
}
#[inline]
pub const fn get(&self, k: Role) -> &T {
&self.0[(k as usize) - 1]
}
#[allow(dead_code)]
#[inline]
pub const fn get_mut(&mut self, k: Role) -> &mut T {
&mut self.0[(k as usize) - 1]
}
}
impl<T: Copy + Default> Default for ByRole<T> {
fn default() -> Self {
Self::new([Default::default(); 6])
}
}
impl<T> ByRole<T>
where
T: Copy,
{
#[inline]
pub(crate) const fn pawn(&self) -> T {
*self.get(Role::Pawn)
}
#[inline]
pub(crate) const fn knight(&self) -> T {
*self.get(Role::Knight)
}
#[inline]
pub(crate) const fn bishop(&self) -> T {
*self.get(Role::Bishop)
}
#[inline]
pub(crate) const fn rook(&self) -> T {
*self.get(Role::Rook)
}
#[inline]
pub(crate) const fn queen(&self) -> T {
*self.get(Role::Queen)
}
#[inline]
pub(crate) const fn king(&self) -> T {
*self.get(Role::King)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Piece {
pub role: Role,
pub color: Color,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub(crate) enum Direction {
East,
NorthEast,
North,
NorthWest,
SouthEast,
South,
SouthWest,
West,
}
container!(pub(crate), Direction, ByDirection, 8);
impl Direction {
#[inline]
pub const fn index(self) -> u8 {
self as u8
}
#[inline]
pub const fn new(index: u8) -> Option<Self> {
match index {
0 => Some(Self::East),
1 => Some(Self::NorthEast),
2 => Some(Self::North),
3 => Some(Self::NorthWest),
4 => Some(Self::SouthEast),
5 => Some(Self::South),
6 => Some(Self::SouthWest),
7 => Some(Self::West),
_ => None,
}
}
}
impl core::ops::Not for Direction {
type Output = Self;
#[inline]
fn not(self) -> Self::Output {
match self {
Self::East => Self::West,
Self::NorthEast => Self::SouthWest,
Self::North => Self::South,
Self::NorthWest => Self::SouthEast,
Self::SouthEast => Self::NorthWest,
Self::South => Self::North,
Self::SouthWest => Self::NorthEast,
Self::West => Self::East,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Castle {
Short,
Long,
}
container!(pub(crate), Castle, ByCastle, 2);
impl Castle {
#[inline]
pub const fn all() -> [Self; 2] {
[Self::Short, Self::Long]
}
#[inline]
pub const fn new(index: u8) -> Option<Self> {
match index {
0 => Some(Self::Short),
1 => Some(Self::Long),
_ => None,
}
}
#[inline]
pub const fn index(self) -> u8 {
self as u8
}
#[inline]
pub(crate) const fn rook_origin_file(self) -> File {
match self {
Self::Short => File::H,
Self::Long => File::A,
}
}
}
#[inline]
pub(crate) const fn sq(s: &str) -> Square {
Square::from_ascii(s.as_bytes()).unwrap()
}