1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
use core::convert::TryInto;
use core::str::FromStr;
use crate::*;
macro_rules! define_square_with_docs {
($($square:ident),*) => {
crate::helpers::simple_enum! {
/// A square on a chessboard.
/// Squares are ordered in rank-major order (A1, B1, C1, ... H8).
#[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,
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
}
crate::helpers::simple_error! {
/// The value was not a valid [`Square`].
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())
}
}
impl Square {
/// Make a square from a file and a rank.
/// # Examples
/// ```
/// # use cozy_chess_types::*;
/// assert_eq!(Square::new(File::A, Rank::First), Square::A1);
/// ```
#[inline(always)]
pub const fn new(file: File, rank: Rank) -> Self {
Self::index_const(((rank as usize) << 3) | file as usize)
}
/// Get the file of this square.
/// # Examples
/// ```
/// # use cozy_chess_types::*;
/// assert_eq!(Square::A1.file(), File::A);
/// ```
#[inline(always)]
pub const fn file(self) -> File {
File::index_const(self as usize & 0b000111)
}
/// Get the rank of this square.
/// # Examples
/// ```
/// # use cozy_chess_types::*;
/// assert_eq!(Square::A1.rank(), Rank::First);
/// ```
#[inline(always)]
pub const fn rank(self) -> Rank {
Rank::index_const(self as usize >> 3)
}
/// Get a bitboard with this square set.
/// ```
/// # use cozy_chess_types::*;
/// assert_eq!(Square::B2.bitboard(), bitboard! {
/// . . . . . . . .
/// . . . . . . . .
/// . . . . . . . .
/// . . . . . . . .
/// . . . . . . . .
/// . . . . . . . .
/// . X . . . . . .
/// . . . . . . . .
/// });
/// ```
#[inline(always)]
pub const fn bitboard(self) -> BitBoard {
BitBoard(1 << self as u8)
}
/// Offsets the square towards the top right.
/// # Panics
/// Panic if the offset would put the square out of bounds.
/// See [`Square::try_offset`] for a non-panicking variant.
/// # Examples
/// ```
/// # use cozy_chess_types::*;
/// assert_eq!(Square::A1.offset(1, 2), Square::B3);
/// ```
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 would put square out of bounds.")
}
}
/// Non-panicking version of [`Square::offset`].
/// # Errors
/// See [`Square::offset`]'s panics.
#[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 >= 8 || rank_index < 0 || rank_index >= 8 {
return None;
}
let file = File::index_const(file_index as usize);
let rank = Rank::index_const(rank_index as usize);
Some(Square::new(file, rank))
}
/// Flip the file of this square.
/// # Examples
/// ```
/// # use cozy_chess_types::*;
/// assert_eq!(Square::A1.flip_file(), Square::H1);
/// ```
#[inline(always)]
pub const fn flip_file(self) -> Self {
Self::index_const(self as usize ^ 0b000111)
}
/// Flip the rank of this square.
/// # Examples
/// ```
/// # use cozy_chess_types::*;
/// assert_eq!(Square::A1.flip_rank(), Square::A8);
/// ```
#[inline(always)]
pub const fn flip_rank(self) -> Self {
Self::index_const(self as usize ^ 0b111000)
}
/// Get a square relative to some color.
/// This flips the square if viewing from black's perspective.
/// # Examples
/// ```
/// # use cozy_chess_types::*;
/// assert_eq!(Square::A1.relative_to(Color::White), Square::A1);
/// assert_eq!(Square::A1.relative_to(Color::Black), Square::A8);
/// ```
#[inline(always)]
pub const fn relative_to(self, color: Color) -> Self {
if let Color::White = color {
self
} else {
self.flip_rank()
}
}
}