[board, moves, position] Make the Peter Ellis Jones gotcha unit tests work
Move this file over to position/tests. That makes it an integration test, technically. Update it to comply with the current API conventions. This is a pretty radical change from when I first wrote these! In the process of running these tests, I found a bug in my PawnMoveGenerator where it was generating the origin squares for en passant captures incorrectly. Fix those bugs and write two new tests to exercise those code paths. One of the test_board! variants was setting .active_color instead of using the setter. This is a build failure. Fix it. Move the assert_move_list! macro to the moves crate. This is a more natural home for it. Additionally, add assert_move_list_contains! and assert_move_list_does_not_contain! to generate assertions that a collection of moves (anything that implements .contains()) has or doesn't have a set of moves. Also remove formatted_move_list!, which is no longer used. All that done, make the tests pass!
This commit is contained in:
parent
d7f426697d
commit
651c982ead
11 changed files with 316 additions and 315 deletions
|
@ -32,7 +32,7 @@ macro_rules! test_board {
|
|||
chessfriend_core::Square::$square,
|
||||
$crate::PlacePieceStrategy::default());
|
||||
)*
|
||||
board.active_color = chessfriend_core::Color::$to_move;
|
||||
board.set_active_color(chessfriend_core::Color::$to_move);
|
||||
|
||||
println!("{}", board.display());
|
||||
|
||||
|
|
|
@ -6,9 +6,6 @@ mod knight;
|
|||
mod pawn;
|
||||
mod slider;
|
||||
|
||||
#[cfg(test)]
|
||||
mod testing;
|
||||
|
||||
pub use all::AllPiecesMoveGenerator;
|
||||
pub use king::KingMoveGenerator;
|
||||
pub use knight::KnightMoveGenerator;
|
||||
|
@ -28,6 +25,11 @@ impl GeneratedMove {
|
|||
pub fn origin(&self) -> Square {
|
||||
self.ply.origin_square()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn target(&self) -> Square {
|
||||
self.ply.target_square()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for GeneratedMove {
|
||||
|
|
|
@ -120,16 +120,16 @@ impl PawnMoveGenerator {
|
|||
MoveType::EnPassant => match self.color {
|
||||
Color::White => {
|
||||
if (self.en_passant & self.left_captures).is_populated() {
|
||||
target.neighbor(Direction::NorthWest, None)
|
||||
target.neighbor(Direction::SouthEast, None)
|
||||
} else {
|
||||
target.neighbor(Direction::NorthEast, None)
|
||||
target.neighbor(Direction::SouthWest, None)
|
||||
}
|
||||
}
|
||||
Color::Black => {
|
||||
if (self.en_passant & self.left_captures).is_populated() {
|
||||
target.neighbor(Direction::SouthEast, None)
|
||||
target.neighbor(Direction::NorthWest, None)
|
||||
} else {
|
||||
target.neighbor(Direction::SouthWest, None)
|
||||
target.neighbor(Direction::NorthEast, None)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -241,7 +241,7 @@ impl MoveType {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Move;
|
||||
use crate::{assert_move_list, ply, Move};
|
||||
use chessfriend_board::test_board;
|
||||
use chessfriend_core::{Color, Square};
|
||||
use std::collections::HashSet;
|
||||
|
@ -485,4 +485,28 @@ mod tests {
|
|||
.into()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn black_e4_captures_d4_en_passant() {
|
||||
let board = test_board!(Black, [
|
||||
White Pawn on D4,
|
||||
Black Pawn on E4
|
||||
], D3);
|
||||
|
||||
let generated_moves = PawnMoveGenerator::new(&board, None);
|
||||
|
||||
assert_move_list!(generated_moves, [ply!(E4 - E3), ply!(E4 x D3 e.p.),]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn white_e5_captures_f5_en_passant() {
|
||||
let board = test_board!(White, [
|
||||
White Pawn on E5,
|
||||
Black Pawn on F5
|
||||
], F6);
|
||||
|
||||
let generated_moves = PawnMoveGenerator::new(&board, None);
|
||||
|
||||
assert_move_list!(generated_moves, [ply!(E5 - E6), ply!(E5 x F6 e.p.),]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_move_list {
|
||||
($generator:expr, [ $($expected:expr),* $(,)? ]) => {
|
||||
{
|
||||
let generated_moves: std::collections::HashSet<$crate::GeneratedMove> = $generator.collect();
|
||||
let expected_moves: std::collections::HashSet<$crate::GeneratedMove> = [
|
||||
$($expected.into(),)*
|
||||
].into();
|
||||
|
||||
assert_eq!(
|
||||
generated_moves,
|
||||
expected_moves,
|
||||
"\n\tMatching: {:?}\n\tGenerated, not expected: {:?}\n\tExpected, not generated: {:?}",
|
||||
generated_moves
|
||||
.intersection(&expected_moves)
|
||||
.map(|mv| format!("{}", mv))
|
||||
.collect::<Vec<String>>(),
|
||||
generated_moves
|
||||
.difference(&expected_moves)
|
||||
.map(|mv| format!("{}", mv))
|
||||
.collect::<Vec<String>>(),
|
||||
expected_moves
|
||||
.difference(&generated_moves)
|
||||
.map(|mv| format!("{}", mv))
|
||||
.collect::<Vec<String>>(),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -5,6 +5,7 @@ pub mod testing;
|
|||
|
||||
mod builder;
|
||||
mod defs;
|
||||
mod macros;
|
||||
mod make_move;
|
||||
mod moves;
|
||||
mod record;
|
||||
|
|
77
moves/src/macros.rs
Normal file
77
moves/src/macros.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_move_list {
|
||||
($generator:expr, [ $($expected:expr),* $(,)? ]) => {
|
||||
{
|
||||
let generated_moves: std::collections::HashSet<$crate::GeneratedMove> = $generator.collect();
|
||||
let expected_moves: std::collections::HashSet<$crate::GeneratedMove> = [
|
||||
$($expected.into(),)*
|
||||
].into();
|
||||
|
||||
assert_eq!(
|
||||
generated_moves,
|
||||
expected_moves,
|
||||
"\n\tMatching: {:?}\n\tGenerated, not expected: {:?}\n\tExpected, not generated: {:?}",
|
||||
generated_moves
|
||||
.intersection(&expected_moves)
|
||||
.map(|mv| format!("{}", mv))
|
||||
.collect::<Vec<String>>(),
|
||||
generated_moves
|
||||
.difference(&expected_moves)
|
||||
.map(|mv| format!("{}", mv))
|
||||
.collect::<Vec<String>>(),
|
||||
expected_moves
|
||||
.difference(&generated_moves)
|
||||
.map(|mv| format!("{}", mv))
|
||||
.collect::<Vec<String>>(),
|
||||
);
|
||||
}
|
||||
};
|
||||
($generator:expr, $expected:expr) => {
|
||||
{
|
||||
use std::collections::HashSet;
|
||||
|
||||
let generated_moves: HashSet<$crate::GeneratedMove> = $generator.collect();
|
||||
let expected_moves: HashSet<$crate::GeneratedMove> = $expected.collect();
|
||||
|
||||
assert_eq!(
|
||||
generated_moves,
|
||||
expected_moves,
|
||||
"\n\tMatching: {:?}\n\tGenerated, not expected: {:?}\n\tExpected, not generated: {:?}",
|
||||
generated_moves
|
||||
.intersection(&expected_moves)
|
||||
.map(|mv| format!("{}", mv))
|
||||
.collect::<Vec<String>>(),
|
||||
generated_moves
|
||||
.difference(&expected_moves)
|
||||
.map(|mv| format!("{}", mv))
|
||||
.collect::<Vec<String>>(),
|
||||
expected_moves
|
||||
.difference(&generated_moves)
|
||||
.map(|mv| format!("{}", mv))
|
||||
.collect::<Vec<String>>(),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_move_list_contains {
|
||||
($generated_moves:expr, [ $($expected:expr),* $(,)? ]) => {
|
||||
$(
|
||||
assert!($generated_moves.contains(&$expected.into()));
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_move_list_does_not_contain {
|
||||
($generated_moves:expr, [ $($expected:expr),* $(,)? ]) => {
|
||||
{
|
||||
$(
|
||||
assert!(!$generated_moves.contains(&$expected.into()));
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
|
@ -5,10 +5,9 @@ mod position;
|
|||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
mod testing;
|
||||
|
||||
pub use chessfriend_board::{fen, PlacePieceError, PlacePieceStrategy};
|
||||
pub use chessfriend_moves::GeneratedMove;
|
||||
pub use position::Position;
|
||||
|
||||
#[macro_use]
|
||||
pub mod testing;
|
||||
|
|
|
@ -1,238 +0,0 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
//! Move generator tests based on board positions described in [Peter Ellis
|
||||
//! Jones][1]' excellent [blog post][2] on generated legal chess moves.
|
||||
//!
|
||||
//! [1]: https://peterellisjones.com
|
||||
//! [2]: https://peterellisjones.com/posts/generating-legal-chess-moves-efficiently/
|
||||
|
||||
use crate::{assert_move_list, formatted_move_list, test_position, testing::*};
|
||||
use chessfriend_core::{piece, Square};
|
||||
use chessfriend_moves::Builder as MoveBuilder;
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[test]
|
||||
fn pseudo_legal_move_generation() -> TestResult {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on E8,
|
||||
White King on E1,
|
||||
White Rook on F5,
|
||||
]);
|
||||
|
||||
let generated_moves = pos.moves();
|
||||
let king_moves = generated_moves
|
||||
.moves_for_piece(&piece!(Black King on E8))
|
||||
.ok_or(TestError::NoLegalMoves)?;
|
||||
|
||||
assert!(!king_moves.can_move_to_square(Square::F8));
|
||||
assert!(!king_moves.can_move_to_square(Square::F7));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gotcha_king_moves_away_from_a_checking_slider() -> TestResult {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on E7,
|
||||
White King on E1,
|
||||
White Rook on E4,
|
||||
]);
|
||||
|
||||
let generated_moves = pos.moves();
|
||||
let king_moves = generated_moves
|
||||
.moves_for_piece(&piece!(Black King on E7))
|
||||
.ok_or(TestError::NoLegalMoves)?;
|
||||
|
||||
assert!(!king_moves.can_move_to_square(Square::E8));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_evasions_1() -> TestResult {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on E8,
|
||||
White King on E1,
|
||||
White Knight on F6,
|
||||
]);
|
||||
|
||||
let generated_moves = pos.moves();
|
||||
|
||||
let builder = MoveBuilder::push(&piece!(Black King on E8));
|
||||
let expected_moves = HashSet::from_iter([
|
||||
builder.clone().to(Square::D8).build()?,
|
||||
builder.clone().to(Square::E7).build()?,
|
||||
builder.clone().to(Square::F7).build()?,
|
||||
builder.clone().to(Square::F8).build()?,
|
||||
]);
|
||||
|
||||
assert_move_list!(
|
||||
generated_moves.iter().collect::<HashSet<_>>(),
|
||||
expected_moves,
|
||||
pos
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_evasions_double_check() -> TestResult {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on E8,
|
||||
Black Bishop on F6,
|
||||
White King on E1,
|
||||
White Knight on G7,
|
||||
White Rook on E5,
|
||||
]);
|
||||
|
||||
let generated_moves = pos.moves();
|
||||
|
||||
let builder = MoveBuilder::push(&piece!(Black King on E8));
|
||||
let expected_moves = HashSet::from_iter([
|
||||
builder.clone().to(Square::D8).build()?,
|
||||
builder.clone().to(Square::D7).build()?,
|
||||
builder.clone().to(Square::F7).build()?,
|
||||
builder.clone().to(Square::F8).build()?,
|
||||
]);
|
||||
|
||||
assert_move_list!(
|
||||
generated_moves.iter().collect::<HashSet<_>>(),
|
||||
expected_moves,
|
||||
pos
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_check_with_blocker() -> TestResult {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on E8,
|
||||
Black Knight on G6,
|
||||
White King on E1,
|
||||
White Rook on E5,
|
||||
]);
|
||||
|
||||
let generated_moves = pos.moves();
|
||||
|
||||
let king_builder = MoveBuilder::push(&piece!(Black King on E8));
|
||||
let knight_builder = MoveBuilder::push(&piece!(Black Knight on G6));
|
||||
let expected_moves = HashSet::from_iter([
|
||||
king_builder.clone().to(Square::D8).build()?,
|
||||
king_builder.clone().to(Square::D7).build()?,
|
||||
king_builder.clone().to(Square::F7).build()?,
|
||||
king_builder.clone().to(Square::F8).build()?,
|
||||
knight_builder.clone().to(Square::E7).build()?,
|
||||
knight_builder.clone().capturing_on(Square::E5).build()?,
|
||||
]);
|
||||
|
||||
assert_move_list!(
|
||||
generated_moves.iter().collect::<HashSet<_>>(),
|
||||
expected_moves,
|
||||
pos
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn en_passant_check_capture() -> TestResult {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on C5,
|
||||
Black Pawn on E4,
|
||||
White Pawn on D4,
|
||||
], 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(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn en_passant_check_block() -> TestResult {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on B5,
|
||||
Black Pawn on E4,
|
||||
White Pawn on D4,
|
||||
White Queen on F1,
|
||||
], 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(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pinned_pieces_rook_cannot_move_out_of_pin() -> TestResult {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on E8,
|
||||
Black Rook on E6,
|
||||
White Queen on E3,
|
||||
White King on C1,
|
||||
]);
|
||||
|
||||
assert!(!pos.is_king_in_check());
|
||||
|
||||
let generated_moves = pos.moves();
|
||||
let rook_moves = generated_moves
|
||||
.moves_for_piece(&piece!(Black Rook on E6))
|
||||
.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::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(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn en_passant_discovered_check() -> TestResult {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on A4,
|
||||
Black Pawn on E4,
|
||||
White Pawn on D4,
|
||||
White Queen on H4,
|
||||
], D3);
|
||||
|
||||
let generated_moves: HashSet<_> = pos.moves().iter().collect();
|
||||
|
||||
let unexpected_move = MoveBuilder::push(&piece!(Black Pawn on E4))
|
||||
.capturing_en_passant_on(Square::D3)
|
||||
.build()?;
|
||||
|
||||
assert!(
|
||||
!generated_moves.contains(&unexpected_move),
|
||||
"Valid moves: {:?}",
|
||||
formatted_move_list!(generated_moves, pos)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -86,6 +86,40 @@ impl Position {
|
|||
AllPiecesMoveGenerator::new(&self.board, color)
|
||||
}
|
||||
|
||||
/// Generate legal moves.
|
||||
///
|
||||
/// ## Panics
|
||||
///
|
||||
/// If the position failed to make a move generated by the internal move
|
||||
/// generator, this method will panic.
|
||||
#[must_use]
|
||||
pub fn all_legal_moves(
|
||||
&self,
|
||||
color: Option<Color>,
|
||||
) -> Box<dyn Iterator<Item = GeneratedMove> + '_> {
|
||||
let generator = self.all_moves(color);
|
||||
|
||||
let mut test_board = self.board.clone();
|
||||
Box::new(generator.filter(move |ply| {
|
||||
let active_color_before_move = test_board.active_color();
|
||||
|
||||
println!("{:?} from:{} to:{}", ply, ply.origin(), ply.target());
|
||||
|
||||
let ply: Move = ply.clone().into();
|
||||
let record = test_board
|
||||
.make_move(ply, ValidateMove::No)
|
||||
.expect("unable to make generated move");
|
||||
|
||||
let move_is_legal = !test_board.color_is_in_check(Some(active_color_before_move));
|
||||
|
||||
test_board
|
||||
.unmake_move(&record)
|
||||
.expect("unable to unmake generated move");
|
||||
|
||||
move_is_legal
|
||||
}))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn moves_for_piece(
|
||||
&self,
|
||||
|
|
|
@ -2,39 +2,6 @@
|
|||
|
||||
use chessfriend_moves::{BuildMoveError, MakeMoveError};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_move_list {
|
||||
($generated:expr, $expected:expr, $position:expr) => {
|
||||
assert_eq!(
|
||||
$generated,
|
||||
$expected,
|
||||
"\n\tMatching: {:?}\n\tGenerated, not expected: {:?}\n\tExpected, not generated: {:?}",
|
||||
$generated
|
||||
.intersection(&$expected)
|
||||
.map(|mv| format!("{}", mv))
|
||||
.collect::<Vec<String>>(),
|
||||
$generated
|
||||
.difference(&$expected)
|
||||
.map(|mv| format!("{}", mv))
|
||||
.collect::<Vec<String>>(),
|
||||
$expected
|
||||
.difference(&$generated)
|
||||
.map(|mv| format!("{}", mv))
|
||||
.collect::<Vec<String>>(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! formatted_move_list {
|
||||
($move_list:expr, $position:expr) => {
|
||||
$move_list
|
||||
.iter()
|
||||
.map(|mv| format!("{}", mv))
|
||||
.collect::<Vec<String>>()
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_eq_bitboards {
|
||||
($result:expr, $expected:expr) => {{
|
||||
|
|
166
position/tests/peterellisjones.rs
Normal file
166
position/tests/peterellisjones.rs
Normal file
|
@ -0,0 +1,166 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
//! Move generator tests based on board positions described in [Peter Ellis
|
||||
//! Jones][1]' excellent [blog post][2] on generated legal chess moves.
|
||||
//!
|
||||
//! [1]: https://peterellisjones.com
|
||||
//! [2]: https://peterellisjones.com/posts/generating-legal-chess-moves-efficiently/
|
||||
|
||||
use chessfriend_core::{Color, Square};
|
||||
use chessfriend_moves::{
|
||||
assert_move_list, assert_move_list_contains, assert_move_list_does_not_contain, ply, Move,
|
||||
};
|
||||
use chessfriend_position::{test_position, testing::*};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[test]
|
||||
fn pseudo_legal_move_generation() {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on E8,
|
||||
White King on E1,
|
||||
White Rook on F5,
|
||||
]);
|
||||
|
||||
let king_moves: HashSet<_> = pos.all_legal_moves(Some(Color::Black)).collect();
|
||||
|
||||
assert_move_list_does_not_contain!(king_moves, [ply!(E8 - F8), ply!(E8 - F7)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gotcha_king_moves_away_from_checking_slider() {
|
||||
let position = test_position!(Black, [
|
||||
Black King on E7,
|
||||
White King on E1,
|
||||
White Rook on E4,
|
||||
]);
|
||||
|
||||
let king_moves: HashSet<Move> = position.all_legal_moves(None).map(Move::from).collect();
|
||||
|
||||
assert_move_list_does_not_contain!(king_moves, [ply!(E7 - E8)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_evasions_1() {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on E8,
|
||||
White King on E1,
|
||||
White Knight on F6,
|
||||
]);
|
||||
|
||||
let generated_moves = pos.all_legal_moves(Some(Color::Black));
|
||||
|
||||
assert_move_list!(
|
||||
generated_moves,
|
||||
[ply!(E8 - D8), ply!(E8 - E7), ply!(E8 - F7), ply!(E8 - F8),]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_evasions_double_check() {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on E8,
|
||||
Black Bishop on F6,
|
||||
White King on E1,
|
||||
White Knight on G7,
|
||||
White Rook on E5,
|
||||
]);
|
||||
|
||||
let generated_moves = pos.all_legal_moves(Some(Color::Black));
|
||||
|
||||
assert_move_list!(
|
||||
generated_moves,
|
||||
[ply!(E8 - D8), ply!(E8 - D7), ply!(E8 - F7), ply!(E8 - F8),]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_check_with_blocker() {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on E8,
|
||||
Black Knight on G6,
|
||||
White King on E1,
|
||||
White Rook on E5,
|
||||
]);
|
||||
|
||||
let generated_moves = pos.all_legal_moves(Some(Color::Black));
|
||||
|
||||
assert_move_list!(
|
||||
generated_moves,
|
||||
[
|
||||
// King moves
|
||||
ply!(E8 - D8),
|
||||
ply!(E8 - D7),
|
||||
ply!(E8 - F7),
|
||||
ply!(E8 - F8),
|
||||
// Knight moves
|
||||
ply!(G6 - E7),
|
||||
ply!(G6 x E5),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn en_passant_check_capture() {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on C5,
|
||||
Black Pawn on E4,
|
||||
White Pawn on D4,
|
||||
], D3);
|
||||
|
||||
assert!(pos.board.active_color_is_in_check());
|
||||
|
||||
let generated_moves: HashSet<_> = pos.all_legal_moves(Some(Color::Black)).collect();
|
||||
|
||||
assert_move_list_contains!(generated_moves, [ply!(E4 x D3 e.p.)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn en_passant_check_block() {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on B5,
|
||||
Black Pawn on E4,
|
||||
White Pawn on D4,
|
||||
White Queen on F1,
|
||||
], D3);
|
||||
|
||||
assert!(pos.board.active_color_is_in_check());
|
||||
|
||||
let generated_moves: HashSet<_> = pos.all_legal_moves(Some(Color::Black)).collect();
|
||||
|
||||
assert_move_list_contains!(generated_moves, [ply!(E4 x D3 e.p.)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pinned_pieces_rook_cannot_move_out_of_pin() {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on E8,
|
||||
Black Rook on E6,
|
||||
White Queen on E3,
|
||||
White King on C1,
|
||||
]);
|
||||
|
||||
assert!(!pos.board.active_color_is_in_check());
|
||||
|
||||
let rook_moves: HashSet<_> = pos.all_legal_moves(None).collect();
|
||||
|
||||
assert_move_list_does_not_contain!(rook_moves, [ply!(E6 - D6), ply!(E6 - F6)]);
|
||||
|
||||
assert_move_list_contains!(
|
||||
rook_moves,
|
||||
[ply!(E6 x E3), ply!(E6 - E4), ply!(E6 - E5), ply!(E6 - E7)]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn en_passant_discovered_check() {
|
||||
let pos = test_position!(Black, [
|
||||
Black King on A4,
|
||||
Black Pawn on E4,
|
||||
White Pawn on D4,
|
||||
White Queen on H4,
|
||||
], D3);
|
||||
|
||||
let generated_moves: HashSet<_> = pos.all_legal_moves(Some(Color::Black)).collect();
|
||||
|
||||
assert_move_list_does_not_contain!(generated_moves, [ply!(E4 x D3 e.p.)]);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue