use kish::{Board, Game, Square, State, Team};
fn validate_path(path: &[Square]) -> Result<(), String> {
let mut visited = std::collections::HashSet::new();
for (i, square) in path.iter().enumerate() {
let notation = square.to_string();
if !visited.insert(notation.clone()) {
return Err(format!(
"Square {} visited multiple times in path",
notation
));
}
if i > 0 {
let prev = &path[i - 1];
let prev_row = prev.row();
let prev_col = prev.column();
let curr_row = square.row();
let curr_col = square.column();
if prev_row != curr_row && prev_col != curr_col {
return Err(format!(
"Invalid move {}→{}: not on same row or column (diagonal moves not allowed)",
prev, square
));
}
}
}
Ok(())
}
#[test]
fn test_impossible_capture_path_bug() {
let white: u64 = 288230376571242752;
let black: u64 = 23104780763204;
let kings: u64 = 288230376151711812;
let state = State::new([white, black], kings);
let board = Board::new(Team::White, state);
let game = Game::from_board(board);
let actions = game.actions();
println!("Legal moves ({} total):", actions.len());
let mut found_bug = false;
for action in &actions {
let detailed = action.to_detailed(Team::White, &board.state);
let path = detailed.path();
let notation = detailed.to_notation();
let path_strs: Vec<_> = path.iter().map(|s| s.to_string()).collect();
println!(" Move: {} (path: {:?})", notation, path_strs);
if let Err(e) = validate_path(path) {
println!(" *** BUG: {} ***", e);
found_bug = true;
}
}
assert!(
!found_bug,
"Found geometrically impossible moves - see output above"
);
}
#[test]
fn test_a5_to_h1_impossible() {
let a5 = Square::A5;
let h1 = Square::H1;
let a5_row = a5.row(); let a5_col = a5.column(); let h1_row = h1.row(); let h1_col = h1.column();
assert_ne!(a5_row, h1_row, "A5 and H1 should NOT be on same row");
assert_ne!(a5_col, h1_col, "A5 and H1 should NOT be on same column");
println!("A5 is at row={}, col={}", a5_row, a5_col);
println!("H1 is at row={}, col={}", h1_row, h1_col);
println!(
"A5→H1 requires moving {} rows and {} columns",
(h1_row as i8 - a5_row as i8).abs(),
(h1_col as i8 - a5_col as i8).abs()
);
}
#[test]
fn all_actions_have_valid_paths() {
let game = Game::default();
validate_all_actions_have_valid_paths(&game);
}
#[test]
fn midgame_position_all_actions_valid() {
let white = Square::A2.to_mask() | Square::D4.to_mask() | Square::H8.to_mask();
let black =
Square::B3.to_mask() | Square::C4.to_mask() | Square::E5.to_mask() | Square::F6.to_mask();
let kings = Square::H8.to_mask();
let state = State::new([white, black], kings);
let board = Board::new(Team::White, state);
let game = Game::from_board(board);
validate_all_actions_have_valid_paths(&game);
}
#[test]
fn king_multi_capture_requires_specific_landing() {
let white = Square::A1.to_mask();
let black = Square::B3.to_mask() | Square::D5.to_mask();
let kings = Square::A1.to_mask();
let state = State::new([white, black], kings);
let board = Board::new(Team::White, state);
let game = Game::from_board(board);
validate_all_actions_have_valid_paths(&game);
}
#[test]
fn king_edge_captures() {
let white = Square::A8.to_mask();
let black = Square::C8.to_mask() | Square::E8.to_mask() | Square::G8.to_mask();
let kings = Square::A8.to_mask();
let state = State::new([white, black], kings);
let board = Board::new(Team::White, state);
let game = Game::from_board(board);
validate_all_actions_have_valid_paths(&game);
}
#[test]
fn king_corner_captures() {
let white = Square::A1.to_mask();
let black = Square::A3.to_mask() | Square::C1.to_mask();
let kings = Square::A1.to_mask();
let state = State::new([white, black], kings);
let board = Board::new(Team::White, state);
let game = Game::from_board(board);
validate_all_actions_have_valid_paths(&game);
}
#[test]
fn king_long_capture_chain() {
let white = Square::A8.to_mask();
let black = Square::A6.to_mask()
| Square::A4.to_mask()
| Square::A2.to_mask()
| Square::G6.to_mask()
| Square::G4.to_mask();
let kings = Square::A8.to_mask();
let state = State::new([white, black], kings);
let board = Board::new(Team::White, state);
let game = Game::from_board(board);
validate_all_actions_have_valid_paths(&game);
}
#[test]
fn black_king_multi_capture() {
let white = Square::B3.to_mask() | Square::D5.to_mask();
let black = Square::A1.to_mask();
let kings = Square::A1.to_mask();
let state = State::new([white, black], kings);
let board = Board::new(Team::Black, state);
let game = Game::from_board(board);
validate_all_actions_have_valid_paths(&game);
}
#[test]
fn pawn_multi_capture_path() {
let white = Square::A3.to_mask();
let black = Square::B3.to_mask() | Square::D3.to_mask();
let kings = 0;
let state = State::new([white, black], kings);
let board = Board::new(Team::White, state);
let game = Game::from_board(board);
validate_all_actions_have_valid_paths(&game);
}
#[test]
fn mixed_pieces_captures() {
let white = Square::A1.to_mask() | Square::A3.to_mask(); let black = Square::B3.to_mask() | Square::C3.to_mask() | Square::A5.to_mask();
let kings = Square::A1.to_mask();
let state = State::new([white, black], kings);
let board = Board::new(Team::White, state);
let game = Game::from_board(board);
validate_all_actions_have_valid_paths(&game);
}
#[test]
fn stress_test_path_reconstruction() {
let mut game = Game::default();
for _ in 0..20 {
validate_all_actions_have_valid_paths(&game);
let actions = game.actions();
if actions.is_empty() {
break;
}
game.make_move(&actions[0]);
}
}
#[test]
fn original_bug_pattern_variations() {
let test_cases = [
(
Square::C8.to_mask(),
Square::C6.to_mask()
| Square::C4.to_mask()
| Square::A4.to_mask()
| Square::A2.to_mask(),
Square::C8.to_mask(),
Team::White,
),
(
Square::H1.to_mask(),
Square::H3.to_mask() | Square::F3.to_mask() | Square::D3.to_mask(),
Square::H1.to_mask(),
Team::White,
),
(
Square::A1.to_mask(),
Square::A3.to_mask()
| Square::A5.to_mask()
| Square::C5.to_mask()
| Square::E5.to_mask(),
Square::A1.to_mask(),
Team::White,
),
];
for (white, black, kings, turn) in test_cases {
let state = State::new([white, black], kings);
let board = Board::new(turn, state);
let game = Game::from_board(board);
validate_all_actions_have_valid_paths(&game);
}
}
fn validate_all_actions_have_valid_paths(game: &Game) {
let board = game.board();
let actions = game.actions();
for action in &actions {
let detailed = action.to_detailed(board.turn, &board.state);
let path = detailed.path();
assert!(!path.is_empty(), "Path should not be empty");
if let Err(e) = validate_path(path) {
panic!(
"Invalid path for action {}: {}\nPath: {:?}",
detailed.to_notation(),
e,
path.iter().map(|s| s.to_string()).collect::<Vec<_>>()
);
}
}
}