chessfriend/position/tests/peterellisjones.rs
Eryn Wells 651c982ead [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!
2025-06-06 21:45:07 -07:00

166 lines
4.2 KiB
Rust

// 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.)]);
}