[board] Implement KingMoveGenerator

Generate moves for kings.

This struct is trying out a different approach to iterators where the type
itself isn't one, but it has an iter() method that returns an iterator over all
the move lists.

This struct also builds all the moves up front, in new() before the new instance
is returned.
This commit is contained in:
Eryn Wells 2023-12-31 16:33:47 -08:00
parent af36b75df7
commit 4a54d8b877
4 changed files with 260 additions and 0 deletions

249
board/src/moves/king.rs Normal file
View file

@ -0,0 +1,249 @@
// 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
}
fn iter(&self) -> impl Iterator<Item = Move> + '_ {
self.move_lists.iter().flatten().cloned()
}
fn bitboard(&self) -> BitBoard {
self.quiet_moves_bitboard() | self.capture_moves_bitboard()
}
fn quiet_moves_iter(&self) -> impl Iterator<Item = Move> + '_ {
self.pushes_move_list().iter().cloned()
}
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().cloned()
}
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 mut king = self
.position
.bitboard_for_piece(Piece::king(self.color))
.clone();
let mut all_moves = king.shift_east_one() | king.shift_west_one();
king |= all_moves;
all_moves |= king.shift_north_one() | king.shift_south_one();
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::*;
use crate::{Position, Square};
use std::collections::HashSet;
#[test]
fn one_king() {
let mut pos = Position::empty();
pos.place_piece(&Piece::king(Color::White), &Square::e4())
.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 = [
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()),
];
let mut generated_moves: HashSet<Move> = generator.iter().collect();
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();
pos.place_piece(&Piece::king(Color::White), &Square::a1())
.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 = [
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()),
];
let mut generated_moves: HashSet<Move> = generator.iter().collect();
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
);
}
}

View file

@ -1,5 +1,6 @@
// Eryn Wells <eryn@erynwells.me>
mod king;
mod r#move;
mod move_generator;
mod pawn;

View file

@ -1,5 +1,6 @@
// Eryn Wells <eryn@erynwells.me>
use super::king::KingMoveGenerator;
use super::pawn::PawnMoveGenerator;
use super::Move;
use crate::piece::Color;
@ -19,6 +20,7 @@ impl<'a> Moves<'a> {
color,
iterators: vec![
Box::new(PawnMoveGenerator::new(position, color)),
Box::new(KingMoveGenerator::new(position, color)),
],
index: 0,
}

View file

@ -14,10 +14,18 @@ macro_rules! sq_constructor {
}
impl Square {
sq_constructor!(a1);
sq_constructor!(a2);
sq_constructor!(b1);
sq_constructor!(b2);
sq_constructor!(d3);
sq_constructor!(d4);
sq_constructor!(d5);
sq_constructor!(e2);
sq_constructor!(e3);
sq_constructor!(e4);
sq_constructor!(e5);
sq_constructor!(f3);
sq_constructor!(f4);
sq_constructor!(f5);
}