[board] Use Castle as the interface type for methods related to castling

Use BoardSide as an internal type for looking up generated bitboards, target squares, etc.
This commit is contained in:
Eryn Wells 2024-01-21 09:05:42 -08:00
parent 7071f6a742
commit 918b68f300
4 changed files with 71 additions and 37 deletions

View file

@ -6,7 +6,7 @@
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet}; use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
use crate::{ use crate::{
piece::{Color, Piece, PlacedPiece}, piece::{Color, Piece, PlacedPiece},
position::BoardSide, r#move::Castle,
BitBoard, Move, MoveBuilder, Position, BitBoard, Move, MoveBuilder, Position,
}; };
@ -17,7 +17,7 @@ move_generator_declaration!(KingMoveGenerator, getters);
impl<'a> KingMoveGenerator<'a> { impl<'a> KingMoveGenerator<'a> {
#[allow(unused_variables)] #[allow(unused_variables)]
fn king_side_castle(position: &Position, color: Color) -> Option<Move> { fn king_side_castle(position: &Position, color: Color) -> Option<Move> {
if !position.player_has_right_to_castle(color, BoardSide::King) { if !position.player_has_right_to_castle(color, Castle::KingSide) {
return None; return None;
} }
@ -27,7 +27,7 @@ impl<'a> KingMoveGenerator<'a> {
#[allow(unused_variables)] #[allow(unused_variables)]
fn queen_side_castle(position: &Position, color: Color) -> Option<Move> { fn queen_side_castle(position: &Position, color: Color) -> Option<Move> {
if !position.player_has_right_to_castle(color, BoardSide::Queen) { if !position.player_has_right_to_castle(color, Castle::QueenSide) {
return None; return None;
} }

View file

@ -1,7 +1,7 @@
// Eryn Wells <eryn@erynwells.me> // Eryn Wells <eryn@erynwells.me>
use super::position::BoardSide; use super::position::BoardSide;
use crate::piece::Color; use crate::{r#move::Castle, Color};
use std::fmt; use std::fmt;
#[derive(Clone, Copy, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Eq, Hash, PartialEq)]
@ -9,20 +9,21 @@ pub(super) struct Flags(u8);
impl Flags { impl Flags {
#[inline] #[inline]
pub(super) fn player_has_right_to_castle_flag_offset(color: Color, side: BoardSide) -> u8 { pub(super) fn player_has_right_to_castle_flag_offset(color: Color, castle: Castle) -> u8 {
((color as u8) << 1) + side as u8 let board_side: BoardSide = castle.into();
((color as u8) << 1) + board_side as u8
} }
pub(super) fn player_has_right_to_castle(&self, color: Color, side: BoardSide) -> bool { pub(super) fn player_has_right_to_castle(&self, color: Color, castle: Castle) -> bool {
(self.0 & (1 << Self::player_has_right_to_castle_flag_offset(color, side))) != 0 (self.0 & (1 << Self::player_has_right_to_castle_flag_offset(color, castle))) != 0
} }
pub(super) fn set_player_has_right_to_castle_flag(&mut self, color: Color, side: BoardSide) { pub(super) fn set_player_has_right_to_castle_flag(&mut self, color: Color, castle: Castle) {
self.0 |= 1 << Self::player_has_right_to_castle_flag_offset(color, side); self.0 |= 1 << Self::player_has_right_to_castle_flag_offset(color, castle);
} }
pub(super) fn clear_player_has_right_to_castle_flag(&mut self, color: Color, side: BoardSide) { pub(super) fn clear_player_has_right_to_castle_flag(&mut self, color: Color, castle: Castle) {
self.0 &= !(1 << Self::player_has_right_to_castle_flag_offset(color, side)); self.0 &= !(1 << Self::player_has_right_to_castle_flag_offset(color, castle));
} }
} }
@ -41,24 +42,24 @@ impl Default for Flags {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::piece::Color; use crate::{r#move::Castle, Color};
#[test] #[test]
fn castle_flags() { fn castle_flags() {
assert_eq!( assert_eq!(
Flags::player_has_right_to_castle_flag_offset(Color::White, BoardSide::King), Flags::player_has_right_to_castle_flag_offset(Color::White, Castle::KingSide),
0 0
); );
assert_eq!( assert_eq!(
Flags::player_has_right_to_castle_flag_offset(Color::White, BoardSide::Queen), Flags::player_has_right_to_castle_flag_offset(Color::White, Castle::QueenSide),
1 1
); );
assert_eq!( assert_eq!(
Flags::player_has_right_to_castle_flag_offset(Color::Black, BoardSide::King), Flags::player_has_right_to_castle_flag_offset(Color::Black, Castle::KingSide),
2 2
); );
assert_eq!( assert_eq!(
Flags::player_has_right_to_castle_flag_offset(Color::Black, BoardSide::Queen), Flags::player_has_right_to_castle_flag_offset(Color::Black, Castle::QueenSide),
3 3
); );
} }
@ -66,21 +67,21 @@ mod tests {
#[test] #[test]
fn defaults() { fn defaults() {
let mut flags: Flags = Default::default(); let mut flags: Flags = Default::default();
assert!(flags.player_has_right_to_castle(Color::White, BoardSide::King)); assert!(flags.player_has_right_to_castle(Color::White, Castle::KingSide));
assert!(flags.player_has_right_to_castle(Color::White, BoardSide::Queen)); assert!(flags.player_has_right_to_castle(Color::White, Castle::QueenSide));
assert!(flags.player_has_right_to_castle(Color::Black, BoardSide::King)); assert!(flags.player_has_right_to_castle(Color::Black, Castle::KingSide));
assert!(flags.player_has_right_to_castle(Color::Black, BoardSide::Queen)); assert!(flags.player_has_right_to_castle(Color::Black, Castle::QueenSide));
flags.clear_player_has_right_to_castle_flag(Color::White, BoardSide::Queen); flags.clear_player_has_right_to_castle_flag(Color::White, Castle::QueenSide);
assert!(flags.player_has_right_to_castle(Color::White, BoardSide::King)); assert!(flags.player_has_right_to_castle(Color::White, Castle::KingSide));
assert!(!flags.player_has_right_to_castle(Color::White, BoardSide::Queen)); assert!(!flags.player_has_right_to_castle(Color::White, Castle::QueenSide));
assert!(flags.player_has_right_to_castle(Color::Black, BoardSide::King)); assert!(flags.player_has_right_to_castle(Color::Black, Castle::KingSide));
assert!(flags.player_has_right_to_castle(Color::Black, BoardSide::Queen)); assert!(flags.player_has_right_to_castle(Color::Black, Castle::QueenSide));
flags.set_player_has_right_to_castle_flag(Color::White, BoardSide::Queen); flags.set_player_has_right_to_castle_flag(Color::White, Castle::QueenSide);
assert!(flags.player_has_right_to_castle(Color::White, BoardSide::King)); assert!(flags.player_has_right_to_castle(Color::White, Castle::KingSide));
assert!(flags.player_has_right_to_castle(Color::White, BoardSide::Queen)); assert!(flags.player_has_right_to_castle(Color::White, Castle::QueenSide));
assert!(flags.player_has_right_to_castle(Color::Black, BoardSide::King)); assert!(flags.player_has_right_to_castle(Color::Black, Castle::KingSide));
assert!(flags.player_has_right_to_castle(Color::Black, BoardSide::Queen)); assert!(flags.player_has_right_to_castle(Color::Black, Castle::QueenSide));
} }
} }

View file

@ -5,6 +5,7 @@ use crate::{
move_generator::Moves, move_generator::Moves,
piece::{Color, Piece, PlacedPiece, Shape}, piece::{Color, Piece, PlacedPiece, Shape},
position::DiagramFormatter, position::DiagramFormatter,
r#move::Castle,
sight::Sight, sight::Sight,
BitBoard, Move, Square, BitBoard, Move, Square,
}; };
@ -14,8 +15,17 @@ use std::{cell::OnceCell, fmt};
/// player's king starts on. Queenside is the side of the board the player's /// player's king starts on. Queenside is the side of the board the player's
/// queen starts on. /// queen starts on.
pub(crate) enum BoardSide { pub(crate) enum BoardSide {
King, King = 0,
Queen, Queen = 1,
}
impl From<Castle> for BoardSide {
fn from(value: Castle) -> Self {
match value {
Castle::KingSide => BoardSide::King,
Castle::QueenSide => BoardSide::Queen,
}
}
} }
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
@ -77,15 +87,26 @@ impl Position {
/// The right to castle on a particular side of the board is retained as /// The right to castle on a particular side of the board is retained as
/// long as the player has not moved their king, or the rook on that side of /// long as the player has not moved their king, or the rook on that side of
/// the board. /// the board.
pub(crate) fn player_has_right_to_castle(&self, color: Color, castle: Castle) -> bool {
self.flags.player_has_right_to_castle(color, castle)
}
/// Returns `true` if the player is able to castle on the given side of the board.
/// ///
/// The following requirements must also be met: /// The following requirements must be met:
/// ///
/// 1. The player must still have the right to castle on that side of the
/// board. The king and rook involved in the castle must not have moved.
/// 1. The spaces between the king and rook must be clear /// 1. The spaces between the king and rook must be clear
/// 2. The king must not be in check /// 2. The king must not be in check
/// 3. In the course of castling on that side, the king must not pass /// 3. In the course of castling on that side, the king must not pass
/// through a square that an enemy piece can see /// through a square that an enemy piece can see
pub(crate) fn player_has_right_to_castle(&self, color: Color, side: BoardSide) -> bool { pub(crate) fn player_can_castle(&self, player: Color, castle: Castle) -> bool {
self.flags.player_has_right_to_castle(color, side) if !self.player_has_right_to_castle(player, castle.into()) {
return false;
}
true
} }
pub fn moves(&self) -> Moves { pub fn moves(&self) -> Moves {

View file

@ -1,5 +1,6 @@
// Eryn Wells <eryn@erynwells.me> // Eryn Wells <eryn@erynwells.me>
use crate::{position::BoardSide, r#move::Castle, Color};
use std::{fmt, str::FromStr}; use std::{fmt, str::FromStr};
pub enum Direction { pub enum Direction {
@ -157,12 +158,23 @@ impl Square {
impl Square { impl Square {
pub(crate) const KING_STARTING_SQUARES: [Square; 2] = [Square::E1, Square::E8]; pub(crate) const KING_STARTING_SQUARES: [Square; 2] = [Square::E1, Square::E8];
pub(crate) const KING_CASTLE_TARGET_SQUARES: [[Square; 2]; 2] = pub(crate) const KING_CASTLE_TARGET_SQUARES: [[Square; 2]; 2] =
[[Square::C1, Square::G1], [Square::C8, Square::G8]]; [[Square::G1, Square::C1], [Square::G8, Square::C8]];
pub(crate) const ROOK_CASTLE_TARGET_SQUARES: [[Square; 2]; 2] =
[[Square::F1, Square::D1], [Square::F8, Square::D8]];
pub fn from_algebraic_str(s: &str) -> Result<Square, ParseSquareError> { pub fn from_algebraic_str(s: &str) -> Result<Square, ParseSquareError> {
s.parse() s.parse()
} }
pub fn king_castle_target(player: Color, castle: Castle) -> Square {
let board_side: BoardSide = castle.into();
Self::KING_CASTLE_TARGET_SQUARES[player as usize][board_side as usize]
}
pub fn rook_castle_target(player: Color, castle: Castle) -> Square {
let board_side: BoardSide = castle.into();
Self::ROOK_CASTLE_TARGET_SQUARES[player as usize][board_side as usize]
}
pub fn neighbor(self, direction: Direction) -> Option<Square> { pub fn neighbor(self, direction: Direction) -> Option<Square> {
match direction { match direction {
Direction::North => Square::try_index(self as usize + 8), Direction::North => Square::try_index(self as usize + 8),