chessfriend/position/src/move_generator/move_set.rs
Eryn Wells 1d7dada987 [bitboard, core, position] Implement proper castle move generation
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.
2024-01-29 14:46:41 -08:00

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()
}),
)
}
}