Compare commits
6 commits
fcf309fad4
...
aa3b464bb8
Author | SHA1 | Date | |
---|---|---|---|
Bruno BELANYI | aa3b464bb8 | ||
Bruno BELANYI | c5949fb01e | ||
Bruno BELANYI | 562182d26b | ||
Bruno BELANYI | 63c5d2dc34 | ||
Bruno BELANYI | e0c667d090 | ||
Bruno BELANYI | 3a6c4113fc |
196
src/board/castle_rights.rs
Normal file
196
src/board/castle_rights.rs
Normal file
|
@ -0,0 +1,196 @@
|
|||
use super::{Bitboard, Color, File, Square};
|
||||
|
||||
/// Current castle rights for a player.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum CastleRights {
|
||||
/// No castling allowed.
|
||||
NoSide,
|
||||
/// King-side castling only.
|
||||
KingSide,
|
||||
/// Queen-side castling only.
|
||||
QueenSide,
|
||||
/// Either side allowed.
|
||||
BothSides,
|
||||
}
|
||||
|
||||
impl CastleRights {
|
||||
/// Convert from a castle rights index into a [CastleRights] type.
|
||||
#[inline(always)]
|
||||
pub fn from_index(index: usize) -> Self {
|
||||
assert!(index < 4);
|
||||
// SAFETY: we know the value is in-bounds
|
||||
unsafe { Self::from_index_unchecked(index) }
|
||||
}
|
||||
|
||||
/// Convert from a castle rights index into a [CastleRights] type, no bounds checking.
|
||||
#[inline(always)]
|
||||
pub unsafe fn from_index_unchecked(index: usize) -> Self {
|
||||
std::mem::transmute(index as u8)
|
||||
}
|
||||
|
||||
/// Return the index of a given [CastleRights].
|
||||
#[inline(always)]
|
||||
pub fn index(self) -> usize {
|
||||
self as usize
|
||||
}
|
||||
|
||||
/// Can the player castle king-side.
|
||||
#[inline(always)]
|
||||
pub fn has_king_side(self) -> bool {
|
||||
(self.index() & 1) != 0
|
||||
}
|
||||
|
||||
/// Can the player castle king-side.
|
||||
#[inline(always)]
|
||||
pub fn has_queen_side(self) -> bool {
|
||||
(self.index() & 2) != 0
|
||||
}
|
||||
|
||||
/// Remove king-side castling rights.
|
||||
#[inline(always)]
|
||||
pub fn without_king_side(self) -> Self {
|
||||
self.remove(Self::KingSide)
|
||||
}
|
||||
|
||||
/// Remove queen-side castling rights.
|
||||
#[inline(always)]
|
||||
pub fn without_queen_side(self) -> Self {
|
||||
self.remove(Self::QueenSide)
|
||||
}
|
||||
|
||||
/// Remove some [CastleRights], and return the resulting [CastleRights].
|
||||
#[inline(always)]
|
||||
pub fn remove(self, to_remove: CastleRights) -> Self {
|
||||
// SAFETY: we know the value is in-bounds
|
||||
unsafe { Self::from_index_unchecked(self.index() & !to_remove.index()) }
|
||||
}
|
||||
|
||||
/// Which rooks have not been moved for a given [CastleRights] and [Color].
|
||||
#[inline(always)]
|
||||
pub fn unmoved_rooks(self, color: Color) -> Bitboard {
|
||||
let rank = color.first_rank();
|
||||
|
||||
let king_side_square = Square::new(File::H, rank);
|
||||
let queen_side_square = Square::new(File::A, rank);
|
||||
|
||||
match self {
|
||||
Self::NoSide => Bitboard::EMPTY,
|
||||
Self::KingSide => king_side_square.into_bitboard(),
|
||||
Self::QueenSide => queen_side_square.into_bitboard(),
|
||||
Self::BothSides => king_side_square | queen_side_square,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn from_index() {
|
||||
assert_eq!(CastleRights::from_index(0), CastleRights::NoSide);
|
||||
assert_eq!(CastleRights::from_index(1), CastleRights::KingSide);
|
||||
assert_eq!(CastleRights::from_index(2), CastleRights::QueenSide);
|
||||
assert_eq!(CastleRights::from_index(3), CastleRights::BothSides);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn index() {
|
||||
assert_eq!(CastleRights::NoSide.index(), 0);
|
||||
assert_eq!(CastleRights::KingSide.index(), 1);
|
||||
assert_eq!(CastleRights::QueenSide.index(), 2);
|
||||
assert_eq!(CastleRights::BothSides.index(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_kingside() {
|
||||
assert!(!CastleRights::NoSide.has_king_side());
|
||||
assert!(!CastleRights::QueenSide.has_king_side());
|
||||
assert!(CastleRights::KingSide.has_king_side());
|
||||
assert!(CastleRights::BothSides.has_king_side());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_queenside() {
|
||||
assert!(!CastleRights::NoSide.has_queen_side());
|
||||
assert!(!CastleRights::KingSide.has_queen_side());
|
||||
assert!(CastleRights::QueenSide.has_queen_side());
|
||||
assert!(CastleRights::BothSides.has_queen_side());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn without_king_side() {
|
||||
assert_eq!(
|
||||
CastleRights::NoSide.without_king_side(),
|
||||
CastleRights::NoSide
|
||||
);
|
||||
assert_eq!(
|
||||
CastleRights::KingSide.without_king_side(),
|
||||
CastleRights::NoSide
|
||||
);
|
||||
assert_eq!(
|
||||
CastleRights::QueenSide.without_king_side(),
|
||||
CastleRights::QueenSide
|
||||
);
|
||||
assert_eq!(
|
||||
CastleRights::BothSides.without_king_side(),
|
||||
CastleRights::QueenSide
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn without_queen_side() {
|
||||
assert_eq!(
|
||||
CastleRights::NoSide.without_queen_side(),
|
||||
CastleRights::NoSide
|
||||
);
|
||||
assert_eq!(
|
||||
CastleRights::QueenSide.without_queen_side(),
|
||||
CastleRights::NoSide
|
||||
);
|
||||
assert_eq!(
|
||||
CastleRights::KingSide.without_queen_side(),
|
||||
CastleRights::KingSide
|
||||
);
|
||||
assert_eq!(
|
||||
CastleRights::BothSides.without_queen_side(),
|
||||
CastleRights::KingSide
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unmoved_rooks() {
|
||||
assert_eq!(
|
||||
CastleRights::NoSide.unmoved_rooks(Color::White),
|
||||
Bitboard::EMPTY
|
||||
);
|
||||
assert_eq!(
|
||||
CastleRights::NoSide.unmoved_rooks(Color::Black),
|
||||
Bitboard::EMPTY
|
||||
);
|
||||
assert_eq!(
|
||||
CastleRights::KingSide.unmoved_rooks(Color::White),
|
||||
Square::H1.into_bitboard()
|
||||
);
|
||||
assert_eq!(
|
||||
CastleRights::KingSide.unmoved_rooks(Color::Black),
|
||||
Square::H8.into_bitboard()
|
||||
);
|
||||
assert_eq!(
|
||||
CastleRights::QueenSide.unmoved_rooks(Color::White),
|
||||
Square::A1.into_bitboard()
|
||||
);
|
||||
assert_eq!(
|
||||
CastleRights::QueenSide.unmoved_rooks(Color::Black),
|
||||
Square::A8.into_bitboard()
|
||||
);
|
||||
assert_eq!(
|
||||
CastleRights::BothSides.unmoved_rooks(Color::White),
|
||||
Square::A1 | Square::H1
|
||||
);
|
||||
assert_eq!(
|
||||
CastleRights::BothSides.unmoved_rooks(Color::Black),
|
||||
Square::A8 | Square::H8
|
||||
);
|
||||
}
|
||||
}
|
102
src/board/color.rs
Normal file
102
src/board/color.rs
Normal file
|
@ -0,0 +1,102 @@
|
|||
use super::Rank;
|
||||
|
||||
/// An enum representing the color of a player.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Color {
|
||||
White,
|
||||
Black,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
/// Convert from a file index into a [Color] type.
|
||||
#[inline(always)]
|
||||
pub fn from_index(index: usize) -> Self {
|
||||
assert!(index < 2);
|
||||
// SAFETY: we know the value is in-bounds
|
||||
unsafe { Self::from_index_unchecked(index) }
|
||||
}
|
||||
|
||||
/// Convert from a file index into a [Color] type, no bounds checking.
|
||||
#[inline(always)]
|
||||
pub unsafe fn from_index_unchecked(index: usize) -> Self {
|
||||
std::mem::transmute(index as u8)
|
||||
}
|
||||
|
||||
/// Return the index of a given [Color].
|
||||
#[inline(always)]
|
||||
pub fn index(self) -> usize {
|
||||
self as usize
|
||||
}
|
||||
|
||||
/// Return the first [Rank] for pieces of the given [Color], where its pieces start.
|
||||
#[inline(always)]
|
||||
pub fn first_rank(self) -> Rank {
|
||||
match self {
|
||||
Self::White => Rank::First,
|
||||
Self::Black => Rank::Eighth,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the second [Rank] for pieces of the given [Color], where its pawns start.
|
||||
#[inline(always)]
|
||||
pub fn second_rank(self) -> Rank {
|
||||
match self {
|
||||
Self::White => Rank::Second,
|
||||
Self::Black => Rank::Seventh,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the fourth [Rank] for pieces of the given [Color], where its pawns move to after a
|
||||
/// two-square move.
|
||||
#[inline(always)]
|
||||
pub fn fourth_rank(self) -> Rank {
|
||||
match self {
|
||||
Self::White => Rank::Fourth,
|
||||
Self::Black => Rank::Fifth,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the seventh [Rank] for pieces of the given [Color], which is the rank before a pawn
|
||||
/// gets promoted.
|
||||
#[inline(always)]
|
||||
pub fn seventh_rank(self) -> Rank {
|
||||
match self {
|
||||
Self::White => Rank::Seventh,
|
||||
Self::Black => Rank::Second,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Not for Color {
|
||||
type Output = Color;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
match self {
|
||||
Self::White => Self::Black,
|
||||
Self::Black => Self::White,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn from_index() {
|
||||
assert_eq!(Color::from_index(0), Color::White);
|
||||
assert_eq!(Color::from_index(1), Color::Black);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn index() {
|
||||
assert_eq!(Color::White.index(), 0);
|
||||
assert_eq!(Color::Black.index(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not() {
|
||||
assert_eq!(!Color::White, Color::Black);
|
||||
assert_eq!(!Color::Black, Color::White);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,14 @@
|
|||
pub mod bitboard;
|
||||
pub use bitboard::*;
|
||||
|
||||
pub mod directions;
|
||||
pub use directions::*;
|
||||
pub mod castle_rights;
|
||||
pub use castle_rights::*;
|
||||
|
||||
pub mod color;
|
||||
pub use color::*;
|
||||
|
||||
pub mod direction;
|
||||
pub use direction::*;
|
||||
|
||||
pub mod file;
|
||||
pub use file::*;
|
||||
|
|
Loading…
Reference in a new issue