Add a field to MoveSet called special that flags special moves that should be generated in the iter() method. This field is a u8. It only tracks castles in the first and second bits (kingside and queenside, respectively). The move iterator chains two maps over Option<()> that produce the kingside and queenside castle moves. With that done, finish the implementation of Position::player_can_castle by adding checks for whether the squares between the rook and king are clear, and that the king would not pass through a check. This is done with BitBoards! Finally, implement some logic in PositionBuilder that updates the position's castling flags based on the positions of king and rooks. Supporting changes: - Add Color:ALL and iterate on that slice - Add Castle::ALL and iterator on that slice - Add a CastlingParameters struct that contains BitBoard properties that describe squares that should be clear of pieces and squares that should not be attacked.
121 lines
3.4 KiB
Rust
121 lines
3.4 KiB
Rust
// Eryn Wells <eryn@erynwells.me>
|
|
|
|
use crate::{r#move::Castle, Move, MoveBuilder};
|
|
use chessfriend_bitboard::BitBoard;
|
|
use chessfriend_core::{Color, Piece, PlacedPiece, Shape};
|
|
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
|
struct BitBoardSet {
|
|
quiet: BitBoard,
|
|
captures: BitBoard,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
struct MoveListSet {
|
|
quiet: Vec<Move>,
|
|
captures: Vec<Move>,
|
|
}
|
|
|
|
impl MoveListSet {
|
|
pub fn contains(&self, mv: &Move) -> bool {
|
|
self.quiet.contains(mv) || self.captures.contains(mv)
|
|
}
|
|
}
|
|
|
|
/// A set of moves for a piece on the board.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub(crate) struct MoveSet {
|
|
piece: PlacedPiece,
|
|
bitboards: BitBoardSet,
|
|
special: u8,
|
|
}
|
|
|
|
impl MoveSet {
|
|
pub(super) fn new(piece: PlacedPiece) -> MoveSet {
|
|
MoveSet {
|
|
piece,
|
|
bitboards: BitBoardSet::default(),
|
|
special: 0,
|
|
}
|
|
}
|
|
|
|
pub(super) fn quiet_moves(mut self, bitboard: BitBoard) -> MoveSet {
|
|
self.bitboards.quiet = bitboard;
|
|
self
|
|
}
|
|
|
|
pub(super) fn capture_moves(mut self, bitboard: BitBoard) -> MoveSet {
|
|
self.bitboards.captures = bitboard;
|
|
self
|
|
}
|
|
|
|
pub(super) fn kingside_castle(&mut self) -> &mut MoveSet {
|
|
self.special |= 0b1;
|
|
self
|
|
}
|
|
|
|
pub(super) fn queenside_castle(&mut self) -> &mut MoveSet {
|
|
self.special |= 0b10;
|
|
self
|
|
}
|
|
|
|
/// Return a BitBoard representing all possible moves.
|
|
pub(super) fn bitboard(&self) -> BitBoard {
|
|
self.bitboards.captures | self.bitboards.quiet
|
|
}
|
|
|
|
pub(super) fn moves(&self) -> impl Iterator<Item = Move> + '_ {
|
|
let piece = self.piece.piece();
|
|
let from_square = self.piece.square();
|
|
|
|
self.bitboards
|
|
.quiet
|
|
.occupied_squares()
|
|
.map(move |to_square| MoveBuilder::new(*piece, from_square, to_square).build())
|
|
.chain(
|
|
self.bitboards
|
|
.captures
|
|
.occupied_squares()
|
|
.map(move |to_square| {
|
|
MoveBuilder::new(*piece, from_square, to_square)
|
|
.capturing(PlacedPiece::new(
|
|
Piece::new(Color::White, Shape::Pawn),
|
|
to_square,
|
|
))
|
|
.build()
|
|
}),
|
|
)
|
|
.chain(
|
|
if (self.special & 0b1) != 0 {
|
|
Some(())
|
|
} else {
|
|
None
|
|
}
|
|
.map(|()| {
|
|
MoveBuilder::new(
|
|
*piece,
|
|
from_square,
|
|
Castle::KingSide.target_squares(piece.color()).king,
|
|
)
|
|
.castle(Castle::KingSide)
|
|
.build()
|
|
}),
|
|
)
|
|
.chain(
|
|
if (self.special & 0b10) != 0 {
|
|
Some(())
|
|
} else {
|
|
None
|
|
}
|
|
.map(|()| {
|
|
MoveBuilder::new(
|
|
*piece,
|
|
from_square,
|
|
Castle::QueenSide.target_squares(piece.color()).king,
|
|
)
|
|
.castle(Castle::QueenSide)
|
|
.build()
|
|
}),
|
|
)
|
|
}
|
|
}
|