diff --git a/moves/src/generators/pawn.rs b/moves/src/generators/pawn.rs index f68bab6..613b6e8 100644 --- a/moves/src/generators/pawn.rs +++ b/moves/src/generators/pawn.rs @@ -46,10 +46,26 @@ impl PawnMoveGenerator { let empty = !occupied; let enemies = board.enemies(color); - let (single_pushes, double_pushes) = Self::pushes(pawns, color, empty); - let (left_captures, right_captures) = Self::captures(pawns, color, enemies); + // En passant captures present a particular challenge. Include the + // target e.p. square when computing captures (i.e. treat it like an + // enemy piece is on that square) but do not include it when generating + // capture moves. If it is included, a regular capture move will be + // generated where a special e.p. move should be created instead. + // + // So, include it in the enemies set when computing captures, then + // remove it from the left and right captures bitboards before passing + // them into the move generator. Additionally, include the target e.p. + // square in the e.p. bitboard iff the capture bitboards include it. + let en_passant: BitBoard = board.en_passant_target().into(); + let (single_pushes, double_pushes) = Self::pushes(pawns, color, empty); + let (left_captures, right_captures) = Self::captures(pawns, color, enemies | en_passant); + + let en_passant = en_passant & (left_captures | right_captures); + let left_captures = left_captures & !en_passant; + let right_captures = right_captures & !en_passant; + Self { color, single_pushes, @@ -241,7 +257,7 @@ impl MoveType { #[cfg(test)] mod tests { use super::*; - use crate::{assert_move_list, ply, Move}; + use crate::{assert_move_list, assert_move_list_does_not_contain, ply, Move}; use chessfriend_board::test_board; use chessfriend_core::{Color, Square}; use std::collections::HashSet; @@ -509,4 +525,31 @@ mod tests { assert_move_list!(generated_moves, [ply!(E5 - E6), ply!(E5 x F6 e.p.),]); } + + #[test] + fn white_no_en_passant_if_no_pawn() { + let board = test_board!(White, [ + White Pawn on A3, + Black Pawn on F5, + ], F6); + + let generated_moves: HashSet<_> = PawnMoveGenerator::new(&board, None).collect(); + + assert_move_list_does_not_contain!( + generated_moves, + [ply!(E5 x F6 e.p.), ply!(G5 x F6 e.p.)] + ); + } + + #[test] + fn black_no_en_passant_if_no_pawn() { + let board = test_board!(Black, [ + White Pawn on A4, + Black Pawn on D4, + ], A3); + + let generated_moves: HashSet<_> = PawnMoveGenerator::new(&board, None).collect(); + + assert_move_list_does_not_contain!(generated_moves, [ply!(B4 x A3 e.p.)]); + } }