use crate::{
constants::{FILE_A, FILE_H}, model::{bitboard::BitBoard, gameboard::GameBoard, piecemove::PieceMove},
};
pub const MAX_BISHOP_MOVES: usize = 28;
pub(crate) fn generate_bishop_moves(state: &GameBoard) -> ([PieceMove; MAX_BISHOP_MOVES], usize) {
let mut moves = [PieceMove::NULL; MAX_BISHOP_MOVES];
let mut count = 0;
let all_occupied =
state.pawns | state.knights | state.bishops | state.rooks | state.queens | state.kings;
let (my_bishops, other_pieces): (BitBoard, u64) = if state.playing {
(
state.bishops & state.colour,
(all_occupied & !state.colour).into(),
)
} else {
(
state.bishops & !state.colour,
(all_occupied & state.colour).into(),
)
};
let mut ray_attackers: u64 = my_bishops.into();
for i in 1..8 {
ray_attackers = (ray_attackers << 7) & !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 * 7);
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 * 7);
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_bishops.into();
for i in 1..8 {
ray_attackers = (ray_attackers << 9) & !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 * 9);
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 * 9);
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_bishops.into();
for i in 1..8 {
ray_attackers = (ray_attackers >> 9) & !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 * 9);
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 * 9);
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_bishops.into();
for i in 1..8 {
ray_attackers = (ray_attackers >> 7) & !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 * 7);
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 * 7);
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_bishop_center() {
let board = GameData::from_fen("8/8/8/8/3B4/8/8/8 w - - 0 1").unwrap();
let (moves, count) = generate_bishop_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(D4, C5, false, None),
PieceMove::new(D4, B6, false, None),
PieceMove::new(D4, A7, false, None),
PieceMove::new(D4, E5, false, None),
PieceMove::new(D4, F6, false, None),
PieceMove::new(D4, G7, false, None),
PieceMove::new(D4, H8, false, None),
PieceMove::new(D4, C3, false, None),
PieceMove::new(D4, B2, false, None),
PieceMove::new(D4, A1, false, None),
PieceMove::new(D4, E3, false, None),
PieceMove::new(D4, F2, false, None),
PieceMove::new(D4, G1, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_single_black_bishop_center() {
let board = GameData::from_fen("8/8/8/8/3b4/8/8/8 b - - 0 1").unwrap();
let (moves, count) = generate_bishop_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(D4, C5, false, None),
PieceMove::new(D4, B6, false, None),
PieceMove::new(D4, A7, false, None),
PieceMove::new(D4, E5, false, None),
PieceMove::new(D4, F6, false, None),
PieceMove::new(D4, G7, false, None),
PieceMove::new(D4, H8, false, None),
PieceMove::new(D4, C3, false, None),
PieceMove::new(D4, B2, false, None),
PieceMove::new(D4, A1, false, None),
PieceMove::new(D4, E3, false, None),
PieceMove::new(D4, F2, false, None),
PieceMove::new(D4, G1, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_white_bishop_corner() {
let board = GameData::from_fen("8/8/8/8/8/8/8/B7 w - - 0 1").unwrap();
let (moves, count) = generate_bishop_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(A1, B2, false, None),
PieceMove::new(A1, C3, false, None),
PieceMove::new(A1, D4, false, None),
PieceMove::new(A1, E5, false, None),
PieceMove::new(A1, F6, false, None),
PieceMove::new(A1, G7, false, None),
PieceMove::new(A1, H8, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_white_bishop_h8_corner() {
let board = GameData::from_fen("7B/8/8/8/8/8/8/8 w - - 0 1").unwrap();
let (moves, count) = generate_bishop_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(H8, G7, false, None),
PieceMove::new(H8, F6, false, None),
PieceMove::new(H8, E5, false, None),
PieceMove::new(H8, D4, false, None),
PieceMove::new(H8, C3, false, None),
PieceMove::new(H8, B2, false, None),
PieceMove::new(H8, A1, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_white_bishop_captures() {
let board = GameData::from_fen("8/3p3p/8/8/3B4/8/1p3p2/8 w - - 0 1").unwrap();
let (moves, count) = generate_bishop_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(D4, C5, false, None),
PieceMove::new(D4, B6, false, None),
PieceMove::new(D4, A7, false, None),
PieceMove::new(D4, E5, false, None),
PieceMove::new(D4, F6, false, None),
PieceMove::new(D4, G7, false, None),
PieceMove::new(D4, H8, false, None), PieceMove::new(D4, C3, false, None),
PieceMove::new(D4, B2, true, None), PieceMove::new(D4, E3, false, None),
PieceMove::new(D4, F2, true, None), ];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_black_bishop_captures() {
let board = GameData::from_fen("8/3P3P/8/8/3b4/8/1P3P2/8 b - - 0 1").unwrap();
let (moves, count) = generate_bishop_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(D4, C5, false, None),
PieceMove::new(D4, B6, false, None),
PieceMove::new(D4, A7, false, None),
PieceMove::new(D4, E5, false, None),
PieceMove::new(D4, F6, false, None),
PieceMove::new(D4, G7, false, None),
PieceMove::new(D4, H8, false, None), PieceMove::new(D4, C3, false, None),
PieceMove::new(D4, B2, true, None), PieceMove::new(D4, E3, false, None),
PieceMove::new(D4, F2, true, None), ];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_bishop_blocked_by_own_pieces() {
let board = GameData::from_fen("8/8/8/2P1P3/3B4/2P1P3/8/8 w - - 0 1").unwrap();
let (moves, count) = generate_bishop_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_bishop_partially_blocked() {
let board = GameData::from_fen("8/8/8/8/3B4/8/1P6/8 w - - 0 1").unwrap();
let (moves, count) = generate_bishop_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(D4, C5, false, None),
PieceMove::new(D4, B6, false, None),
PieceMove::new(D4, A7, false, None),
PieceMove::new(D4, E5, false, None),
PieceMove::new(D4, F6, false, None),
PieceMove::new(D4, G7, false, None),
PieceMove::new(D4, H8, false, None),
PieceMove::new(D4, C3, false, None),
PieceMove::new(D4, E3, false, None),
PieceMove::new(D4, F2, false, None),
PieceMove::new(D4, G1, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_multiple_bishops() {
let board = GameData::from_fen("8/8/8/8/3B4/8/8/B7 w - - 0 1").unwrap();
let (moves, count) = generate_bishop_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(D4, C5, false, None),
PieceMove::new(D4, B6, false, None),
PieceMove::new(D4, A7, false, None),
PieceMove::new(D4, E5, false, None),
PieceMove::new(D4, F6, false, None),
PieceMove::new(D4, G7, false, None),
PieceMove::new(D4, H8, false, None),
PieceMove::new(D4, C3, false, None),
PieceMove::new(D4, B2, false, None),
PieceMove::new(D4, E3, false, None),
PieceMove::new(D4, F2, false, None),
PieceMove::new(D4, G1, false, None),
PieceMove::new(A1, B2, false, None),
PieceMove::new(A1, C3, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_bishop_edge_cases() {
let board = GameData::from_fen("8/8/8/8/B7/8/8/8 w - - 0 1").unwrap();
let (moves, count) = generate_bishop_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(A4, B5, false, None),
PieceMove::new(A4, C6, false, None),
PieceMove::new(A4, D7, false, None),
PieceMove::new(A4, E8, false, None),
PieceMove::new(A4, B3, false, None),
PieceMove::new(A4, C2, false, None),
PieceMove::new(A4, D1, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_no_bishops() {
let board = GameData::from_fen("8/8/8/8/8/8/8/8 w - - 0 1").unwrap();
let (_moves, count) = generate_bishop_moves(&board.board);
assert_eq!(count, 0);
}
#[test]
fn test_bishop_initial_position() {
let board =
GameData::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
let (_moves, count) = generate_bishop_moves(&board.board);
assert_eq!(count, 0);
}
#[test]
fn test_bishop_after_pawn_moves() {
let board =
GameData::from_fen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 1").unwrap();
let (moves, count) = generate_bishop_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let expected_moves = vec![
PieceMove::new(F1, E2, false, None),
PieceMove::new(F1, D3, false, None),
PieceMove::new(F1, C4, false, None),
PieceMove::new(F1, B5, false, None),
PieceMove::new(F1, A6, false, None),
];
assert_eq!(
sort_and_compare_moves(generated_moves),
sort_and_compare_moves(expected_moves)
);
}
#[test]
fn test_complex_bishop_position() {
let board =
GameData::from_fen("r1bqkb1r/pppp1ppp/2n2n2/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 1")
.unwrap();
let (moves, count) = generate_bishop_moves(&board.board);
let generated_moves: Vec<PieceMove> = moves[..count].to_vec();
let mut found_bishop_moves = false;
for move_item in &generated_moves {
if move_item.from_square() == C4 {
found_bishop_moves = true;
break;
}
}
assert!(found_bishop_moves, "Should find moves for bishop on C4");
assert!(count > 0, "Should generate some bishop moves");
}
}