2023-12-31 16:33:47 -08:00
|
|
|
// Eryn Wells <eryn@erynwells.me>
|
|
|
|
|
|
|
|
//! Declares the KingMoveGenerator type. This struct is responsible for
|
|
|
|
//! generating the possible moves for the king in the given position.
|
|
|
|
|
|
|
|
use crate::bitboard::BitBoard;
|
|
|
|
use crate::piece::{Color, Piece};
|
|
|
|
use crate::{Move, Position};
|
|
|
|
|
|
|
|
enum MoveList {
|
|
|
|
Pushes = 0,
|
|
|
|
Captures = 1,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) struct KingMoveGenerator<'a> {
|
|
|
|
position: &'a Position,
|
|
|
|
color: Color,
|
|
|
|
|
|
|
|
did_populate_move_lists: bool,
|
|
|
|
|
|
|
|
pushes: BitBoard,
|
|
|
|
captures: BitBoard,
|
|
|
|
|
|
|
|
move_lists: [Vec<Move>; 2],
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> KingMoveGenerator<'a> {
|
|
|
|
pub(super) fn new(position: &Position, color: Color) -> KingMoveGenerator {
|
|
|
|
let mut generator = KingMoveGenerator {
|
|
|
|
position,
|
|
|
|
color,
|
|
|
|
did_populate_move_lists: false,
|
|
|
|
pushes: BitBoard::empty(),
|
|
|
|
captures: BitBoard::empty(),
|
|
|
|
move_lists: [Vec::new(), Vec::new()],
|
|
|
|
};
|
|
|
|
|
|
|
|
generator.generate_moves();
|
|
|
|
|
|
|
|
generator
|
|
|
|
}
|
|
|
|
|
2024-01-01 09:33:33 -08:00
|
|
|
pub(super) fn iter(&self) -> impl Iterator<Item = &Move> + '_ {
|
|
|
|
self.move_lists.iter().flatten()
|
2023-12-31 16:33:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
fn bitboard(&self) -> BitBoard {
|
|
|
|
self.quiet_moves_bitboard() | self.capture_moves_bitboard()
|
|
|
|
}
|
|
|
|
|
2024-01-01 09:33:33 -08:00
|
|
|
fn quiet_moves_iter(&self) -> impl Iterator<Item = &Move> + '_ {
|
|
|
|
self.pushes_move_list().iter()
|
2023-12-31 16:33:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
fn quiet_moves_bitboard(&self) -> BitBoard {
|
|
|
|
self.pushes
|
|
|
|
}
|
|
|
|
|
|
|
|
fn capture_moves_bitboard(&self) -> BitBoard {
|
|
|
|
self.captures
|
|
|
|
}
|
|
|
|
|
2024-01-01 09:33:33 -08:00
|
|
|
fn capture_moves_iter(&self) -> impl Iterator<Item = &Move> + '_ {
|
|
|
|
self.captures_move_list().iter()
|
2023-12-31 16:33:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
fn generate_moves(&mut self) {
|
|
|
|
self.generate_bitboards();
|
|
|
|
self.populate_move_lists();
|
|
|
|
self.did_populate_move_lists = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pushes_move_list(&self) -> &Vec<Move> {
|
|
|
|
&self.move_lists[MoveList::Pushes as usize]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pushes_move_list_mut(&mut self) -> &mut Vec<Move> {
|
|
|
|
&mut self.move_lists[MoveList::Pushes as usize]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn captures_move_list(&self) -> &Vec<Move> {
|
|
|
|
&self.move_lists[MoveList::Captures as usize]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn captures_move_list_mut(&mut self) -> &mut Vec<Move> {
|
|
|
|
&mut self.move_lists[MoveList::Captures as usize]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn generate_bitboards(&mut self) {
|
2024-01-02 08:42:47 -08:00
|
|
|
let king = self
|
2023-12-31 16:33:47 -08:00
|
|
|
.position
|
|
|
|
.bitboard_for_piece(Piece::king(self.color))
|
2024-01-02 08:42:47 -08:00
|
|
|
.occupied_squares()
|
|
|
|
.next()
|
|
|
|
.unwrap();
|
|
|
|
let all_moves = BitBoard::king_moves(king);
|
2023-12-31 16:33:47 -08:00
|
|
|
|
|
|
|
let empty_squares = self.position.empty_squares();
|
|
|
|
let opposing_pieces = self.position.bitboard_for_color(self.color.other());
|
|
|
|
|
|
|
|
self.pushes = all_moves & empty_squares;
|
|
|
|
self.captures = all_moves & opposing_pieces;
|
|
|
|
|
|
|
|
// TODO: Handle checks. Prevent moving a king to a square attacked by a
|
|
|
|
// piece of the opposite color.
|
|
|
|
}
|
|
|
|
|
|
|
|
fn populate_move_lists(&mut self) {
|
|
|
|
let king = Piece::king(self.color);
|
|
|
|
|
|
|
|
let from_sq = self
|
|
|
|
.position
|
|
|
|
.bitboard_for_piece(king)
|
|
|
|
.occupied_squares()
|
|
|
|
.next()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
for to_sq in self.pushes.occupied_squares() {
|
|
|
|
self.pushes_move_list_mut()
|
|
|
|
.push(Move::new(king, from_sq, to_sq));
|
|
|
|
}
|
|
|
|
|
|
|
|
for to_sq in self.captures.occupied_squares() {
|
|
|
|
let captured_piece = self.position.piece_on_square(to_sq).unwrap();
|
|
|
|
self.captures_move_list_mut()
|
|
|
|
.push(Move::new(king, from_sq, to_sq).capturing(captured_piece));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(unused_variables)]
|
|
|
|
if let Some(castle) = self.king_side_castle() {
|
|
|
|
// TODO: Add castle to the pushes move list.
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(unused_variables)]
|
|
|
|
if let Some(castle) = self.queen_side_castle() {
|
|
|
|
// TODO: Add castle to the pushes move list.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn king_side_castle(&self) -> Option<Move> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn queen_side_castle(&self) -> Option<Move> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'pos> Iterator for KingMoveGenerator<'pos> {
|
|
|
|
type Item = Move;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
if !self.did_populate_move_lists {
|
|
|
|
self.generate_moves();
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2024-01-06 16:40:21 -08:00
|
|
|
use crate::{piece::Piece, Position, Square};
|
2023-12-31 16:33:47 -08:00
|
|
|
use std::collections::HashSet;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn one_king() {
|
|
|
|
let mut pos = Position::empty();
|
2024-01-06 16:40:21 -08:00
|
|
|
pos.place_piece(Piece::king(Color::White), Square::E4)
|
2023-12-31 16:33:47 -08:00
|
|
|
.expect("Failed to place king on e4");
|
|
|
|
|
|
|
|
let generator = KingMoveGenerator::new(&pos, Color::White);
|
|
|
|
|
|
|
|
let bb = generator.bitboard();
|
|
|
|
assert_eq!(
|
|
|
|
bb,
|
|
|
|
BitBoard::new(
|
|
|
|
0b00000000_00000000_00000000_00111000_00101000_00111000_00000000_00000000
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
let expected_moves = [
|
2024-01-06 16:45:13 -08:00
|
|
|
Move::new(Piece::king(Color::White), Square::E4, Square::D5),
|
|
|
|
Move::new(Piece::king(Color::White), Square::E4, Square::E5),
|
|
|
|
Move::new(Piece::king(Color::White), Square::E4, Square::F5),
|
|
|
|
Move::new(Piece::king(Color::White), Square::E4, Square::F4),
|
|
|
|
Move::new(Piece::king(Color::White), Square::E4, Square::F3),
|
|
|
|
Move::new(Piece::king(Color::White), Square::E4, Square::E3),
|
|
|
|
Move::new(Piece::king(Color::White), Square::E4, Square::D3),
|
|
|
|
Move::new(Piece::king(Color::White), Square::E4, Square::D4),
|
2023-12-31 16:33:47 -08:00
|
|
|
];
|
|
|
|
|
2024-01-01 09:33:33 -08:00
|
|
|
let mut generated_moves: HashSet<Move> = generator.iter().cloned().collect();
|
2023-12-31 16:33:47 -08:00
|
|
|
|
|
|
|
for ex_move in expected_moves {
|
|
|
|
assert!(
|
|
|
|
generated_moves.remove(&ex_move),
|
|
|
|
"{:#?} was not generated",
|
|
|
|
&ex_move
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
assert!(
|
|
|
|
generated_moves.is_empty(),
|
|
|
|
"Moves unexpectedly present: {:#?}",
|
|
|
|
generated_moves
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn one_king_corner() {
|
|
|
|
let mut pos = Position::empty();
|
2024-01-06 16:40:21 -08:00
|
|
|
pos.place_piece(Piece::king(Color::White), Square::A1)
|
2023-12-31 16:33:47 -08:00
|
|
|
.expect("Failed to place king on a1");
|
|
|
|
|
|
|
|
let generator = KingMoveGenerator::new(&pos, Color::White);
|
|
|
|
|
|
|
|
let bb = generator.bitboard();
|
|
|
|
assert_eq!(
|
|
|
|
bb,
|
|
|
|
BitBoard::new(
|
|
|
|
0b00000000_00000000_00000000_00000000_00000000_00000000_00000011_00000010
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
let expected_moves = [
|
2024-01-06 16:45:13 -08:00
|
|
|
Move::new(Piece::king(Color::White), Square::A1, Square::A2),
|
|
|
|
Move::new(Piece::king(Color::White), Square::A1, Square::B1),
|
|
|
|
Move::new(Piece::king(Color::White), Square::A1, Square::B2),
|
2023-12-31 16:33:47 -08:00
|
|
|
];
|
|
|
|
|
2024-01-01 09:33:33 -08:00
|
|
|
let mut generated_moves: HashSet<Move> = generator.iter().cloned().collect();
|
2023-12-31 16:33:47 -08:00
|
|
|
|
|
|
|
for ex_move in expected_moves {
|
|
|
|
assert!(
|
|
|
|
generated_moves.remove(&ex_move),
|
|
|
|
"{:#?} was not generated",
|
|
|
|
&ex_move
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
assert!(
|
|
|
|
generated_moves.is_empty(),
|
|
|
|
"Moves unexpectedly present: {:#?}",
|
|
|
|
generated_moves
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|