diff --git a/board/src/moves/king.rs b/board/src/moves/king.rs new file mode 100644 index 0000000..0884a86 --- /dev/null +++ b/board/src/moves/king.rs @@ -0,0 +1,249 @@ +// Eryn Wells + +//! 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; 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 + '_ { + 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 + '_ { + 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 + '_ { + 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 { + &self.move_lists[MoveList::Pushes as usize] + } + + fn pushes_move_list_mut(&mut self) -> &mut Vec { + &mut self.move_lists[MoveList::Pushes as usize] + } + + fn captures_move_list(&self) -> &Vec { + &self.move_lists[MoveList::Captures as usize] + } + + fn captures_move_list_mut(&mut self) -> &mut Vec { + &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 { + None + } + + fn queen_side_castle(&self) -> Option { + None + } +} + +impl<'pos> Iterator for KingMoveGenerator<'pos> { + type Item = Move; + + fn next(&mut self) -> Option { + 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 = 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 = 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 + ); + } +} diff --git a/board/src/moves/mod.rs b/board/src/moves/mod.rs index b0fa3a9..1745e63 100644 --- a/board/src/moves/mod.rs +++ b/board/src/moves/mod.rs @@ -1,5 +1,6 @@ // Eryn Wells +mod king; mod r#move; mod move_generator; mod pawn; diff --git a/board/src/moves/move_generator.rs b/board/src/moves/move_generator.rs index c7d5800..e93d46b 100644 --- a/board/src/moves/move_generator.rs +++ b/board/src/moves/move_generator.rs @@ -1,5 +1,6 @@ // Eryn Wells +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, } diff --git a/board/src/tests.rs b/board/src/tests.rs index 5924d5b..35ac163 100644 --- a/board/src/tests.rs +++ b/board/src/tests.rs @@ -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); }