Fix a couple of the obscure en passant cases from PEJ
This commit is contained in:
parent
673d57c02e
commit
8f07e08500
3 changed files with 77 additions and 20 deletions
|
@ -56,7 +56,7 @@ macro_rules! move_generator_declaration {
|
|||
push_mask: chessfriend_bitboard::BitBoard,
|
||||
) -> $name {
|
||||
let move_sets = if Self::shape() == chessfriend_core::Shape::King
|
||||
|| (!capture_mask.is_empty() && !push_mask.is_empty())
|
||||
|| !(capture_mask.is_empty() && push_mask.is_empty())
|
||||
{
|
||||
Self::move_sets(position, color, capture_mask, push_mask)
|
||||
} else {
|
||||
|
|
|
@ -37,7 +37,9 @@ impl MoveGeneratorInternal for PawnMoveGenerator {
|
|||
.quiet_moves(quiet_moves)
|
||||
.capture_moves(capture_moves);
|
||||
|
||||
if let Some(en_passant) = Self::en_passant(position, placed_piece) {
|
||||
if let Some(en_passant) =
|
||||
Self::en_passant(position, placed_piece, &push_mask, &capture_mask)
|
||||
{
|
||||
move_set.en_passant(en_passant);
|
||||
}
|
||||
|
||||
|
@ -52,7 +54,7 @@ impl PawnMoveGenerator {
|
|||
capture_mask: BitBoard,
|
||||
push_mask: BitBoard,
|
||||
) -> Self {
|
||||
let move_sets = if !capture_mask.is_empty() && !push_mask.is_empty() {
|
||||
let move_sets = if !(capture_mask.is_empty() && push_mask.is_empty()) {
|
||||
Self::move_sets(position, player_to_move, capture_mask, push_mask)
|
||||
} else {
|
||||
std::collections::BTreeMap::new()
|
||||
|
@ -121,14 +123,26 @@ impl PawnMoveGenerator {
|
|||
BitBoard::pawn_attacks(piece.square(), color) & opponent_pieces
|
||||
}
|
||||
|
||||
fn en_passant(position: &Position, piece: &PlacedPiece) -> Option<EnPassant> {
|
||||
fn en_passant(
|
||||
position: &Position,
|
||||
piece: &PlacedPiece,
|
||||
push_mask: &BitBoard,
|
||||
capture_mask: &BitBoard,
|
||||
) -> Option<EnPassant> {
|
||||
match position.en_passant() {
|
||||
Some(en_passant) => {
|
||||
let target_square = en_passant.target_square();
|
||||
let target_square: BitBoard = en_passant.target_square().into();
|
||||
let capture_square: BitBoard = en_passant.capture_square().into();
|
||||
|
||||
let en_passant_bitboard: BitBoard = target_square.into();
|
||||
let capture =
|
||||
BitBoard::pawn_attacks(piece.square(), piece.color()) & en_passant_bitboard;
|
||||
if (target_square & push_mask).is_empty()
|
||||
&& (capture_square & capture_mask).is_empty()
|
||||
{
|
||||
// Do not allow en passant if capturing would not either
|
||||
// block an active check, or capture a checking pawn.
|
||||
return None;
|
||||
}
|
||||
|
||||
let capture = BitBoard::pawn_attacks(piece.square(), piece.color()) & target_square;
|
||||
if capture.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
@ -141,12 +155,28 @@ impl PawnMoveGenerator {
|
|||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(none)]
|
||||
fn does_en_passant_reveal_check(&self, position: &Position) -> bool {
|
||||
let player_to_move = position.player_to_move();
|
||||
let opposing_player = player_to_move.other();
|
||||
|
||||
if position.king_square(opposing_player).rank()
|
||||
!= Rank::PAWN_DOUBLE_PUSH_TARGET_RANKS[player_to_move as usize]
|
||||
{
|
||||
return false;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{assert_move_list, position::DiagramFormatter, test_position, testing::*};
|
||||
use crate::{
|
||||
assert_move_list, formatted_move_list, position::DiagramFormatter, test_position,
|
||||
testing::*,
|
||||
};
|
||||
use chessfriend_core::{piece, Color, Square};
|
||||
use chessfriend_moves::{Builder as MoveBuilder, Move};
|
||||
use std::collections::HashSet;
|
||||
|
@ -292,4 +322,31 @@ mod tests {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Make sure the player cannot capture en passant if doing so would not resolve the check.
|
||||
#[test]
|
||||
fn cannot_capture_en_passant_while_in_check() -> TestResult {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on B5,
|
||||
Black Pawn on E4,
|
||||
White Pawn on D4,
|
||||
White Rook on B1,
|
||||
], D3);
|
||||
|
||||
assert!(pos.is_king_in_check());
|
||||
|
||||
let generated_moves: HashSet<_> = pos.moves().iter().collect();
|
||||
|
||||
assert!(
|
||||
!generated_moves.contains(
|
||||
&MoveBuilder::push(&piece!(Black Pawn on E4))
|
||||
.capturing_en_passant_on(Square::D3)
|
||||
.build()?
|
||||
),
|
||||
"Valid moves: {:?}",
|
||||
formatted_move_list!(generated_moves, pos)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -145,12 +145,12 @@ fn en_passant_check_capture() -> TestResult {
|
|||
|
||||
assert!(pos.is_king_in_check());
|
||||
|
||||
let generated_moves = pos.moves().iter().collect::<HashSet<_>>();
|
||||
let generated_moves: HashSet<_> = pos.moves().iter().collect();
|
||||
|
||||
assert!(
|
||||
generated_moves.contains(
|
||||
&MoveBuilder::push(&piece!(Black Pawn on E4))
|
||||
.capturing_en_passant_on(Square::D4)
|
||||
.capturing_en_passant_on(Square::D3)
|
||||
.build()?
|
||||
),
|
||||
"Valid moves: {:?}",
|
||||
|
@ -171,12 +171,12 @@ fn en_passant_check_block() -> TestResult {
|
|||
|
||||
assert!(pos.is_king_in_check());
|
||||
|
||||
let generated_moves = pos.moves().iter().collect::<HashSet<_>>();
|
||||
let generated_moves: HashSet<_> = pos.moves().iter().collect();
|
||||
|
||||
assert!(
|
||||
generated_moves.contains(
|
||||
&MoveBuilder::push(&piece!(Black Pawn on E4))
|
||||
.capturing_en_passant_on(Square::D4)
|
||||
.capturing_en_passant_on(Square::D3)
|
||||
.build()?
|
||||
),
|
||||
"Valid moves: {:?}",
|
||||
|
@ -187,7 +187,7 @@ fn en_passant_check_block() -> TestResult {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn pinned_pieces_rook_cannot_move_out_of_pin() -> Result<(), String> {
|
||||
fn pinned_pieces_rook_cannot_move_out_of_pin() -> TestResult {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on E8,
|
||||
Black Rook on E6,
|
||||
|
@ -200,15 +200,15 @@ fn pinned_pieces_rook_cannot_move_out_of_pin() -> Result<(), String> {
|
|||
let generated_moves = pos.moves();
|
||||
let rook_moves = generated_moves
|
||||
.moves_for_piece(&piece!(Black Rook on E6))
|
||||
.ok_or("No valid rook moves")?;
|
||||
.ok_or(TestError::NoLegalMoves)?;
|
||||
|
||||
assert!(!rook_moves.can_move_to_square(Square::D6));
|
||||
assert!(!rook_moves.can_move_to_square(Square::F6));
|
||||
|
||||
assert!(rook_moves.can_move_to_square(Square::E7));
|
||||
assert!(rook_moves.can_move_to_square(Square::E5));
|
||||
assert!(rook_moves.can_move_to_square(Square::E4));
|
||||
assert!(rook_moves.can_move_to_square(Square::E3));
|
||||
assert!(rook_moves.can_move_to_square(Square::E4));
|
||||
assert!(rook_moves.can_move_to_square(Square::E5));
|
||||
assert!(rook_moves.can_move_to_square(Square::E7));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -222,10 +222,10 @@ fn en_passant_discovered_check() -> TestResult {
|
|||
White Queen on H4,
|
||||
], D3);
|
||||
|
||||
let generated_moves = pos.moves().iter().collect::<HashSet<_>>();
|
||||
let generated_moves: HashSet<_> = pos.moves().iter().collect();
|
||||
|
||||
let unexpected_move = MoveBuilder::push(&piece!(Black Pawn on E4))
|
||||
.capturing_en_passant_on(Square::D4)
|
||||
.capturing_en_passant_on(Square::D3)
|
||||
.build()?;
|
||||
|
||||
assert!(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue