use crate::{
constants::{FILE_A, FILE_H}, model::{bitboard::BitBoard, gameboard::GameBoard, piecemove::PieceMove},
};
pub const MAX_ROOK_MOVES: usize = 28;
pub(crate) fn generate_rook_moves(state: &GameBoard) -> ([PieceMove; MAX_ROOK_MOVES], usize) {
let mut moves = [PieceMove::NULL; MAX_ROOK_MOVES];
let mut count = 0;
let all_occupied =
state.pawns | state.knights | state.bishops | state.rooks | state.queens | state.kings;
let (my_rooks, other_pieces): (BitBoard, u64) = if state.playing {
(
state.rooks & state.colour,
(all_occupied & !state.colour).into(),
)
} else {
(
state.rooks & !state.colour,
(all_occupied & state.colour).into(),
)
};
let mut ray_attackers: u64 = my_rooks.into();
for i in 1..8 {
ray_attackers <<= 8;
let mut captures = ray_attackers & other_pieces;
while captures != 0 {
let to_board = captures.trailing_zeros() as u8;
let from_board = to_board - (i * 8);
moves[count] = PieceMove::new(from_board, to_board, true, None);
count += 1;
captures &= captures - 1;
}
let blockers = ray_attackers & all_occupied.raw();
ray_attackers &= !blockers;
let mut quiet_moves = ray_attackers;
while quiet_moves != 0 {
let to_board = quiet_moves.trailing_zeros() as u8;
let from_board = to_board - (i * 8);
moves[count] = PieceMove::new(from_board, to_board, false, None);
count += 1;
quiet_moves &= quiet_moves - 1;
}
if ray_attackers == 0 {
break;
}
}
ray_attackers = my_rooks.into();
for i in 1..8 {
ray_attackers <<= 1;
ray_attackers &= !FILE_A;
let mut captures = ray_attackers & other_pieces;
while captures != 0 {
let to_board = captures.trailing_zeros() as u8;
let from_board = to_board - i;
moves[count] = PieceMove::new(from_board, to_board, true, None);
count += 1;
captures &= captures - 1;
}
let blockers = ray_attackers & all_occupied.raw();
ray_attackers &= !blockers;
let mut quiet_moves = ray_attackers;
while quiet_moves != 0 {
let to_board = quiet_moves.trailing_zeros() as u8;
let from_board = to_board - i;
moves[count] = PieceMove::new(from_board, to_board, false, None);
count += 1;
quiet_moves &= quiet_moves - 1;
}
if ray_attackers == 0 {
break;
}
}
ray_attackers = my_rooks.into();
for i in 1..8 {
ray_attackers >>= 8;
let mut captures = ray_attackers & other_pieces;
while captures != 0 {
let to_board = captures.trailing_zeros() as u8;
let from_board = to_board + (i * 8);
moves[count] = PieceMove::new(from_board, to_board, true, None);
count += 1;
captures &= captures - 1;
}
let blockers = ray_attackers & all_occupied.raw();
ray_attackers &= !blockers;
let mut quiet_moves = ray_attackers;
while quiet_moves != 0 {
let to_board = quiet_moves.trailing_zeros() as u8;
let from_board = to_board + (i * 8);
moves[count] = PieceMove::new(from_board, to_board, false, None);
count += 1;
quiet_moves &= quiet_moves - 1;
}
if ray_attackers == 0 {
break;
}
}
ray_attackers = my_rooks.into();
for i in 1..8 {
ray_attackers >>= 1;
ray_attackers &= !FILE_H;
let mut captures = ray_attackers & other_pieces;
while captures != 0 {
let to_board = captures.trailing_zeros() as u8;
let from_board = to_board + i;
moves[count] = PieceMove::new(from_board, to_board, true, None);
count += 1;
captures &= captures - 1;
}
let blockers = ray_attackers & all_occupied.raw();
ray_attackers &= !blockers;
let mut quiet_moves = ray_attackers;
while quiet_moves != 0 {
let to_board = quiet_moves.trailing_zeros() as u8;
let from_board = to_board + i;
moves[count] = PieceMove::new(from_board, to_board, false, None);
count += 1;
quiet_moves &= quiet_moves - 1;
}
if ray_attackers == 0 {
break;
}
}
(moves, count)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::constants::*;
use crate::model::gamedata::GameData;
use crate::model::piecemove::PieceMove;
fn sort_and_compare_moves(mut moves: Vec<PieceMove>) -> Vec<PieceMove> {
moves.sort();
moves
}
#[test]
fn test_single_white_rook_center() {
let board = GameData::from_fen("8/8/8/8/3R4/8/8/8 w - - 0 1").unwrap();
let (moves, count) = generate_rook_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(D4, D5, false, None),
PieceMove::new(D4, D6, false, None),
PieceMove::new(D4, D7, false, None),
PieceMove::new(D4, D8, false, None),
PieceMove::new(D4, E4, false, None),
PieceMove::new(D4, F4, false, None),
PieceMove::new(D4, G4, false, None),
PieceMove::new(D4, H4, false, None),
PieceMove::new(D4, D3, false, None),
PieceMove::new(D4, D2, false, None),
PieceMove::new(D4, D1, false, None),
PieceMove::new(D4, C4, false, None),
PieceMove::new(D4, B4, false, None),
PieceMove::new(D4, A4, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_single_black_rook_center() {
let board = GameData::from_fen("8/8/8/8/3r4/8/8/8 b - - 0 1").unwrap();
let (moves, count) = generate_rook_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(D4, D5, false, None),
PieceMove::new(D4, D6, false, None),
PieceMove::new(D4, D7, false, None),
PieceMove::new(D4, D8, false, None),
PieceMove::new(D4, E4, false, None),
PieceMove::new(D4, F4, false, None),
PieceMove::new(D4, G4, false, None),
PieceMove::new(D4, H4, false, None),
PieceMove::new(D4, D3, false, None),
PieceMove::new(D4, D2, false, None),
PieceMove::new(D4, D1, false, None),
PieceMove::new(D4, C4, false, None),
PieceMove::new(D4, B4, false, None),
PieceMove::new(D4, A4, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_white_rook_corner_a1() {
let board = GameData::from_fen("8/8/8/8/8/8/8/R7 w - - 0 1").unwrap();
let (moves, count) = generate_rook_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(A1, A2, false, None),
PieceMove::new(A1, A3, false, None),
PieceMove::new(A1, A4, false, None),
PieceMove::new(A1, A5, false, None),
PieceMove::new(A1, A6, false, None),
PieceMove::new(A1, A7, false, None),
PieceMove::new(A1, A8, false, None),
PieceMove::new(A1, B1, false, None),
PieceMove::new(A1, C1, false, None),
PieceMove::new(A1, D1, false, None),
PieceMove::new(A1, E1, false, None),
PieceMove::new(A1, F1, false, None),
PieceMove::new(A1, G1, false, None),
PieceMove::new(A1, H1, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_white_rook_corner_h8() {
let board = GameData::from_fen("7R/8/8/8/8/8/8/8 w - - 0 1").unwrap();
let (moves, count) = generate_rook_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(H8, H7, false, None),
PieceMove::new(H8, H6, false, None),
PieceMove::new(H8, H5, false, None),
PieceMove::new(H8, H4, false, None),
PieceMove::new(H8, H3, false, None),
PieceMove::new(H8, H2, false, None),
PieceMove::new(H8, H1, false, None),
PieceMove::new(H8, G8, false, None),
PieceMove::new(H8, F8, false, None),
PieceMove::new(H8, E8, false, None),
PieceMove::new(H8, D8, false, None),
PieceMove::new(H8, C8, false, None),
PieceMove::new(H8, B8, false, None),
PieceMove::new(H8, A8, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_white_rook_captures() {
let board = GameData::from_fen("8/8/8/3p4/1p1R1p2/8/3p4/8 w - - 0 1").unwrap();
let (moves, count) = generate_rook_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(D4, D5, true, None), PieceMove::new(D4, E4, false, None),
PieceMove::new(D4, F4, true, None), PieceMove::new(D4, D3, false, None),
PieceMove::new(D4, D2, true, None), PieceMove::new(D4, C4, false, None),
PieceMove::new(D4, B4, true, None), ];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_black_rook_captures() {
let board = GameData::from_fen("8/8/8/3P4/1P1r1P2/8/3P4/8 b - - 0 1").unwrap();
let (moves, count) = generate_rook_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(D4, D5, true, None), PieceMove::new(D4, E4, false, None),
PieceMove::new(D4, F4, true, None), PieceMove::new(D4, D3, false, None),
PieceMove::new(D4, D2, true, None), PieceMove::new(D4, C4, false, None),
PieceMove::new(D4, B4, true, None), ];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_rook_blocked_by_own_pieces() {
let board = GameData::from_fen("8/8/8/3P4/1P1R1P2/8/3P4/8 w - - 0 1").unwrap();
let (moves, count) = generate_rook_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(D4, D3, false, None),
PieceMove::new(D4, E4, false, None),
PieceMove::new(D4, C4, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_rook_partially_blocked() {
let board = GameData::from_fen("8/8/8/8/3R4/8/1P6/8 w - - 0 1").unwrap();
let (moves, count) = generate_rook_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(D4, D5, false, None),
PieceMove::new(D4, D6, false, None),
PieceMove::new(D4, D7, false, None),
PieceMove::new(D4, D8, false, None),
PieceMove::new(D4, E4, false, None),
PieceMove::new(D4, F4, false, None),
PieceMove::new(D4, G4, false, None),
PieceMove::new(D4, H4, false, None),
PieceMove::new(D4, D3, false, None),
PieceMove::new(D4, D2, false, None),
PieceMove::new(D4, D1, false, None),
PieceMove::new(D4, C4, false, None),
PieceMove::new(D4, B4, false, None),
PieceMove::new(D4, A4, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_multiple_rooks() {
let board = GameData::from_fen("8/8/8/8/3R4/8/8/R7 w - - 0 1").unwrap();
let (moves, count) = generate_rook_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let mut d4_moves = vec![
PieceMove::new(D4, D1, false, None),
PieceMove::new(D4, D2, false, None),
PieceMove::new(D4, D3, false, None),
PieceMove::new(D4, D5, false, None),
PieceMove::new(D4, D6, false, None),
PieceMove::new(D4, D7, false, None),
PieceMove::new(D4, D8, false, None),
PieceMove::new(D4, A4, false, None),
PieceMove::new(D4, B4, false, None),
PieceMove::new(D4, C4, false, None),
PieceMove::new(D4, E4, false, None),
PieceMove::new(D4, F4, false, None),
PieceMove::new(D4, G4, false, None),
PieceMove::new(D4, H4, false, None),
];
let mut a1_moves = vec![
PieceMove::new(A1, A2, false, None),
PieceMove::new(A1, A3, false, None),
PieceMove::new(A1, A4, false, None),
PieceMove::new(A1, A5, false, None),
PieceMove::new(A1, A6, false, None),
PieceMove::new(A1, A7, false, None),
PieceMove::new(A1, A8, false, None),
PieceMove::new(A1, B1, false, None),
PieceMove::new(A1, C1, false, None),
PieceMove::new(A1, D1, false, None),
PieceMove::new(A1, E1, false, None),
PieceMove::new(A1, F1, false, None),
PieceMove::new(A1, G1, false, None),
PieceMove::new(A1, H1, false, None),
];
let mut expected_moves = Vec::new();
expected_moves.append(&mut d4_moves);
expected_moves.append(&mut a1_moves);
assert!(expected_moves.len() == MAX_ROOK_MOVES);
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_rook_edge_cases() {
let board = GameData::from_fen("8/8/8/8/R7/8/8/8 w - - 0 1").unwrap();
let (moves, count) = generate_rook_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(A4, A5, false, None),
PieceMove::new(A4, A6, false, None),
PieceMove::new(A4, A7, false, None),
PieceMove::new(A4, A8, false, None),
PieceMove::new(A4, B4, false, None),
PieceMove::new(A4, C4, false, None),
PieceMove::new(A4, D4, false, None),
PieceMove::new(A4, E4, false, None),
PieceMove::new(A4, F4, false, None),
PieceMove::new(A4, G4, false, None),
PieceMove::new(A4, H4, false, None),
PieceMove::new(A4, A3, false, None),
PieceMove::new(A4, A2, false, None),
PieceMove::new(A4, A1, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_no_rooks() {
let board = GameData::from_fen("8/8/8/8/8/8/8/8 w - - 0 1").unwrap();
let (_moves, count) = generate_rook_moves(&board.board);
assert_eq!(count, 0);
}
#[test]
fn test_rook_initial_position() {
let board =
GameData::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
let (_moves, count) = generate_rook_moves(&board.board);
assert_eq!(count, 0);
}
#[test]
fn test_rook_after_pawn_moves() {
let board =
GameData::from_fen("rnbqkbnr/pppppppp/8/8/8/7P/PPPPPPP1/RNBQKBNR w KQkq - 0 1").unwrap();
let (moves, count) = generate_rook_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(H1, H2, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_complex_rook_position() {
let board = GameData::from_fen("r3k2r/pppppppp/8/8/8/8/PPPPPPPP/RN2K1NR w KQkq - 0 1").unwrap();
let (moves, count) = generate_rook_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
drop(generated_moves);
assert_eq!(count, 0);
}
#[test]
fn test_rook_open_files() {
let board = GameData::from_fen("8/8/8/8/8/8/8/3R4 w - - 0 1").unwrap();
let (moves, count) = generate_rook_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(D1, D2, false, None),
PieceMove::new(D1, D3, false, None),
PieceMove::new(D1, D4, false, None),
PieceMove::new(D1, D5, false, None),
PieceMove::new(D1, D6, false, None),
PieceMove::new(D1, D7, false, None),
PieceMove::new(D1, D8, false, None),
PieceMove::new(D1, E1, false, None),
PieceMove::new(D1, F1, false, None),
PieceMove::new(D1, G1, false, None),
PieceMove::new(D1, H1, false, None),
PieceMove::new(D1, C1, false, None),
PieceMove::new(D1, B1, false, None),
PieceMove::new(D1, A1, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_rook_x_ray_attacks() {
let board = GameData::from_fen("8/8/8/3p4/3R4/3p4/8/8 w - - 0 1").unwrap();
let (moves, count) = generate_rook_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(D4, D5, true, None), PieceMove::new(D4, E4, false, None),
PieceMove::new(D4, F4, false, None),
PieceMove::new(D4, G4, false, None),
PieceMove::new(D4, H4, false, None),
PieceMove::new(D4, D3, true, None), PieceMove::new(D4, C4, false, None),
PieceMove::new(D4, B4, false, None),
PieceMove::new(D4, A4, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
}