use core::convert::TryInto;
use core::str::FromStr;
use crate::*;
macro_rules! define_square_with_docs {
($($square:ident),*) => {
crate::helpers::simple_enum! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Square {
$(
#[doc = concat!("The ", stringify!($square), " square.")]
$square
),*
}
}
}
}
define_square_with_docs! {
A1, B1, C1, D1, E1, F1, G1, H1, I1,
A2, B2, C2, D2, E2, F2, G2, H2, I2,
A3, B3, C3, D3, E3, F3, G3, H3, I3,
A4, B4, C4, D4, E4, F4, G4, H4, I4,
A5, B5, C5, D5, E5, F5, G5, H5, I5,
A6, B6, C6, D6, E6, F6, G6, H6, I6,
A7, B7, C7, D7, E7, F7, G7, H7, I7,
A8, B8, C8, D8, E8, F8, G8, H8, I8,
A9, B9, C9, D9, E9, F9, G9, H9, I9
}
crate::helpers::simple_error! {
pub struct SquareParseError = "The value was not a valid Square.";
}
impl FromStr for Square {
type Err = SquareParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut chars = s.chars();
let file = chars
.next()
.and_then(|c| c.try_into().ok())
.ok_or(SquareParseError)?;
let rank = chars
.next()
.and_then(|c| c.try_into().ok())
.ok_or(SquareParseError)?;
if chars.next().is_some() {
return Err(SquareParseError);
}
Ok(Square::new(file, rank))
}
}
impl core::fmt::Display for Square {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
write!(f, "{}{}", self.file(), self.rank())
}
}
const NEG_MASK: u128 = 0x100401004010040100401;
const POS_MASK: u128 = 0x1010101010101010100;
const NEGD: BitBoard = BitBoard::new(NEG_MASK);
const POSD: BitBoard = BitBoard::new(POS_MASK);
pub const POS_DIA: [BitBoard; 17] = [
POSD.shr(8),
POSD.shr(7),
POSD.shr(6),
POSD.shr(5),
POSD.shr(4),
POSD.shr(3),
POSD.shr(2),
POSD.shr(1),
POSD,
POSD.shl(1),
POSD.shl(2),
POSD.shl(3),
POSD.shl(4),
POSD.shl(5),
POSD.shl(6),
POSD.shl(7),
POSD.shl(8),
];
pub const NEG_DIA: [BitBoard; 17] = [
NEGD.shr(8),
NEGD.shr(7),
NEGD.shr(6),
NEGD.shr(5),
NEGD.shr(4),
NEGD.shr(3),
NEGD.shr(2),
NEGD.shr(1),
NEGD,
NEGD.shl(1),
NEGD.shl(2),
NEGD.shl(3),
NEGD.shl(4),
NEGD.shl(5),
NEGD.shl(6),
NEGD.shl(7),
NEGD.shl(8),
];
impl Square {
#[inline(always)]
pub const fn new(file: File, rank: Rank) -> Self {
Self::index_const((file as usize) * 9 + (rank as usize))
}
#[inline(always)]
pub const fn file(self) -> File {
File::index_const(self as usize / 9)
}
#[inline(always)]
pub const fn rank(self) -> Rank {
Rank::index_const(self as usize % 9)
}
#[inline(always)]
pub const fn bitboard(self) -> BitBoard {
BitBoard(1 << self as usize)
}
#[inline(always)]
pub const fn up_diagonal(self) -> BitBoard {
let rank = self as usize % 9;
let file = self as usize / 9;
NEG_DIA[8 + rank - file]
}
#[inline(always)]
pub const fn down_diagonal(self) -> BitBoard {
let rank = self as usize % 9;
let file = self as usize / 9;
POS_DIA[file + rank]
}
pub const fn offset(self, file_offset: i8, rank_offset: i8) -> Square {
if let Some(sq) = self.try_offset(file_offset, rank_offset) {
sq
} else {
panic!("Offset puts square out of bounds");
}
}
#[inline(always)]
pub const fn try_offset(self, file_offset: i8, rank_offset: i8) -> Option<Square> {
let file_index = self.file() as i8 + file_offset;
let rank_index = self.rank() as i8 + rank_offset;
if file_index < 0 || file_index >= 9 || rank_index < 0 || rank_index >= 9 {
return None;
}
Some(Square::new(
File::index_const(file_index as usize),
Rank::index_const(rank_index as usize),
))
}
#[inline(always)]
pub const fn flip_file(self) -> Self {
Self::new(self.file().flip(), self.rank())
}
#[inline(always)]
pub const fn flip_rank(self) -> Self {
Self::new(self.file(), self.rank().flip())
}
#[inline(always)]
pub const fn flip(self) -> Self {
Self::new(self.file().flip(), self.rank().flip())
}
#[inline(always)]
pub const fn relative_to(self, color: Color) -> Self {
if let Color::Black = color {
self
} else {
Self::new(self.file().flip(), self.rank().flip())
}
}
}