[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
|
@ -1,8 +1,8 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use crate::{defs::Kind, Move, PromotionShape};
|
||||
use chessfriend_board::{castle, en_passant::EnPassant};
|
||||
use chessfriend_core::{Color, File, PlacedPiece, Rank, Square};
|
||||
use chessfriend_board::{en_passant::EnPassant, CastleParameters};
|
||||
use chessfriend_core::{Color, File, PlacedPiece, Rank, Square, Wing};
|
||||
use std::result::Result as StdResult;
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -92,7 +92,7 @@ pub struct Promotion<S> {
|
|||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Castle {
|
||||
color: Color,
|
||||
castle: castle::Castle,
|
||||
wing: Wing,
|
||||
}
|
||||
|
||||
impl Style for Null {}
|
||||
|
@ -119,13 +119,13 @@ impl Style for Capture {
|
|||
|
||||
impl Style for Castle {
|
||||
fn origin_square(&self) -> Option<Square> {
|
||||
let parameters = self.castle.parameters(self.color);
|
||||
Some(parameters.king_origin_square())
|
||||
let parameters = CastleParameters::get(self.color, self.wing);
|
||||
Some(parameters.origin.king)
|
||||
}
|
||||
|
||||
fn target_square(&self) -> Option<Square> {
|
||||
let parameters = self.castle.parameters(self.color);
|
||||
Some(parameters.king_target_square())
|
||||
let parameters = CastleParameters::get(self.color, self.wing);
|
||||
Some(parameters.target.king)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,9 +255,9 @@ impl Builder<Null> {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn castling(color: Color, castle: castle::Castle) -> Builder<Castle> {
|
||||
pub fn castling(color: Color, wing: Wing) -> Builder<Castle> {
|
||||
Builder {
|
||||
style: Castle { color, castle },
|
||||
style: Castle { color, wing },
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -357,9 +357,9 @@ impl Builder<Push> {
|
|||
|
||||
impl Builder<Castle> {
|
||||
fn bits(&self) -> u16 {
|
||||
let bits = match self.style.castle {
|
||||
castle::Castle::KingSide => Kind::KingSideCastle,
|
||||
castle::Castle::QueenSide => Kind::QueenSideCastle,
|
||||
let bits = match self.style.wing {
|
||||
Wing::KingSide => Kind::KingSideCastle,
|
||||
Wing::QueenSide => Kind::QueenSideCastle,
|
||||
};
|
||||
|
||||
bits as u16
|
||||
|
@ -403,6 +403,11 @@ impl Builder<EnPassantCapture> {
|
|||
Move(Kind::EnPassantCapture as u16 | self.style.move_bits_unchecked())
|
||||
}
|
||||
|
||||
/// Build an en passant move.
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// Returns an error if the target or origin squares are invalid.
|
||||
pub fn build(&self) -> Result {
|
||||
Ok(Move(
|
||||
Kind::EnPassantCapture as u16 | self.style.move_bits()?,
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
use crate::builder::Builder;
|
||||
use crate::defs::Kind;
|
||||
use chessfriend_board::castle::Castle;
|
||||
use chessfriend_core::{Rank, Shape, Square};
|
||||
use chessfriend_core::{Rank, Shape, Square, Wing};
|
||||
use std::fmt;
|
||||
|
||||
/// A single player's move. In game theory parlance, this is a "ply".
|
||||
|
@ -62,10 +61,10 @@ impl Move {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn castle(&self) -> Option<Castle> {
|
||||
pub fn castle(&self) -> Option<Wing> {
|
||||
match self.flags() {
|
||||
0b0010 => Some(Castle::KingSide),
|
||||
0b0011 => Some(Castle::QueenSide),
|
||||
0b0010 => Some(Wing::KingSide),
|
||||
0b0011 => Some(Wing::QueenSide),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +76,7 @@ impl Move {
|
|||
|
||||
#[must_use]
|
||||
pub fn is_en_passant(&self) -> bool {
|
||||
self.0 == Kind::EnPassantCapture as u16
|
||||
self.flags() == Kind::EnPassantCapture as u16
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
@ -130,8 +129,8 @@ impl fmt::Display for Move {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(castle) = self.castle() {
|
||||
return match castle {
|
||||
Castle::KingSide => write!(f, "{KINGSIDE_CASTLE_STR}"),
|
||||
Castle::QueenSide => write!(f, "{QUEENSIDE_CASTLE_STR}"),
|
||||
Wing::KingSide => write!(f, "{KINGSIDE_CASTLE_STR}"),
|
||||
Wing::QueenSide => write!(f, "{QUEENSIDE_CASTLE_STR}"),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use chessfriend_board::castle::Castle;
|
||||
use chessfriend_core::{piece, Color, File, Shape, Square};
|
||||
use chessfriend_core::{piece, Color, File, Shape, Square, Wing};
|
||||
use chessfriend_moves::{testing::*, Builder, PromotionShape};
|
||||
|
||||
macro_rules! assert_flag {
|
||||
|
@ -58,51 +57,50 @@ fn move_flags_capture() -> TestResult {
|
|||
|
||||
#[test]
|
||||
fn move_flags_en_passant_capture() -> TestResult {
|
||||
let mv = unsafe {
|
||||
Builder::new()
|
||||
.from(Square::A4)
|
||||
.capturing_en_passant_on(Square::B3)
|
||||
.build_unchecked()
|
||||
};
|
||||
let ply = Builder::new()
|
||||
.from(Square::A4)
|
||||
.capturing_en_passant_on(Square::B3)
|
||||
.build()?;
|
||||
|
||||
assert_flags!(mv, false, false, true, true, false, false);
|
||||
assert_eq!(mv.origin_square(), Square::A4);
|
||||
assert_eq!(mv.target_square(), Square::B3);
|
||||
assert_eq!(mv.capture_square(), Some(Square::B4));
|
||||
assert!(ply.is_en_passant());
|
||||
assert_eq!(ply.origin_square(), Square::A4);
|
||||
assert_eq!(ply.target_square(), Square::B3);
|
||||
assert_eq!(ply.capture_square(), Some(Square::B4));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move_flags_promotion() -> TestResult {
|
||||
let mv = Builder::push(&piece!(White Pawn on H7))
|
||||
let ply = Builder::push(&piece!(White Pawn on H7))
|
||||
.to(Square::H8)
|
||||
.promoting_to(PromotionShape::Queen)
|
||||
.build()?;
|
||||
|
||||
assert_flags!(mv, false, false, false, false, false, true);
|
||||
assert_eq!(mv.promotion(), Some(Shape::Queen));
|
||||
assert!(ply.is_promotion());
|
||||
assert_eq!(ply.promotion(), Some(Shape::Queen));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move_flags_capture_promotion() -> TestResult {
|
||||
let mv = Builder::push(&piece!(White Pawn on H7))
|
||||
let ply = Builder::push(&piece!(White Pawn on H7))
|
||||
.to(Square::H8)
|
||||
.capturing_piece(&piece!(Black Knight on G8))
|
||||
.promoting_to(PromotionShape::Queen)
|
||||
.build()?;
|
||||
|
||||
assert_flags!(mv, false, false, false, true, false, true);
|
||||
assert_eq!(mv.promotion(), Some(Shape::Queen));
|
||||
assert!(ply.is_capture());
|
||||
assert!(ply.is_promotion());
|
||||
assert_eq!(ply.promotion(), Some(Shape::Queen));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move_flags_castle() -> TestResult {
|
||||
let mv = Builder::castling(Color::White, Castle::KingSide).build()?;
|
||||
let mv = Builder::castling(Color::White, Wing::KingSide).build()?;
|
||||
|
||||
assert_flags!(mv, false, false, false, false, true, false);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue