2022-07-16 14:05:49 +02:00
|
|
|
use super::Bitboard;
|
2022-07-18 11:05:38 +02:00
|
|
|
use crate::utils::static_assert;
|
2022-07-16 14:05:49 +02:00
|
|
|
|
|
|
|
/// An enum representing a singular rank on a chess board (i.e: the rows).
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
pub enum Rank {
|
|
|
|
First,
|
|
|
|
Second,
|
|
|
|
Third,
|
|
|
|
Fourth,
|
|
|
|
Fifth,
|
|
|
|
Sixth,
|
|
|
|
Seventh,
|
|
|
|
Eighth,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Rank {
|
|
|
|
const ALL: [Rank; 8] = [
|
|
|
|
Rank::First,
|
|
|
|
Rank::Second,
|
|
|
|
Rank::Third,
|
|
|
|
Rank::Fourth,
|
|
|
|
Rank::Fifth,
|
|
|
|
Rank::Sixth,
|
|
|
|
Rank::Seventh,
|
|
|
|
Rank::Eighth,
|
|
|
|
];
|
|
|
|
|
|
|
|
/// Iterate over all ranks in order.
|
|
|
|
pub fn iter() -> impl Iterator<Item = Rank> {
|
|
|
|
Self::ALL.iter().cloned()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Convert from a rank index into a [Rank] type.
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn from_index(index: usize) -> Self {
|
|
|
|
assert!(index < 8);
|
|
|
|
// SAFETY: we know the value is in-bounds
|
|
|
|
unsafe { Self::from_index_unchecked(index) }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Convert from a rank index into a [Rank] type, no bounds checking.
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// Should only be called with values that can be output by [Rank::index()].
|
|
|
|
#[inline(always)]
|
|
|
|
pub unsafe fn from_index_unchecked(index: usize) -> Self {
|
|
|
|
std::mem::transmute(index as u8)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the index of a given [Rank].
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn index(self) -> usize {
|
|
|
|
self as usize
|
|
|
|
}
|
|
|
|
|
2022-07-16 14:34:46 +02:00
|
|
|
/// Return the [Rank] one-row up, as seen from white's perspective. Wraps around the board.
|
|
|
|
pub fn up(self) -> Self {
|
|
|
|
// SAFETY: we know the value is in-bounds, through masking
|
|
|
|
unsafe { Self::from_index_unchecked(self.index().wrapping_add(1) & 7) }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the [Rank] one-row down, as seen from white's perspective. Wraps around the board.
|
|
|
|
pub fn down(self) -> Self {
|
|
|
|
// SAFETY: we know the value is in-bounds, through masking
|
|
|
|
unsafe { Self::from_index_unchecked(self.index().wrapping_sub(1) & 7) }
|
|
|
|
}
|
|
|
|
|
2022-07-16 14:05:49 +02:00
|
|
|
/// Turn a [Rank] into a [Bitboard] of all squares in that rank.
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn into_bitboard(self) -> Bitboard {
|
|
|
|
// SAFETY: we know the value is in-bounds
|
|
|
|
unsafe { *Bitboard::RANKS.get_unchecked(self.index()) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-18 11:05:38 +02:00
|
|
|
// Ensure that niche-optimization is in effect.
|
|
|
|
static_assert!(std::mem::size_of::<Option<Rank>>() == std::mem::size_of::<Rank>());
|
|
|
|
|
2022-07-16 14:05:49 +02:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn from_index() {
|
|
|
|
assert_eq!(Rank::from_index(0), Rank::First);
|
|
|
|
assert_eq!(Rank::from_index(1), Rank::Second);
|
|
|
|
assert_eq!(Rank::from_index(7), Rank::Eighth);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn index() {
|
|
|
|
assert_eq!(Rank::First.index(), 0);
|
|
|
|
assert_eq!(Rank::Second.index(), 1);
|
|
|
|
assert_eq!(Rank::Eighth.index(), 7);
|
|
|
|
}
|
|
|
|
|
2022-07-16 14:34:46 +02:00
|
|
|
#[test]
|
|
|
|
fn up() {
|
|
|
|
assert_eq!(Rank::First.up(), Rank::Second);
|
|
|
|
assert_eq!(Rank::Second.up(), Rank::Third);
|
|
|
|
assert_eq!(Rank::Eighth.up(), Rank::First);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn down() {
|
|
|
|
assert_eq!(Rank::First.down(), Rank::Eighth);
|
|
|
|
assert_eq!(Rank::Second.down(), Rank::First);
|
|
|
|
assert_eq!(Rank::Eighth.down(), Rank::Seventh);
|
|
|
|
}
|
|
|
|
|
2022-07-16 14:05:49 +02:00
|
|
|
#[test]
|
|
|
|
fn into_bitboard() {
|
|
|
|
assert_eq!(Rank::First.into_bitboard(), Bitboard::RANKS[0]);
|
|
|
|
assert_eq!(Rank::Second.into_bitboard(), Bitboard::RANKS[1]);
|
|
|
|
assert_eq!(Rank::Eighth.into_bitboard(), Bitboard::RANKS[7]);
|
|
|
|
}
|
|
|
|
}
|