[moves] Knight move generator and tests

This commit is contained in:
Eryn Wells 2025-05-25 11:05:10 -07:00
parent 3f3842c7c8
commit faca844733
3 changed files with 170 additions and 0 deletions

View file

@ -1,10 +1,12 @@
// Eryn Wells <eryn@erynwells.me>
mod knight;
mod pawn;
#[cfg(test)]
mod testing;
pub use knight::KnightMoveGenerator;
pub use pawn::PawnMoveGenerator;
use crate::Move;

View file

@ -0,0 +1,164 @@
// Eryn Wells <eryn@erynwells.me>
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<TrailingBitScanner>,
current_origin: Option<Square>,
friends: BitBoard,
enemies: BitBoard,
}
impl KnightMoveGenerator {
pub fn new(board: &Board, color: Option<Color>) -> 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<Self::Item> {
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),
]
);
}
}