#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Square(pub u8);
const SQUARE_NOTATIONS: &[&str] = &[
"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", ];
impl Square {
#[inline]
pub const fn from_rank_file(rank: u8, file: u8) -> Self {
Self(rank * 8 + file)
}
#[inline]
pub const fn rank(self) -> u8 {
self.0 / 8
}
#[inline]
pub const fn file(self) -> u8 {
self.0 % 8
}
#[inline]
pub const fn bitboard(self) -> u64 {
1u64 << self.0
}
#[inline]
pub const fn algebraic_notation(self) -> &'static str {
SQUARE_NOTATIONS[self.0 as usize]
}
pub fn from_algebraic_notation(notation: &str) -> Option<Self> {
if notation.len() != 2 {
return None;
}
let bytes = notation.as_bytes();
let file_char = bytes[0];
let rank_char = bytes[1];
let file = match file_char {
b'a'..=b'h' => file_char - b'a',
_ => return None,
};
let rank = match rank_char {
b'1'..=b'8' => rank_char - b'1',
_ => return None,
};
Some(Self::from_rank_file(rank, file))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Color {
White,
Black,
}
impl Color {
#[inline]
pub const fn opponent(self) -> Self {
match self {
Color::White => Color::Black,
Color::Black => Color::White,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PieceType {
Pawn = 0,
Knight = 1,
Bishop = 2,
Rook = 3,
Queen = 4,
King = 5,
}
impl PieceType {
#[inline]
pub const fn index(self) -> usize {
self as usize
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CastlingRights {
pub white_kingside: bool,
pub white_queenside: bool,
pub black_kingside: bool,
pub black_queenside: bool,
}
impl Default for CastlingRights {
fn default() -> Self {
Self {
white_kingside: true,
white_queenside: true,
black_kingside: true,
black_queenside: true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_algebraic_notation_valid_squares() {
let a1 = Square::from_algebraic_notation("a1").expect("a1 should parse");
assert_eq!(a1, Square(0));
assert_eq!(a1.rank(), 0);
assert_eq!(a1.file(), 0);
assert_eq!(a1.algebraic_notation(), "a1");
let h8 = Square::from_algebraic_notation("h8").expect("h8 should parse");
assert_eq!(h8, Square(63));
assert_eq!(h8.rank(), 7);
assert_eq!(h8.file(), 7);
assert_eq!(h8.algebraic_notation(), "h8");
let a8 = Square::from_algebraic_notation("a8").expect("a8 should parse");
assert_eq!(a8, Square(56));
assert_eq!(a8.rank(), 7);
assert_eq!(a8.file(), 0);
let h1 = Square::from_algebraic_notation("h1").expect("h1 should parse");
assert_eq!(h1, Square(7));
assert_eq!(h1.rank(), 0);
assert_eq!(h1.file(), 7);
}
#[test]
fn test_from_algebraic_notation_center_squares() {
let e4 = Square::from_algebraic_notation("e4").expect("e4 should parse");
assert_eq!(e4.rank(), 3); assert_eq!(e4.file(), 4); assert_eq!(e4.algebraic_notation(), "e4");
let d5 = Square::from_algebraic_notation("d5").expect("d5 should parse");
assert_eq!(d5.rank(), 4); assert_eq!(d5.file(), 3); assert_eq!(d5.algebraic_notation(), "d5");
let c3 = Square::from_algebraic_notation("c3").expect("c3 should parse");
assert_eq!(c3.rank(), 2); assert_eq!(c3.file(), 2); }
#[test]
fn test_from_algebraic_notation_all_files() {
for (file_index, file_letter) in "abcdefgh".chars().enumerate() {
let notation = format!("{}1", file_letter);
let sq = Square::from_algebraic_notation(¬ation)
.unwrap_or_else(|| panic!("{}1 should parse", file_letter));
assert_eq!(sq.file(), file_index as u8);
assert_eq!(sq.rank(), 0);
}
}
#[test]
fn test_from_algebraic_notation_all_ranks() {
for (rank_index, rank_digit) in "12345678".chars().enumerate() {
let notation = format!("a{}", rank_digit);
let sq = Square::from_algebraic_notation(¬ation)
.unwrap_or_else(|| panic!("a{} should parse", rank_digit));
assert_eq!(sq.rank(), rank_index as u8);
assert_eq!(sq.file(), 0);
}
}
#[test]
fn test_from_algebraic_notation_invalid_length() {
assert!(
Square::from_algebraic_notation("").is_none(),
"empty string should fail"
);
assert!(
Square::from_algebraic_notation("e").is_none(),
"single char should fail"
);
assert!(
Square::from_algebraic_notation("e45").is_none(),
"three chars should fail"
);
assert!(
Square::from_algebraic_notation("e4x").is_none(),
"invalid third char should fail"
);
}
#[test]
fn test_from_algebraic_notation_invalid_file() {
assert!(
Square::from_algebraic_notation("i4").is_none(),
"file i should fail"
);
assert!(
Square::from_algebraic_notation("z5").is_none(),
"file z should fail"
);
assert!(
Square::from_algebraic_notation("@4").is_none(),
"special char @ should fail"
);
assert!(
Square::from_algebraic_notation("A4").is_none(),
"uppercase A should fail"
);
assert!(
Square::from_algebraic_notation(" 4").is_none(),
"space should fail"
);
}
#[test]
fn test_from_algebraic_notation_invalid_rank() {
assert!(
Square::from_algebraic_notation("e9").is_none(),
"rank 9 should fail"
);
assert!(
Square::from_algebraic_notation("e0").is_none(),
"rank 0 should fail"
);
assert!(
Square::from_algebraic_notation("ex").is_none(),
"non-digit should fail"
);
assert!(
Square::from_algebraic_notation("eA").is_none(),
"uppercase letter should fail"
);
assert!(
Square::from_algebraic_notation("e ").is_none(),
"space should fail"
);
}
#[test]
fn test_from_algebraic_notation_roundtrip() {
let notations = vec![
"a1", "b2", "c3", "d4", "e5", "f6", "g7", "h8", "e4", "d5", "c6", "b7", "a8", "h1",
"g2", "f3",
];
for notation in notations {
let sq = Square::from_algebraic_notation(notation)
.unwrap_or_else(|| panic!("{} should parse", notation));
assert_eq!(
sq.algebraic_notation(),
notation,
"roundtrip failed for {}",
notation
);
}
}
#[test]
fn test_from_algebraic_notation_bitboard() {
let a1 = Square::from_algebraic_notation("a1").expect("a1 should parse");
assert_eq!(a1.bitboard(), 1u64);
let b1 = Square::from_algebraic_notation("b1").expect("b1 should parse");
assert_eq!(b1.bitboard(), 2u64);
let a2 = Square::from_algebraic_notation("a2").expect("a2 should parse");
assert_eq!(a2.bitboard(), 1u64 << 8);
let h8 = Square::from_algebraic_notation("h8").expect("h8 should parse");
assert_eq!(h8.bitboard(), 1u64 << 63); }
#[test]
fn test_from_algebraic_notation_equivalence() {
let sq_from_notation = Square::from_algebraic_notation("e4").expect("e4 should parse");
let sq_from_rank_file = Square::from_rank_file(3, 4);
assert_eq!(sq_from_notation, sq_from_rank_file);
assert_eq!(sq_from_notation.rank(), sq_from_rank_file.rank());
assert_eq!(sq_from_notation.file(), sq_from_rank_file.file());
}
}