[board, core, moves, position] Implement castling
Implement a new method on Position that evaluates whether the active color can castle on a given wing of the board. Then, implement making a castling move in the position. Make a new Wing enum in the core crate to specify kingside or queenside. Replace the Castle enum from the board crate with this one. This caused a lot of churn... Along the way fix a bunch of tests. Note: there's still no way to actually make a castling move in explorer.
This commit is contained in:
parent
6816e350eb
commit
0c1863acb9
18 changed files with 499 additions and 258 deletions
|
@ -7,7 +7,7 @@ use crate::{
|
|||
PieceSet,
|
||||
};
|
||||
use chessfriend_bitboard::BitBoard;
|
||||
use chessfriend_core::{Color, Piece, Shape, Square};
|
||||
use chessfriend_core::{Color, Piece, Shape, Square, Wing};
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct Board {
|
||||
|
@ -82,10 +82,12 @@ impl Board {
|
|||
}
|
||||
|
||||
impl Board {
|
||||
/// A [`BitBoard`] of squares occupied by pieces of all colors.
|
||||
pub fn occupancy(&self) -> BitBoard {
|
||||
self.pieces.occpuancy()
|
||||
}
|
||||
|
||||
/// A [`BitBoard`] of squares that are vacant.
|
||||
pub fn vacancy(&self) -> BitBoard {
|
||||
!self.occupancy()
|
||||
}
|
||||
|
@ -99,6 +101,13 @@ impl Board {
|
|||
}
|
||||
}
|
||||
|
||||
impl Board {
|
||||
#[must_use]
|
||||
pub fn castling_parameters(&self, wing: Wing) -> &'static castle::Parameters {
|
||||
&castle::Parameters::BY_COLOR[self.active_color as usize][wing as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl Board {
|
||||
pub fn display(&self) -> DiagramFormatter<'_> {
|
||||
DiagramFormatter::new(self)
|
||||
|
|
|
@ -3,22 +3,5 @@
|
|||
mod parameters;
|
||||
mod rights;
|
||||
|
||||
pub use parameters::Parameters;
|
||||
pub use rights::Rights;
|
||||
|
||||
use chessfriend_core::Color;
|
||||
use parameters::Parameters;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Castle {
|
||||
KingSide = 0,
|
||||
QueenSide = 1,
|
||||
}
|
||||
|
||||
impl Castle {
|
||||
pub const ALL: [Castle; 2] = [Castle::KingSide, Castle::QueenSide];
|
||||
|
||||
pub fn parameters(self, color: Color) -> &'static Parameters {
|
||||
&Parameters::BY_COLOR[color as usize][self as usize]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,31 +1,32 @@
|
|||
use chessfriend_bitboard::BitBoard;
|
||||
use chessfriend_core::{Color, Square};
|
||||
use chessfriend_core::{Color, Square, Wing};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Parameters {
|
||||
/// Origin squares of the king and rook.
|
||||
origin: Squares,
|
||||
pub origin: Squares,
|
||||
|
||||
/// Target or destination squares for the king and rook.
|
||||
target: Squares,
|
||||
pub target: Squares,
|
||||
|
||||
/// The set of squares that must be clear of any pieces in order to perform
|
||||
/// this castle.
|
||||
clear: BitBoard,
|
||||
pub clear: BitBoard,
|
||||
|
||||
/// The set of squares that must not be attacked (i.e. visible to opposing
|
||||
/// pieces) in order to perform this castle.
|
||||
check: BitBoard,
|
||||
pub check: BitBoard,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Squares {
|
||||
pub struct Squares {
|
||||
pub king: Square,
|
||||
pub rook: Square,
|
||||
}
|
||||
|
||||
impl Parameters {
|
||||
/// Parameters for each castling move, organized by color and board-side.
|
||||
pub(super) const BY_COLOR: [[Self; 2]; Color::NUM] = [
|
||||
pub(crate) const BY_COLOR: [[Self; Wing::NUM]; Color::NUM] = [
|
||||
[
|
||||
Parameters {
|
||||
origin: Squares {
|
||||
|
@ -80,31 +81,8 @@ impl Parameters {
|
|||
],
|
||||
];
|
||||
|
||||
pub fn king_origin_square(&self) -> Square {
|
||||
self.origin.king
|
||||
}
|
||||
|
||||
pub fn rook_origin_square(&self) -> Square {
|
||||
self.origin.rook
|
||||
}
|
||||
|
||||
pub fn king_target_square(&self) -> Square {
|
||||
self.target.king
|
||||
}
|
||||
|
||||
pub fn rook_target_square(&self) -> Square {
|
||||
self.target.rook
|
||||
}
|
||||
|
||||
/// A [`BitBoard`] of the squares that must be clear of any piece in order
|
||||
/// to perform this castle move.
|
||||
pub fn clear_squares(&self) -> &BitBoard {
|
||||
&self.clear
|
||||
}
|
||||
|
||||
/// A [`BitBoard`] of the squares that must not be visible to opposing
|
||||
/// pieces in order to perform this castle move.
|
||||
pub fn check_squares(&self) -> &BitBoard {
|
||||
&self.check
|
||||
#[must_use]
|
||||
pub fn get(color: Color, wing: Wing) -> &'static Parameters {
|
||||
&Self::BY_COLOR[color as usize][wing as usize]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use super::Castle;
|
||||
use chessfriend_core::Color;
|
||||
use chessfriend_core::{Color, Wing};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||
|
@ -13,16 +12,16 @@ impl Rights {
|
|||
/// as long as they have not moved their king, or the rook on that side of
|
||||
/// the board.
|
||||
#[must_use]
|
||||
pub fn color_has_right(self, color: Color, castle: Castle) -> bool {
|
||||
(self.0 & (1 << Self::flag_offset(color, castle))) != 0
|
||||
pub fn color_has_right(self, color: Color, wing: Wing) -> bool {
|
||||
(self.0 & (1 << Self::flag_offset(color, wing))) != 0
|
||||
}
|
||||
|
||||
pub fn grant(&mut self, color: Color, castle: Castle) {
|
||||
self.0 |= 1 << Self::flag_offset(color, castle);
|
||||
pub fn grant(&mut self, color: Color, wing: Wing) {
|
||||
self.0 |= 1 << Self::flag_offset(color, wing);
|
||||
}
|
||||
|
||||
pub fn revoke(&mut self, color: Color, castle: Castle) {
|
||||
self.0 &= !(1 << Self::flag_offset(color, castle));
|
||||
pub fn revoke(&mut self, color: Color, wing: Wing) {
|
||||
self.0 &= !(1 << Self::flag_offset(color, wing));
|
||||
}
|
||||
|
||||
/// Revoke castling rights for all colors and all sides of the board.
|
||||
|
@ -32,8 +31,8 @@ impl Rights {
|
|||
}
|
||||
|
||||
impl Rights {
|
||||
fn flag_offset(color: Color, castle: Castle) -> usize {
|
||||
((color as usize) << 1) + castle as usize
|
||||
fn flag_offset(color: Color, wing: Wing) -> usize {
|
||||
((color as usize) << 1) + wing as usize
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,30 +54,30 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn bitfield_offsets() {
|
||||
assert_eq!(Rights::flag_offset(Color::White, Castle::KingSide), 0);
|
||||
assert_eq!(Rights::flag_offset(Color::White, Castle::QueenSide), 1);
|
||||
assert_eq!(Rights::flag_offset(Color::Black, Castle::KingSide), 2);
|
||||
assert_eq!(Rights::flag_offset(Color::Black, Castle::QueenSide), 3);
|
||||
assert_eq!(Rights::flag_offset(Color::White, Wing::KingSide), 0);
|
||||
assert_eq!(Rights::flag_offset(Color::White, Wing::QueenSide), 1);
|
||||
assert_eq!(Rights::flag_offset(Color::Black, Wing::KingSide), 2);
|
||||
assert_eq!(Rights::flag_offset(Color::Black, Wing::QueenSide), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_rights() {
|
||||
let mut rights = Rights::default();
|
||||
assert!(rights.color_has_right(Color::White, Castle::KingSide));
|
||||
assert!(rights.color_has_right(Color::White, Castle::QueenSide));
|
||||
assert!(rights.color_has_right(Color::Black, Castle::KingSide));
|
||||
assert!(rights.color_has_right(Color::Black, Castle::QueenSide));
|
||||
assert!(rights.color_has_right(Color::White, Wing::KingSide));
|
||||
assert!(rights.color_has_right(Color::White, Wing::QueenSide));
|
||||
assert!(rights.color_has_right(Color::Black, Wing::KingSide));
|
||||
assert!(rights.color_has_right(Color::Black, Wing::QueenSide));
|
||||
|
||||
rights.revoke(Color::White, Castle::QueenSide);
|
||||
assert!(rights.color_has_right(Color::White, Castle::KingSide));
|
||||
assert!(!rights.color_has_right(Color::White, Castle::QueenSide));
|
||||
assert!(rights.color_has_right(Color::Black, Castle::KingSide));
|
||||
assert!(rights.color_has_right(Color::Black, Castle::QueenSide));
|
||||
rights.revoke(Color::White, Wing::QueenSide);
|
||||
assert!(rights.color_has_right(Color::White, Wing::KingSide));
|
||||
assert!(!rights.color_has_right(Color::White, Wing::QueenSide));
|
||||
assert!(rights.color_has_right(Color::Black, Wing::KingSide));
|
||||
assert!(rights.color_has_right(Color::Black, Wing::QueenSide));
|
||||
|
||||
rights.grant(Color::White, Castle::QueenSide);
|
||||
assert!(rights.color_has_right(Color::White, Castle::KingSide));
|
||||
assert!(rights.color_has_right(Color::White, Castle::QueenSide));
|
||||
assert!(rights.color_has_right(Color::Black, Castle::KingSide));
|
||||
assert!(rights.color_has_right(Color::Black, Castle::QueenSide));
|
||||
rights.grant(Color::White, Wing::QueenSide);
|
||||
assert!(rights.color_has_right(Color::White, Wing::KingSide));
|
||||
assert!(rights.color_has_right(Color::White, Wing::QueenSide));
|
||||
assert!(rights.color_has_right(Color::Black, Wing::KingSide));
|
||||
assert!(rights.color_has_right(Color::Black, Wing::QueenSide));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use crate::{piece_sets::PlacePieceStrategy, Board, Castle};
|
||||
use chessfriend_core::{coordinates::ParseSquareError, piece, Color, File, Piece, Rank, Square};
|
||||
use crate::{piece_sets::PlacePieceStrategy, Board};
|
||||
use chessfriend_core::{
|
||||
coordinates::ParseSquareError, piece, Color, File, Piece, Rank, Square, Wing,
|
||||
};
|
||||
use std::fmt::Write;
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -115,10 +117,10 @@ impl ToFenStr for Board {
|
|||
.map_err(ToFenStrError::FmtError)?;
|
||||
|
||||
let castling = [
|
||||
(Color::White, Castle::KingSide),
|
||||
(Color::White, Castle::QueenSide),
|
||||
(Color::Black, Castle::KingSide),
|
||||
(Color::Black, Castle::QueenSide),
|
||||
(Color::White, Wing::KingSide),
|
||||
(Color::White, Wing::QueenSide),
|
||||
(Color::Black, Wing::KingSide),
|
||||
(Color::Black, Wing::QueenSide),
|
||||
]
|
||||
.map(|(color, castle)| {
|
||||
if !self.castling_rights.color_has_right(color, castle) {
|
||||
|
@ -126,10 +128,10 @@ impl ToFenStr for Board {
|
|||
}
|
||||
|
||||
match (color, castle) {
|
||||
(Color::White, Castle::KingSide) => "K",
|
||||
(Color::White, Castle::QueenSide) => "Q",
|
||||
(Color::Black, Castle::KingSide) => "k",
|
||||
(Color::Black, Castle::QueenSide) => "q",
|
||||
(Color::White, Wing::KingSide) => "K",
|
||||
(Color::White, Wing::QueenSide) => "Q",
|
||||
(Color::Black, Wing::KingSide) => "k",
|
||||
(Color::Black, Wing::QueenSide) => "q",
|
||||
}
|
||||
})
|
||||
.concat();
|
||||
|
@ -232,10 +234,10 @@ impl FromFenStr for Board {
|
|||
} else {
|
||||
for ch in castling_rights.chars() {
|
||||
match ch {
|
||||
'K' => board.castling_rights.grant(Color::White, Castle::KingSide),
|
||||
'Q' => board.castling_rights.grant(Color::White, Castle::QueenSide),
|
||||
'k' => board.castling_rights.grant(Color::Black, Castle::KingSide),
|
||||
'q' => board.castling_rights.grant(Color::Black, Castle::QueenSide),
|
||||
'K' => board.castling_rights.grant(Color::White, Wing::KingSide),
|
||||
'Q' => board.castling_rights.grant(Color::White, Wing::QueenSide),
|
||||
'k' => board.castling_rights.grant(Color::Black, Wing::KingSide),
|
||||
'q' => board.castling_rights.grant(Color::Black, Wing::QueenSide),
|
||||
_ => return Err(FromFenStrError::InvalidValue),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,12 +10,7 @@ mod board;
|
|||
mod piece_sets;
|
||||
|
||||
pub use board::Board;
|
||||
pub use castle::Parameters as CastleParameters;
|
||||
pub use piece_sets::{PlacePieceError, PlacePieceStrategy};
|
||||
|
||||
use castle::Castle;
|
||||
use en_passant::EnPassant;
|
||||
use piece_sets::PieceSet;
|
||||
|
||||
// Used by macros.
|
||||
#[allow(unused_imports)]
|
||||
use piece_sets::{PlacePieceError, PlacePieceStrategy};
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
use chessfriend_bitboard::BitBoard;
|
||||
use chessfriend_core::{Color, Piece, Shape, Square};
|
||||
|
||||
/// A collection of bitboards that organize pieces by color.
|
||||
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
|
||||
pub(super) struct ByColor(BitBoard, [BitBoard; Color::NUM]);
|
||||
|
||||
/// A collection of bitboards that organize pieces first by color and then by piece type.
|
||||
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
|
||||
pub(super) struct ByColorAndShape([[BitBoard; Shape::NUM]; Color::NUM]);
|
||||
|
||||
impl ByColor {
|
||||
pub(super) fn new(all_pieces: BitBoard, bitboards_by_color: [BitBoard; Color::NUM]) -> Self {
|
||||
ByColor(all_pieces, bitboards_by_color)
|
||||
}
|
||||
|
||||
pub(crate) fn all(&self) -> BitBoard {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub(crate) fn bitboard(&self, color: Color) -> BitBoard {
|
||||
self.1[color as usize]
|
||||
}
|
||||
|
||||
pub(super) fn set_square(&mut self, square: Square, color: Color) {
|
||||
self.0.set(square);
|
||||
self.1[color as usize].set(square);
|
||||
}
|
||||
|
||||
pub(super) fn clear_square(&mut self, square: Square, color: Color) {
|
||||
self.0.clear(square);
|
||||
self.1[color as usize].clear(square);
|
||||
}
|
||||
}
|
||||
|
||||
impl ByColorAndShape {
|
||||
pub(super) fn new(bitboards: [[BitBoard; Shape::NUM]; Color::NUM]) -> Self {
|
||||
Self(bitboards)
|
||||
}
|
||||
|
||||
pub(super) fn bitboard_for_piece(&self, piece: Piece) -> BitBoard {
|
||||
self.0[piece.color as usize][piece.shape as usize]
|
||||
}
|
||||
|
||||
pub(super) fn bitboard_for_piece_mut(&mut self, piece: Piece) -> &mut BitBoard {
|
||||
&mut self.0[piece.color as usize][piece.shape as usize]
|
||||
}
|
||||
|
||||
pub(super) fn set_square(&mut self, square: Square, piece: Piece) {
|
||||
self.bitboard_for_piece_mut(piece).set(square);
|
||||
}
|
||||
|
||||
pub(super) fn clear_square(&mut self, square: Square, piece: Piece) {
|
||||
self.bitboard_for_piece_mut(piece).clear(square);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue