Compare commits

...

6 commits

Author SHA1 Message Date
Bruno BELANYI aa3b464bb8 Add 'CastleRights::unmoved_rooks'
All checks were successful
continuous-integration/drone/push Build is passing
2022-07-18 13:50:23 +02:00
Bruno BELANYI c5949fb01e Add 'CastleRights::without_{king,queen}_side' 2022-07-18 13:38:52 +02:00
Bruno BELANYI 562182d26b Add 'CastleRights::has_{king,queen}_side' 2022-07-18 13:28:23 +02:00
Bruno BELANYI 63c5d2dc34 Add 'CastleRights' enum 2022-07-18 13:27:03 +02:00
Bruno BELANYI e0c667d090 Add 'Color' enum 2022-07-18 13:22:04 +02:00
Bruno BELANYI 3a6c4113fc Rename 'board::direction{s,}' 2022-07-18 13:11:37 +02:00
4 changed files with 306 additions and 2 deletions

196
src/board/castle_rights.rs Normal file
View 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
View 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);
}
}

View file

@ -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::*;