[board] Replace implementations of the king and knight move generators with a more generic version

While implementing these move generators, I realized I was duplicating a lot of code. So, I started thinking about how to do less of that. I create a MoveGeneratorInternal trait that these move generators implement. This trait provides default implementations of several methods.

I also discovered that I needed a way to keep track of move sets per piece for each kind (shape) of piece. The new move generators, and the generators still to come, can now keep track of moves per piece separately in MoveSet instances.
This commit is contained in:
Eryn Wells 2024-01-06 17:01:16 -08:00
parent bcab682bb1
commit e2d93b8c3c
3 changed files with 151 additions and 210 deletions

View file

@ -3,158 +3,66 @@
//! 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};
use super::{move_generator_declaration, MoveGeneratorInternal, MoveSet};
use crate::{
bitboard::BitBoard,
piece::{Color, Piece, PlacedPiece},
Move, Position, Square,
};
use std::collections::BTreeMap;
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],
}
move_generator_declaration!(KingMoveGenerator, struct);
move_generator_declaration!(KingMoveGenerator, new);
move_generator_declaration!(KingMoveGenerator, getters);
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
#[allow(unused_variables)]
fn king_side_castle(position: &Position) -> Option<Move> {
// TODO: Implement king side castle.
None
}
pub(super) fn iter(&self) -> impl Iterator<Item = &Move> + '_ {
self.move_lists.iter().flatten()
#[allow(unused_variables)]
fn queen_side_castle(position: &Position) -> Option<Move> {
// TODO: Implement queen side castle.
None
}
}
impl<'pos> MoveGeneratorInternal for KingMoveGenerator<'pos> {
fn piece(color: Color) -> Piece {
Piece::king(color)
}
fn bitboard(&self) -> BitBoard {
self.quiet_moves_bitboard() | self.capture_moves_bitboard()
}
fn move_set_for_piece(position: &Position, placed_piece: PlacedPiece) -> MoveSet {
let piece = placed_piece.piece();
let color = piece.color();
let square = placed_piece.square();
fn quiet_moves_iter(&self) -> impl Iterator<Item = &Move> + '_ {
self.pushes_move_list().iter()
}
let empty_squares = position.empty_squares();
let opposing_pieces = position.bitboard_for_color(color.other());
fn quiet_moves_bitboard(&self) -> BitBoard {
self.pushes
}
fn capture_moves_bitboard(&self) -> BitBoard {
self.captures
}
fn capture_moves_iter(&self) -> impl Iterator<Item = &Move> + '_ {
self.captures_move_list().iter()
}
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) {
let king = self
.position
.bitboard_for_piece(Piece::king(self.color))
.occupied_squares()
.next()
.unwrap();
let all_moves = BitBoard::king_moves(king);
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;
let all_moves = BitBoard::king_moves(square);
let quiet_moves_bb = all_moves & empty_squares;
let capture_moves_bb = 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 map_to_move = |sq| Move::new(piece, square, sq);
let from_sq = self
.position
.bitboard_for_piece(king)
let king_side_castle = Self::king_side_castle(position);
let queen_side_castle = Self::queen_side_castle(position);
let quiet_moves = quiet_moves_bb
.occupied_squares()
.next()
.unwrap();
.map(map_to_move)
.chain(king_side_castle.iter().cloned())
.chain(queen_side_castle.iter().cloned());
let capture_moves = capture_moves_bb.occupied_squares().map(map_to_move);
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
MoveSet::new(placed_piece)
.quiet_moves(quiet_moves_bb, quiet_moves)
.capture_moves(capture_moves_bb, capture_moves)
}
}