diff --git a/board/src/board.rs b/board/src/board.rs index 966d611..e7e748a 100644 --- a/board/src/board.rs +++ b/board/src/board.rs @@ -115,6 +115,10 @@ impl Board { pub fn pawns(&self, color: Color) -> BitBoard { self.pieces.find_pieces(Piece::pawn(color)) } + + pub fn knights(&self, color: Color) -> BitBoard { + self.find_pieces(Piece::knight(color)) + } } impl Board { diff --git a/moves/src/generators.rs b/moves/src/generators.rs index ee0e6bd..9c84bab 100644 --- a/moves/src/generators.rs +++ b/moves/src/generators.rs @@ -1,10 +1,12 @@ // Eryn Wells +mod knight; mod pawn; #[cfg(test)] mod testing; +pub use knight::KnightMoveGenerator; pub use pawn::PawnMoveGenerator; use crate::Move; diff --git a/moves/src/generators/knight.rs b/moves/src/generators/knight.rs new file mode 100644 index 0000000..4b5a696 --- /dev/null +++ b/moves/src/generators/knight.rs @@ -0,0 +1,164 @@ +// Eryn Wells + +use crate::Move; + +use super::GeneratedMove; +use chessfriend_bitboard::{bit_scanner::TrailingBitScanner, BitBoard}; +use chessfriend_board::Board; +use chessfriend_core::{Color, Square}; + +#[must_use] +pub struct KnightMoveGenerator { + origin_iterator: TrailingBitScanner, + target_iterator: Option, + current_origin: Option, + friends: BitBoard, + enemies: BitBoard, +} + +impl KnightMoveGenerator { + pub fn new(board: &Board, color: Option) -> Self { + let color = board.unwrap_color(color); + + let knights = board.knights(color); + let friends = board.friendly_occupancy(color); + let enemies = board.enemies(color); + + Self { + origin_iterator: knights.occupied_squares_trailing(), + target_iterator: None, + current_origin: None, + friends, + enemies, + } + } +} + +impl Iterator for KnightMoveGenerator { + type Item = GeneratedMove; + + fn next(&mut self) -> Option { + if self.current_origin.is_none() { + self.current_origin = self.origin_iterator.next(); + } + + let origin = self.current_origin?; + + let target_iterator = self.target_iterator.get_or_insert_with(|| { + let knight_moves = BitBoard::knight_moves(origin); + knight_moves.occupied_squares_trailing() + }); + + if let Some(target) = target_iterator.next() { + let target_bitboard: BitBoard = target.into(); + if (target_bitboard & self.friends).is_populated() { + self.next() + } else if (target_bitboard & self.enemies).is_populated() { + Some(Move::capture(origin, target).into()) + } else { + Some(Move::quiet(origin, target).into()) + } + } else { + self.current_origin = None; + self.target_iterator = None; + self.next() + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{assert_move_list, ply}; + use chessfriend_board::test_board; + use chessfriend_core::Color; + + #[test] + fn white_f5_moves() { + let board = test_board!(White Knight on F5); + assert_move_list!( + KnightMoveGenerator::new(&board, Some(Color::White)), + [ + ply!(F5 - G7), + ply!(F5 - H6), + ply!(F5 - H4), + ply!(F5 - G3), + ply!(F5 - E3), + ply!(F5 - D4), + ply!(F5 - D6), + ply!(F5 - E7), + ] + ); + } + + #[test] + fn white_f5_and_c6_moves() { + let board = test_board!( + White Knight on C6, + White Knight on F5, + ); + assert_move_list!( + KnightMoveGenerator::new(&board, Some(Color::White)), + [ + ply!(C6 - D8), + ply!(C6 - D8), + ply!(C6 - E7), + ply!(C6 - E5), + ply!(C6 - D4), + ply!(C6 - B4), + ply!(C6 - A5), + ply!(C6 - A7), + ply!(C6 - B8), + ply!(F5 - G7), + ply!(F5 - H6), + ply!(F5 - H4), + ply!(F5 - G3), + ply!(F5 - E3), + ply!(F5 - D4), + ply!(F5 - D6), + ply!(F5 - E7), + ] + ); + } + + #[test] + fn white_f5_moves_with_blockers() { + let board = test_board!( + White Knight on F5, + White Queen on G3, + White Bishop on D6, + ); + assert_move_list!( + KnightMoveGenerator::new(&board, Some(Color::White)), + [ + ply!(F5 - G7), + ply!(F5 - H6), + ply!(F5 - H4), + ply!(F5 - E3), + ply!(F5 - D4), + ply!(F5 - E7), + ] + ); + } + + #[test] + fn white_f5_moves_with_captures() { + let board = test_board!( + White Knight on F5, + White Queen on G3, + White Bishop on D6, + Black Queen on D4, + ); + assert_move_list!( + KnightMoveGenerator::new(&board, Some(Color::White)), + [ + ply!(F5 - G7), + ply!(F5 - H6), + ply!(F5 - H4), + ply!(F5 - E3), + ply!(F5 - E7), + ply!(F5 x D4), + ] + ); + } +}