[position] Implement all the example positions from Peter Ellis Jones' blog post
https://peterellisjones.com/posts/generating-legal-chess-moves-efficiently/
This commit is contained in:
parent
6c14851806
commit
f4e57d7d6c
5 changed files with 282 additions and 3 deletions
|
@ -1,3 +1,4 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
mod peterellisjones;
|
||||
mod single_pieces;
|
||||
|
|
226
position/src/move_generator/tests/peterellisjones.rs
Normal file
226
position/src/move_generator/tests/peterellisjones.rs
Normal file
|
@ -0,0 +1,226 @@
|
|||
// 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, move_generator::Moves, r#move::AlgebraicMoveFormatter,
|
||||
test_position, Move, MoveBuilder,
|
||||
};
|
||||
use chessfriend_core::{piece, Square};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[test]
|
||||
fn pseudo_legal_move_generation() -> Result<(), String> {
|
||||
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("No valid king moves")?;
|
||||
|
||||
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() -> Result<(), String> {
|
||||
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("No valid king moves")?;
|
||||
|
||||
assert!(!king_moves.can_move_to_square(Square::E8));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[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.moves();
|
||||
|
||||
let expected_moves = HashSet::from_iter([
|
||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::D8).build(),
|
||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::E7).build(),
|
||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::F7).build(),
|
||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::F8).build(),
|
||||
]);
|
||||
|
||||
assert_move_list!(
|
||||
generated_moves.iter().collect::<HashSet<_>>(),
|
||||
expected_moves,
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
#[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.moves();
|
||||
|
||||
let expected_moves = HashSet::from_iter([
|
||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::D8).build(),
|
||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::D7).build(),
|
||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::F7).build(),
|
||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::F8).build(),
|
||||
]);
|
||||
|
||||
assert_move_list!(
|
||||
generated_moves.iter().collect::<HashSet<_>>(),
|
||||
expected_moves,
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
#[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.moves();
|
||||
|
||||
let expected_moves = HashSet::from_iter([
|
||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::D8).build(),
|
||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::D7).build(),
|
||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::F7).build(),
|
||||
MoveBuilder::new(piece!(Black King), Square::E8, Square::F8).build(),
|
||||
MoveBuilder::new(piece!(Black Knight), Square::G6, Square::E7).build(),
|
||||
MoveBuilder::new(piece!(Black Knight), Square::G6, Square::E5)
|
||||
.capturing(piece!(White Rook on E5))
|
||||
.build(),
|
||||
]);
|
||||
|
||||
assert_move_list!(
|
||||
generated_moves.iter().collect::<HashSet<_>>(),
|
||||
expected_moves,
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
#[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.is_king_in_check());
|
||||
|
||||
let generated_moves = pos.moves().iter().collect::<HashSet<_>>();
|
||||
|
||||
assert!(
|
||||
generated_moves.contains(
|
||||
&MoveBuilder::new(piece!(Black Pawn), Square::E4, Square::D3)
|
||||
.capturing_en_passant(piece!(White Pawn on D4))
|
||||
.build()
|
||||
),
|
||||
"Valid moves: {:?}",
|
||||
formatted_move_list!(generated_moves, pos)
|
||||
);
|
||||
}
|
||||
|
||||
#[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.is_king_in_check());
|
||||
|
||||
let generated_moves = pos.moves().iter().collect::<HashSet<_>>();
|
||||
|
||||
assert!(
|
||||
generated_moves.contains(
|
||||
&MoveBuilder::new(piece!(Black Pawn), Square::E4, Square::D3)
|
||||
.capturing_en_passant(piece!(White Pawn on D4))
|
||||
.build()
|
||||
),
|
||||
"Valid moves: {:?}",
|
||||
formatted_move_list!(generated_moves, pos)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pinned_pieces_rook_cannot_move_out_of_pin() -> Result<(), String> {
|
||||
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("No valid rook moves")?;
|
||||
|
||||
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));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[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 = pos.moves().iter().collect::<HashSet<_>>();
|
||||
|
||||
assert!(
|
||||
generated_moves.contains(
|
||||
&MoveBuilder::new(piece!(Black Pawn), Square::E4, Square::D3)
|
||||
.capturing_en_passant(piece!(White Pawn on D4))
|
||||
.build()
|
||||
),
|
||||
"Valid moves: {:?}",
|
||||
formatted_move_list!(generated_moves, pos)
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue