Skip to content

Commit

Permalink
Store Square with a NonZeroU8 to allow niche optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
bsamseth committed Oct 31, 2024
1 parent 091230d commit cd7c3e6
Showing 1 changed file with 85 additions and 81 deletions.
166 changes: 85 additions & 81 deletions goldchess/src/square.rs
Original file line number Diff line number Diff line change
@@ -1,117 +1,120 @@
use std::str::FromStr;
use std::{num::NonZeroU8, str::FromStr};

use crate::{Bitboard, Error, File, Rank, Result};

/// A square on the chessboard.
///
/// Squares are indexed from 0 to 63, where `A1=0`, `B1=1`, ..., `H8=63`.
/// Squares are indexed from 1 to 64, where `A1=1`, `B1=2`, ..., `H8=64`. Yes, that's right,
/// one-based indexing. This enables the niche optimization, where [`Option<Square>`] is still
/// only the size of a single `u8`.
///
/// You can get [`File`] and [`Rank`] from a square, and combining squares with the `|` operator
/// yields a [`Bitboard`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Square(pub u8);
pub struct Square(NonZeroU8);

impl Square {
/// Create a new square from a `u8` without checking if it is a valid square index.
///
/// # Safety
/// The input must be a valid square index, i.e. 0 <= sq < 64.
/// The input must be a valid square index, i.e. 1 <= sq <= 64.
#[must_use]
pub unsafe fn new_unchecked(sq: u8) -> Self {
Self(sq)
pub const unsafe fn new_unchecked(sq: u8) -> Self {
Self(NonZeroU8::new_unchecked(sq))
}

/// Create a new square from a `u8`.
///
/// # Errors
/// Returns an error if the input is not a valid square index.
pub fn new(sq: u8) -> Result<Self, Error> {
if sq < 64 {
Ok(Self(sq))
pub const fn new(sq: u8) -> Result<Self, Error> {
if sq > 0 && sq <= 64 {
// SAFETY: sq is in 1..=64, so it is safe to create.
Ok(unsafe { Self::new_unchecked(sq) })
} else {
Err(Error::InvalidSquare(sq))
}
}

#[must_use]
pub fn file(self) -> File {
pub const fn file(self) -> File {
// SAFETY: The modulo operation ensures that the result is always in 0..8.
unsafe { File::new_unchecked(self.0 % 8) }
unsafe { File::new_unchecked((self.0.get() - 1) % 8) }
}

#[must_use]
pub fn rank(self) -> Rank {
// SAFETY: self is a valid square index, so self.0 / 8 is a valid rank index.
unsafe { Rank::new_unchecked(self.0 / 8) }
pub const fn rank(self) -> Rank {
// SAFETY: self is a valid square index, so (self.0 - 1) / 8 is a valid rank index.
unsafe { Rank::new_unchecked((self.0.get() - 1) / 8) }
}
}

#[allow(dead_code)]
impl Square {
pub const A1: Square = Square(0);
pub const B1: Square = Square(1);
pub const C1: Square = Square(2);
pub const D1: Square = Square(3);
pub const E1: Square = Square(4);
pub const F1: Square = Square(5);
pub const G1: Square = Square(6);
pub const H1: Square = Square(7);
pub const A2: Square = Square(8);
pub const B2: Square = Square(9);
pub const C2: Square = Square(10);
pub const D2: Square = Square(11);
pub const E2: Square = Square(12);
pub const F2: Square = Square(13);
pub const G2: Square = Square(14);
pub const H2: Square = Square(15);
pub const A3: Square = Square(16);
pub const B3: Square = Square(17);
pub const C3: Square = Square(18);
pub const D3: Square = Square(19);
pub const E3: Square = Square(20);
pub const F3: Square = Square(21);
pub const G3: Square = Square(22);
pub const H3: Square = Square(23);
pub const A4: Square = Square(24);
pub const B4: Square = Square(25);
pub const C4: Square = Square(26);
pub const D4: Square = Square(27);
pub const E4: Square = Square(28);
pub const F4: Square = Square(29);
pub const G4: Square = Square(30);
pub const H4: Square = Square(31);
pub const A5: Square = Square(32);
pub const B5: Square = Square(33);
pub const C5: Square = Square(34);
pub const D5: Square = Square(35);
pub const E5: Square = Square(36);
pub const F5: Square = Square(37);
pub const G5: Square = Square(38);
pub const H5: Square = Square(39);
pub const A6: Square = Square(40);
pub const B6: Square = Square(41);
pub const C6: Square = Square(42);
pub const D6: Square = Square(43);
pub const E6: Square = Square(44);
pub const F6: Square = Square(45);
pub const G6: Square = Square(46);
pub const H6: Square = Square(47);
pub const A7: Square = Square(48);
pub const B7: Square = Square(49);
pub const C7: Square = Square(50);
pub const D7: Square = Square(51);
pub const E7: Square = Square(52);
pub const F7: Square = Square(53);
pub const G7: Square = Square(54);
pub const H7: Square = Square(55);
pub const A8: Square = Square(56);
pub const B8: Square = Square(57);
pub const C8: Square = Square(58);
pub const D8: Square = Square(59);
pub const E8: Square = Square(60);
pub const F8: Square = Square(61);
pub const G8: Square = Square(62);
pub const H8: Square = Square(63);
pub const A1: Square = unsafe { Square::new_unchecked(1) };
pub const B1: Square = unsafe { Square::new_unchecked(2) };
pub const C1: Square = unsafe { Square::new_unchecked(3) };
pub const D1: Square = unsafe { Square::new_unchecked(4) };
pub const E1: Square = unsafe { Square::new_unchecked(5) };
pub const F1: Square = unsafe { Square::new_unchecked(6) };
pub const G1: Square = unsafe { Square::new_unchecked(7) };
pub const H1: Square = unsafe { Square::new_unchecked(8) };
pub const A2: Square = unsafe { Square::new_unchecked(9) };
pub const B2: Square = unsafe { Square::new_unchecked(10) };
pub const C2: Square = unsafe { Square::new_unchecked(11) };
pub const D2: Square = unsafe { Square::new_unchecked(12) };
pub const E2: Square = unsafe { Square::new_unchecked(13) };
pub const F2: Square = unsafe { Square::new_unchecked(14) };
pub const G2: Square = unsafe { Square::new_unchecked(15) };
pub const H2: Square = unsafe { Square::new_unchecked(16) };
pub const A3: Square = unsafe { Square::new_unchecked(17) };
pub const B3: Square = unsafe { Square::new_unchecked(18) };
pub const C3: Square = unsafe { Square::new_unchecked(19) };
pub const D3: Square = unsafe { Square::new_unchecked(20) };
pub const E3: Square = unsafe { Square::new_unchecked(21) };
pub const F3: Square = unsafe { Square::new_unchecked(22) };
pub const G3: Square = unsafe { Square::new_unchecked(23) };
pub const H3: Square = unsafe { Square::new_unchecked(24) };
pub const A4: Square = unsafe { Square::new_unchecked(25) };
pub const B4: Square = unsafe { Square::new_unchecked(26) };
pub const C4: Square = unsafe { Square::new_unchecked(27) };
pub const D4: Square = unsafe { Square::new_unchecked(28) };
pub const E4: Square = unsafe { Square::new_unchecked(29) };
pub const F4: Square = unsafe { Square::new_unchecked(30) };
pub const G4: Square = unsafe { Square::new_unchecked(31) };
pub const H4: Square = unsafe { Square::new_unchecked(32) };
pub const A5: Square = unsafe { Square::new_unchecked(33) };
pub const B5: Square = unsafe { Square::new_unchecked(34) };
pub const C5: Square = unsafe { Square::new_unchecked(35) };
pub const D5: Square = unsafe { Square::new_unchecked(36) };
pub const E5: Square = unsafe { Square::new_unchecked(37) };
pub const F5: Square = unsafe { Square::new_unchecked(38) };
pub const G5: Square = unsafe { Square::new_unchecked(39) };
pub const H5: Square = unsafe { Square::new_unchecked(40) };
pub const A6: Square = unsafe { Square::new_unchecked(41) };
pub const B6: Square = unsafe { Square::new_unchecked(42) };
pub const C6: Square = unsafe { Square::new_unchecked(43) };
pub const D6: Square = unsafe { Square::new_unchecked(44) };
pub const E6: Square = unsafe { Square::new_unchecked(45) };
pub const F6: Square = unsafe { Square::new_unchecked(46) };
pub const G6: Square = unsafe { Square::new_unchecked(47) };
pub const H6: Square = unsafe { Square::new_unchecked(48) };
pub const A7: Square = unsafe { Square::new_unchecked(49) };
pub const B7: Square = unsafe { Square::new_unchecked(50) };
pub const C7: Square = unsafe { Square::new_unchecked(51) };
pub const D7: Square = unsafe { Square::new_unchecked(52) };
pub const E7: Square = unsafe { Square::new_unchecked(53) };
pub const F7: Square = unsafe { Square::new_unchecked(54) };
pub const G7: Square = unsafe { Square::new_unchecked(55) };
pub const H7: Square = unsafe { Square::new_unchecked(56) };
pub const A8: Square = unsafe { Square::new_unchecked(57) };
pub const B8: Square = unsafe { Square::new_unchecked(58) };
pub const C8: Square = unsafe { Square::new_unchecked(59) };
pub const D8: Square = unsafe { Square::new_unchecked(60) };
pub const E8: Square = unsafe { Square::new_unchecked(61) };
pub const F8: Square = unsafe { Square::new_unchecked(62) };
pub const G8: Square = unsafe { Square::new_unchecked(63) };
pub const H8: Square = unsafe { Square::new_unchecked(64) };
pub const ALL_SQUARES: [Square; 64] = [
Self::A1,
Self::B1,
Expand Down Expand Up @@ -188,7 +191,7 @@ impl std::fmt::Display for Square {

impl From<Square> for u8 {
fn from(sq: Square) -> u8 {
sq.0
sq.0.get()
}
}

Expand Down Expand Up @@ -222,7 +225,7 @@ impl std::ops::BitOr for Square {

impl From<Square> for Bitboard {
fn from(sq: Square) -> Bitboard {
Bitboard(1 << sq.0)
Bitboard(1 << (sq.0.get() - 1))
}
}

Expand All @@ -232,6 +235,7 @@ impl FromStr for Square {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let file = File::from_str(&s[0..1])?;
let rank = Rank::from_str(&s[1..2])?;
Ok(Self(rank.0 * 8 + file.0))
// SAFETY: file and rank are valid, so the square is valid.
Ok(unsafe { Self::new_unchecked(rank.0 * 8 + file.0 + 1) })
}
}

0 comments on commit cd7c3e6

Please sign in to comment.