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 as usize]
}
#[inline]
pub const fn get_mut(&mut self, k: $a) -> &mut T {
&mut self.0[k as usize]
}
#[inline]
pub fn with<F>(f: F) -> Self
where
F: FnMut($a) -> T,
{
Self($a::all().map(f))
}
}
impl<T: Copy + Default> Default for $b<T> {
fn default() -> Self {
Self::new([Default::default(); $n])
}
}
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(u8)]
pub enum Color {
#[default]
White,
Black,
}
container!(pub, 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(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, 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 to_ascii(self) -> u8 {
self as u8 + 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 as u8))
}
}
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, 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 to_ascii(self) -> u8 {
self as u8 + 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 as u8)).unwrap()
}
}
impl core::fmt::Display for Rank {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.to_char())
}
}
#[test]
fn rank_mirror() {
for (a, b) in Rank::all().into_iter().zip(Rank::all().into_iter().rev()) {
assert_eq!(a.mirror(), b);
assert_eq!(b, a.mirror());
}
}
#[rustfmt::skip]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Square{
A1, B1, C1, D1, E1, F1, G1, H1,
A2, B2, C2, D2, E2, F2, G2, H2,
A3, B3, C3, D3, E3, F3, G3, H3,
A4, B4, C4, D4, E4, F4, G4, H4,
A5, B5, C5, D5, E5, F5, G5, H5,
A6, B6, C6, D6, E6, F6, G6, H6,
A7, B7, C7, D7, E7, F7, G7, H7,
A8, B8, C8, D8, E8, F8, G8, H8,
}
container!(pub, Square, BySquare, 64);
impl Square {
#[rustfmt::skip]
#[inline]
pub const fn all() -> [Self; 64] {
[
Self::A1, Self::B1, Self::C1, Self::D1, Self::E1, Self::F1, Self::G1, Self::H1,
Self::A2, Self::B2, Self::C2, Self::D2, Self::E2, Self::F2, Self::G2, Self::H2,
Self::A3, Self::B3, Self::C3, Self::D3, Self::E3, Self::F3, Self::G3, Self::H3,
Self::A4, Self::B4, Self::C4, Self::D4, Self::E4, Self::F4, Self::G4, Self::H4,
Self::A5, Self::B5, Self::C5, Self::D5, Self::E5, Self::F5, Self::G5, Self::H5,
Self::A6, Self::B6, Self::C6, Self::D6, Self::E6, Self::F6, Self::G6, Self::H6,
Self::A7, Self::B7, Self::C7, Self::D7, Self::E7, Self::F7, Self::G7, Self::H7,
Self::A8, Self::B8, Self::C8, Self::D8, Self::E8, Self::F8, Self::G8, Self::H8,
]
}
#[rustfmt::skip]
#[inline]
pub const fn new(index: u8) -> Option<Self> {
match index {
0 => Some(Self::A1), 1 => Some(Self::B1), 2 => Some(Self::C1), 3 => Some(Self::D1), 4 => Some(Self::E1), 5 => Some(Self::F1), 6 => Some(Self::G1), 7 => Some(Self::H1),
8 => Some(Self::A2), 9 => Some(Self::B2), 10 => Some(Self::C2), 11 => Some(Self::D2), 12 => Some(Self::E2), 13 => Some(Self::F2), 14 => Some(Self::G2), 15 => Some(Self::H2),
16 => Some(Self::A3), 17 => Some(Self::B3), 18 => Some(Self::C3), 19 => Some(Self::D3), 20 => Some(Self::E3), 21 => Some(Self::F3), 22 => Some(Self::G3), 23 => Some(Self::H3),
24 => Some(Self::A4), 25 => Some(Self::B4), 26 => Some(Self::C4), 27 => Some(Self::D4), 28 => Some(Self::E4), 29 => Some(Self::F4), 30 => Some(Self::G4), 31 => Some(Self::H4),
32 => Some(Self::A5), 33 => Some(Self::B5), 34 => Some(Self::C5), 35 => Some(Self::D5), 36 => Some(Self::E5), 37 => Some(Self::F5), 38 => Some(Self::G5), 39 => Some(Self::H5),
40 => Some(Self::A6), 41 => Some(Self::B6), 42 => Some(Self::C6), 43 => Some(Self::D6), 44 => Some(Self::E6), 45 => Some(Self::F6), 46 => Some(Self::G6), 47 => Some(Self::H6),
48 => Some(Self::A7), 49 => Some(Self::B7), 50 => Some(Self::C7), 51 => Some(Self::D7), 52 => Some(Self::E7), 53 => Some(Self::F7), 54 => Some(Self::G7), 55 => Some(Self::H7),
56 => Some(Self::A8), 57 => Some(Self::B8), 58 => Some(Self::C8), 59 => Some(Self::D8), 60 => Some(Self::E8), 61 => Some(Self::F8), 62 => Some(Self::G8), 63 => Some(Self::H8),
_ => None,
}
}
#[inline]
pub const unsafe fn new_unchecked(index: u8) -> Self {
debug_assert!(index < 64);
unsafe { core::mem::transmute(index) }
}
#[inline]
pub const fn from_coords(file: File, rank: Rank) -> Self {
Self::new(((rank as u8) << 3) | file as u8).unwrap()
}
#[inline]
pub const fn file(self) -> File {
File::new((self as u8) & 7).unwrap()
}
#[inline]
pub const fn rank(self) -> Rank {
Rank::new((self as u8) >> 3).unwrap()
}
#[inline]
pub const fn bitboard(self) -> Bitboard {
Bitboard(1 << self as u8)
}
#[inline]
pub const fn mirror(self) -> Self {
Self::new((self as u8) ^ 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 as u8;
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(())
}
}
#[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> {
(|| match s.as_bytes() {
[f, r] => Some(Self::from_coords(
File::from_ascii(*f)?,
Rank::from_ascii(*r)?,
)),
_ => None,
})()
.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 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]
}
#[inline]
pub const fn get_mut(&mut self, k: Role) -> &mut T {
&mut self.0[(k as usize) - 1]
}
#[inline]
pub fn with<F>(f: F) -> Self
where
F: FnMut(Role) -> T,
{
Self(Role::all().map(f))
}
}
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 all() -> [Self; 8] {
[
Self::East,
Self::NorthEast,
Self::North,
Self::NorthWest,
Self::SouthEast,
Self::South,
Self::SouthWest,
Self::West,
]
}
#[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, 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(crate) const fn rook_origin_file(self) -> File {
match self {
Self::Short => File::H,
Self::Long => File::A,
}
}
}