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,
|
push_mask: chessfriend_bitboard::BitBoard,
|
||||||
) -> $name {
|
) -> $name {
|
||||||
let move_sets = if Self::shape() == chessfriend_core::Shape::King
|
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)
|
Self::move_sets(position, color, capture_mask, push_mask)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -37,7 +37,9 @@ impl MoveGeneratorInternal for PawnMoveGenerator {
|
||||||
.quiet_moves(quiet_moves)
|
.quiet_moves(quiet_moves)
|
||||||
.capture_moves(capture_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);
|
move_set.en_passant(en_passant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +54,7 @@ impl PawnMoveGenerator {
|
||||||
capture_mask: BitBoard,
|
capture_mask: BitBoard,
|
||||||
push_mask: BitBoard,
|
push_mask: BitBoard,
|
||||||
) -> Self {
|
) -> 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)
|
Self::move_sets(position, player_to_move, capture_mask, push_mask)
|
||||||
} else {
|
} else {
|
||||||
std::collections::BTreeMap::new()
|
std::collections::BTreeMap::new()
|
||||||
|
@ -121,14 +123,26 @@ impl PawnMoveGenerator {
|
||||||
BitBoard::pawn_attacks(piece.square(), color) & opponent_pieces
|
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() {
|
match position.en_passant() {
|
||||||
Some(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();
|
if (target_square & push_mask).is_empty()
|
||||||
let capture =
|
&& (capture_square & capture_mask).is_empty()
|
||||||
BitBoard::pawn_attacks(piece.square(), piece.color()) & en_passant_bitboard;
|
{
|
||||||
|
// 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() {
|
if capture.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -141,12 +155,28 @@ impl PawnMoveGenerator {
|
||||||
None => None,
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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_core::{piece, Color, Square};
|
||||||
use chessfriend_moves::{Builder as MoveBuilder, Move};
|
use chessfriend_moves::{Builder as MoveBuilder, Move};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
@ -292,4 +322,31 @@ mod tests {
|
||||||
|
|
||||||
Ok(())
|
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());
|
assert!(pos.is_king_in_check());
|
||||||
|
|
||||||
let generated_moves = pos.moves().iter().collect::<HashSet<_>>();
|
let generated_moves: HashSet<_> = pos.moves().iter().collect();
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
generated_moves.contains(
|
generated_moves.contains(
|
||||||
&MoveBuilder::push(&piece!(Black Pawn on E4))
|
&MoveBuilder::push(&piece!(Black Pawn on E4))
|
||||||
.capturing_en_passant_on(Square::D4)
|
.capturing_en_passant_on(Square::D3)
|
||||||
.build()?
|
.build()?
|
||||||
),
|
),
|
||||||
"Valid moves: {:?}",
|
"Valid moves: {:?}",
|
||||||
|
@ -171,12 +171,12 @@ fn en_passant_check_block() -> TestResult {
|
||||||
|
|
||||||
assert!(pos.is_king_in_check());
|
assert!(pos.is_king_in_check());
|
||||||
|
|
||||||
let generated_moves = pos.moves().iter().collect::<HashSet<_>>();
|
let generated_moves: HashSet<_> = pos.moves().iter().collect();
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
generated_moves.contains(
|
generated_moves.contains(
|
||||||
&MoveBuilder::push(&piece!(Black Pawn on E4))
|
&MoveBuilder::push(&piece!(Black Pawn on E4))
|
||||||
.capturing_en_passant_on(Square::D4)
|
.capturing_en_passant_on(Square::D3)
|
||||||
.build()?
|
.build()?
|
||||||
),
|
),
|
||||||
"Valid moves: {:?}",
|
"Valid moves: {:?}",
|
||||||
|
@ -187,7 +187,7 @@ fn en_passant_check_block() -> TestResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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, [
|
let pos = test_position!(Black, [
|
||||||
Black King on E8,
|
Black King on E8,
|
||||||
Black Rook on E6,
|
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 generated_moves = pos.moves();
|
||||||
let rook_moves = generated_moves
|
let rook_moves = generated_moves
|
||||||
.moves_for_piece(&piece!(Black Rook on E6))
|
.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::D6));
|
||||||
assert!(!rook_moves.can_move_to_square(Square::F6));
|
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::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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -222,10 +222,10 @@ fn en_passant_discovered_check() -> TestResult {
|
||||||
White Queen on H4,
|
White Queen on H4,
|
||||||
], D3);
|
], 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))
|
let unexpected_move = MoveBuilder::push(&piece!(Black Pawn on E4))
|
||||||
.capturing_en_passant_on(Square::D4)
|
.capturing_en_passant_on(Square::D3)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue