chessfriend/position/src/move_generator/tests/peterellisjones.rs

238 lines
6.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 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(())
}