Rename movegen module files and make readability improvements
This commit is contained in:
475
src/movegen/attack_generator.rs
Normal file
475
src/movegen/attack_generator.rs
Normal file
@@ -0,0 +1,475 @@
|
||||
use crate::board::{
|
||||
bitboard::{
|
||||
bitboard_to_coords, have_common_bit, lsb, square_to_bitboard, square_to_bitboard_wrapping,
|
||||
EMPTY, NOT_FILE_A, NOT_FILE_AB, NOT_FILE_GH, NOT_FILE_H,
|
||||
},
|
||||
board::Color,
|
||||
square::coords_to_square,
|
||||
};
|
||||
use u64 as Bitboard;
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub const ROOK_RELEVANT_BITS: [usize; 64] = [
|
||||
12, 11, 11, 11, 11, 11, 11, 12,
|
||||
11, 10, 10, 10, 10, 10, 10, 11,
|
||||
11, 10, 10, 10, 10, 10, 10, 11,
|
||||
11, 10, 10, 10, 10, 10, 10, 11,
|
||||
11, 10, 10, 10, 10, 10, 10, 11,
|
||||
11, 10, 10, 10, 10, 10, 10, 11,
|
||||
11, 10, 10, 10, 10, 10, 10, 11,
|
||||
12, 11, 11, 11, 11, 11, 11, 12,
|
||||
];
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub const BISHOP_RELEVANT_BITS: [usize; 64] = [
|
||||
6, 5, 5, 5, 5, 5, 5, 6,
|
||||
5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 7, 7, 7, 7, 5, 5,
|
||||
5, 5, 7, 9, 9, 7, 5, 5,
|
||||
5, 5, 7, 9, 9, 7, 5, 5,
|
||||
5, 5, 7, 7, 7, 7, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5,
|
||||
6, 5, 5, 5, 5, 5, 5, 6,
|
||||
];
|
||||
|
||||
static mut PAWN_ATTACKS: [[Bitboard; 2]; 64] = [[0; 2]; 64];
|
||||
static mut KNIGHT_ATTACKS: [Bitboard; 64] = [0; 64];
|
||||
static mut KING_ATTACKS: [Bitboard; 64] = [0; 64];
|
||||
static mut BISHOP_ATTACKS: [[Bitboard; 512]; 64] = [[0; 512]; 64];
|
||||
static mut ROOK_ATTACKS: [[Bitboard; 4096]; 64] = [[0; 4096]; 64];
|
||||
|
||||
const fn pawn_attacks(bitboard: Bitboard, color: Color) -> Bitboard {
|
||||
let mut attacks = EMPTY;
|
||||
|
||||
match color {
|
||||
Color::Black => {
|
||||
attacks |= (bitboard & NOT_FILE_H) >> 7;
|
||||
attacks |= (bitboard & NOT_FILE_A) >> 9;
|
||||
}
|
||||
Color::White => {
|
||||
attacks |= (bitboard & NOT_FILE_A) << 7;
|
||||
attacks |= (bitboard & NOT_FILE_H) << 9;
|
||||
}
|
||||
};
|
||||
|
||||
attacks
|
||||
}
|
||||
|
||||
const fn knight_attacks(bitboard: Bitboard) -> Bitboard {
|
||||
let mut attacks = EMPTY;
|
||||
|
||||
attacks |= (bitboard & NOT_FILE_AB) << 6;
|
||||
attacks |= (bitboard & NOT_FILE_GH) << 10;
|
||||
attacks |= (bitboard & NOT_FILE_A) << 15;
|
||||
attacks |= (bitboard & NOT_FILE_H) << 17;
|
||||
attacks |= (bitboard & NOT_FILE_GH) >> 6;
|
||||
attacks |= (bitboard & NOT_FILE_AB) >> 10;
|
||||
attacks |= (bitboard & NOT_FILE_H) >> 15;
|
||||
attacks |= (bitboard & NOT_FILE_A) >> 17;
|
||||
|
||||
attacks
|
||||
}
|
||||
|
||||
const fn king_attacks(bitboard: Bitboard) -> Bitboard {
|
||||
let mut attacks = EMPTY;
|
||||
|
||||
attacks |= (bitboard & NOT_FILE_H) << 1;
|
||||
attacks |= (bitboard & NOT_FILE_A) << 7;
|
||||
attacks |= bitboard << 8;
|
||||
attacks |= (bitboard & NOT_FILE_H) << 9;
|
||||
attacks |= (bitboard & NOT_FILE_A) >> 1;
|
||||
attacks |= (bitboard & NOT_FILE_H) >> 7;
|
||||
attacks |= bitboard >> 8;
|
||||
attacks |= (bitboard & NOT_FILE_A) >> 9;
|
||||
|
||||
attacks
|
||||
}
|
||||
|
||||
pub fn mask_bishop_attacks(bitboard: Bitboard) -> Bitboard {
|
||||
let mut attacks = EMPTY;
|
||||
let (rank_dst, file_dst) = bitboard_to_coords(bitboard);
|
||||
|
||||
let mut add_attacks_in_direction = |rank_step: isize, file_step: isize| {
|
||||
let mut rank = rank_dst as isize;
|
||||
let mut file = file_dst as isize;
|
||||
|
||||
while (1..=6).contains(&(rank + rank_step)) && (1..=6).contains(&(file + file_step)) {
|
||||
rank += rank_step;
|
||||
file += file_step;
|
||||
attacks |= square_to_bitboard(coords_to_square(rank as usize, file as usize));
|
||||
}
|
||||
};
|
||||
|
||||
add_attacks_in_direction(1, 1);
|
||||
add_attacks_in_direction(-1, 1);
|
||||
add_attacks_in_direction(1, -1);
|
||||
add_attacks_in_direction(-1, -1);
|
||||
|
||||
attacks
|
||||
}
|
||||
|
||||
pub fn mask_rook_attacks(bitboard: Bitboard) -> Bitboard {
|
||||
let mut attacks = EMPTY;
|
||||
let (rank_dst, file_dst) = bitboard_to_coords(bitboard);
|
||||
|
||||
let mut add_attacks_in_direction = |mut coord: usize, step: isize, is_vertical: bool| {
|
||||
while (1..=6).contains(&(coord as isize + step)) {
|
||||
coord = (coord as isize + step) as usize;
|
||||
|
||||
let attack_square = if is_vertical {
|
||||
square_to_bitboard(coords_to_square(coord, file_dst))
|
||||
} else {
|
||||
square_to_bitboard(coords_to_square(rank_dst, coord))
|
||||
};
|
||||
|
||||
attacks |= attack_square;
|
||||
}
|
||||
};
|
||||
|
||||
add_attacks_in_direction(rank_dst, 1, true);
|
||||
add_attacks_in_direction(rank_dst, -1, true);
|
||||
add_attacks_in_direction(file_dst, 1, false);
|
||||
add_attacks_in_direction(file_dst, -1, false);
|
||||
|
||||
attacks
|
||||
}
|
||||
|
||||
pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard {
|
||||
let mut attacks = EMPTY;
|
||||
let (rank_dst, file_dst) = bitboard_to_coords(bitboard);
|
||||
|
||||
let mut add_attacks_in_direction = |rank_step: isize, file_step: isize| {
|
||||
let mut rank = rank_dst as isize;
|
||||
let mut file = file_dst as isize;
|
||||
|
||||
while (0..=7).contains(&(rank + rank_step)) && (0..=7).contains(&(file + file_step)) {
|
||||
rank += rank_step;
|
||||
file += file_step;
|
||||
|
||||
let attack_square = square_to_bitboard(coords_to_square(rank as usize, file as usize));
|
||||
attacks |= attack_square;
|
||||
|
||||
if have_common_bit(attack_square, blocker) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
add_attacks_in_direction(1, 1);
|
||||
add_attacks_in_direction(-1, 1);
|
||||
add_attacks_in_direction(1, -1);
|
||||
add_attacks_in_direction(-1, -1);
|
||||
|
||||
attacks
|
||||
}
|
||||
|
||||
pub fn rook_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard {
|
||||
let mut attacks = EMPTY;
|
||||
let (rank_dst, file_dst) = bitboard_to_coords(bitboard);
|
||||
|
||||
let mut add_attacks_in_direction = |mut coord: usize, step: isize, is_vertical: bool| {
|
||||
while (0..=7).contains(&(coord as isize + step)) {
|
||||
coord = (coord as isize + step) as usize;
|
||||
|
||||
let attack_square = if is_vertical {
|
||||
square_to_bitboard(coords_to_square(coord, file_dst))
|
||||
} else {
|
||||
square_to_bitboard(coords_to_square(rank_dst, coord))
|
||||
};
|
||||
|
||||
attacks |= attack_square;
|
||||
|
||||
if have_common_bit(attack_square, blocker) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
add_attacks_in_direction(rank_dst, 1, true);
|
||||
add_attacks_in_direction(rank_dst, -1, true);
|
||||
add_attacks_in_direction(file_dst, 1, false);
|
||||
add_attacks_in_direction(file_dst, -1, false);
|
||||
|
||||
attacks
|
||||
}
|
||||
|
||||
pub fn set_occupancy(index: u64, relevant_bits: usize, mut attack_mask: Bitboard) -> Bitboard {
|
||||
let mut occupancy = EMPTY;
|
||||
|
||||
for bit in 0..relevant_bits {
|
||||
let square = lsb(attack_mask);
|
||||
attack_mask &= !square_to_bitboard(square);
|
||||
if have_common_bit(index, square_to_bitboard(bit)) {
|
||||
occupancy |= square_to_bitboard(square);
|
||||
}
|
||||
}
|
||||
|
||||
occupancy
|
||||
}
|
||||
|
||||
pub fn fetch_pawn_attacks(square: usize, color: Color) -> Bitboard {
|
||||
unsafe {
|
||||
match color {
|
||||
Color::White => PAWN_ATTACKS[square][0],
|
||||
Color::Black => PAWN_ATTACKS[square][1],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fetch_knight_attacks(square: usize) -> Bitboard {
|
||||
unsafe { KNIGHT_ATTACKS[square] }
|
||||
}
|
||||
|
||||
pub fn fetch_king_attacks(square: usize) -> Bitboard {
|
||||
unsafe { KING_ATTACKS[square] }
|
||||
}
|
||||
|
||||
use crate::movegen::magic_bitboards::{BISHOP_MAGIC, ROOK_MAGIC};
|
||||
|
||||
pub fn fetch_bishop_attacks(mut occupancy: Bitboard, square: usize) -> Bitboard {
|
||||
unsafe {
|
||||
occupancy &= mask_bishop_attacks(square_to_bitboard_wrapping(square));
|
||||
occupancy = occupancy.wrapping_mul(BISHOP_MAGIC[square]);
|
||||
occupancy >>= 64 - BISHOP_RELEVANT_BITS[square];
|
||||
BISHOP_ATTACKS[square][occupancy as usize]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fetch_rook_attacks(mut occupancy: Bitboard, square: usize) -> Bitboard {
|
||||
unsafe {
|
||||
occupancy &= mask_rook_attacks(square_to_bitboard_wrapping(square));
|
||||
occupancy = occupancy.wrapping_mul(ROOK_MAGIC[square]);
|
||||
occupancy >>= 64 - ROOK_RELEVANT_BITS[square];
|
||||
ROOK_ATTACKS[square][occupancy as usize]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fetch_queen_attacks(occupancy: Bitboard, square: usize) -> Bitboard {
|
||||
fetch_rook_attacks(occupancy, square) | fetch_bishop_attacks(occupancy, square)
|
||||
}
|
||||
|
||||
pub fn init_pawn_attacks() {
|
||||
(0..64).for_each(|square| unsafe {
|
||||
PAWN_ATTACKS[square][0] = pawn_attacks(square_to_bitboard(square), Color::White);
|
||||
PAWN_ATTACKS[square][1] = pawn_attacks(square_to_bitboard(square), Color::Black);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn init_knight_attacks() {
|
||||
(0..64).for_each(|square| unsafe {
|
||||
KNIGHT_ATTACKS[square] = knight_attacks(square_to_bitboard(square));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn init_king_attacks() {
|
||||
(0..64).for_each(|square| unsafe {
|
||||
KING_ATTACKS[square] = king_attacks(square_to_bitboard(square));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn init_bishop_attacks() {
|
||||
let mut bishop_masks = [0; 64];
|
||||
|
||||
for square in 0..64 {
|
||||
bishop_masks[square] = mask_bishop_attacks(square_to_bitboard(square));
|
||||
let attack_mask = bishop_masks[square];
|
||||
let occupancy_indices = square_to_bitboard(BISHOP_RELEVANT_BITS[square]);
|
||||
|
||||
for index in 0..occupancy_indices {
|
||||
let occupancy = set_occupancy(index, BISHOP_RELEVANT_BITS[square], attack_mask);
|
||||
unsafe {
|
||||
let magic_index = (occupancy.wrapping_mul(BISHOP_MAGIC[square])
|
||||
>> (64 - BISHOP_RELEVANT_BITS[square]))
|
||||
as usize;
|
||||
|
||||
BISHOP_ATTACKS[square][magic_index] =
|
||||
bishop_attacks_on_the_fly(square_to_bitboard(square), occupancy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_rook_attacks() {
|
||||
let mut rook_masks = [0; 64];
|
||||
|
||||
for square in 0..64 {
|
||||
rook_masks[square] = mask_rook_attacks(square_to_bitboard(square));
|
||||
let attack_mask = rook_masks[square];
|
||||
let occupancy_indices = square_to_bitboard(ROOK_RELEVANT_BITS[square]);
|
||||
|
||||
for idx in 0..occupancy_indices {
|
||||
let occupancy = set_occupancy(idx, ROOK_RELEVANT_BITS[square], attack_mask);
|
||||
unsafe {
|
||||
let magic_index = (occupancy.wrapping_mul(ROOK_MAGIC[square])
|
||||
>> (64 - ROOK_RELEVANT_BITS[square]))
|
||||
as usize;
|
||||
|
||||
ROOK_ATTACKS[square][magic_index] =
|
||||
rook_attacks_on_the_fly(square_to_bitboard(square), occupancy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_attacks() {
|
||||
init_pawn_attacks();
|
||||
init_knight_attacks();
|
||||
init_king_attacks();
|
||||
init_bishop_attacks();
|
||||
init_rook_attacks()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::board::board::{Color, Piece, PieceType};
|
||||
|
||||
#[test]
|
||||
fn test_pawn_attacks() -> Result<(), String> {
|
||||
let white_pawn_a_file = Piece::new(0x100, PieceType::Pawn, Color::White);
|
||||
let attacks = pawn_attacks(white_pawn_a_file.bitboard, white_pawn_a_file.color);
|
||||
assert_eq!(attacks, 0x20000);
|
||||
|
||||
let white_pawn_b_file = Piece::new(0x200, PieceType::Pawn, Color::White);
|
||||
let attacks = pawn_attacks(white_pawn_b_file.bitboard, white_pawn_b_file.color);
|
||||
assert_eq!(attacks, 0x50000);
|
||||
|
||||
let white_pawn_h_file = Piece::new(0x8000, PieceType::Pawn, Color::White);
|
||||
let attacks = pawn_attacks(white_pawn_h_file.bitboard, white_pawn_h_file.color);
|
||||
assert_eq!(attacks, 0x400000);
|
||||
|
||||
let black_pawn_a_file = Piece::new(0x1000000000000, PieceType::Pawn, Color::Black);
|
||||
let attacks = pawn_attacks(black_pawn_a_file.bitboard, black_pawn_a_file.color);
|
||||
assert_eq!(attacks, 0x20000000000);
|
||||
|
||||
let black_pawn_b_file = Piece::new(0x2000000000000, PieceType::Pawn, Color::Black);
|
||||
let attacks = pawn_attacks(black_pawn_b_file.bitboard, black_pawn_b_file.color);
|
||||
assert_eq!(attacks, 0x50000000000);
|
||||
|
||||
let black_pawn_h_file = Piece::new(0x80000000000000, PieceType::Pawn, Color::Black);
|
||||
let attacks = pawn_attacks(black_pawn_h_file.bitboard, black_pawn_h_file.color);
|
||||
assert_eq!(attacks, 0x400000000000);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_knight_attacks() -> Result<(), String> {
|
||||
let knight_two_attacks = Piece::new(0x1, PieceType::Knight, Color::White);
|
||||
let attacks = knight_attacks(knight_two_attacks.bitboard);
|
||||
assert_eq!(attacks, 0x20400);
|
||||
|
||||
let knight_three_attacks = Piece::new(0x2, PieceType::Knight, Color::White);
|
||||
let attacks = knight_attacks(knight_three_attacks.bitboard);
|
||||
assert_eq!(attacks, 0x50800);
|
||||
|
||||
let knight_three_attacks = Piece::new(0x4, PieceType::Knight, Color::White);
|
||||
let attacks = knight_attacks(knight_three_attacks.bitboard);
|
||||
assert_eq!(attacks, 0xa1100);
|
||||
|
||||
let knight_six_attacks = Piece::new(0x400, PieceType::Knight, Color::White);
|
||||
let attacks = knight_attacks(knight_six_attacks.bitboard);
|
||||
assert_eq!(attacks, 0xa110011);
|
||||
|
||||
let knight_eight_attacks = Piece::new(0x40000, PieceType::Knight, Color::White);
|
||||
let attacks = knight_attacks(knight_eight_attacks.bitboard);
|
||||
assert_eq!(attacks, 0xa1100110a);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_king_attacks() -> Result<(), String> {
|
||||
let king_three_attacks = Piece::new(0x1, PieceType::King, Color::White);
|
||||
let attacks = king_attacks(king_three_attacks.bitboard);
|
||||
assert_eq!(attacks, 0x302);
|
||||
|
||||
let king_five_attacks = Piece::new(0x2, PieceType::King, Color::White);
|
||||
let attacks = king_attacks(king_five_attacks.bitboard);
|
||||
assert_eq!(attacks, 0x705);
|
||||
|
||||
let king_eight_attacks = Piece::new(0x200, PieceType::King, Color::White);
|
||||
let attacks = king_attacks(king_eight_attacks.bitboard);
|
||||
assert_eq!(attacks, 0x70507);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mask_bishop_attacks() -> Result<(), String> {
|
||||
let bishop_d4 = Piece::new(0x8000000, PieceType::Bishop, Color::White);
|
||||
let all_directions_mask = mask_bishop_attacks(bishop_d4.bitboard);
|
||||
assert_eq!(all_directions_mask, 0x40221400142200);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bishop_attacks_on_the_fly() -> Result<(), String> {
|
||||
let bishop_d4 = Piece::new(0x8000000, PieceType::Bishop, Color::White);
|
||||
let blocker_c5 = 0x400000000_u64;
|
||||
let attacks = bishop_attacks_on_the_fly(bishop_d4.bitboard, blocker_c5);
|
||||
assert_eq!(attacks, 0x8040201400142241);
|
||||
|
||||
let bishop_a1 = Piece::new(0x0, PieceType::Bishop, Color::White);
|
||||
let blocker_none = 0x0_u64;
|
||||
let attacks = bishop_attacks_on_the_fly(bishop_a1.bitboard, blocker_none);
|
||||
assert_eq!(attacks, 0x8040201008040200);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mask_rook_attacks() -> Result<(), String> {
|
||||
let rook_d4 = Piece::new(0x8000000, PieceType::Rook, Color::White);
|
||||
let all_directions_mask = mask_rook_attacks(rook_d4.bitboard);
|
||||
assert_eq!(all_directions_mask, 0x8080876080800);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rook_attacks_on_the_fly() -> Result<(), String> {
|
||||
let rook_d4 = Piece::new(0x8000000, PieceType::Rook, Color::White);
|
||||
let blocker_c4 = 0x4000000_u64;
|
||||
let attacks = rook_attacks_on_the_fly(rook_d4.bitboard, blocker_c4);
|
||||
assert_eq!(attacks, 0x8080808f4080808);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bishop_attacks() -> Result<(), String> {
|
||||
init_bishop_attacks();
|
||||
let bishop_d3_square = 0x80000_u64.trailing_zeros() as usize;
|
||||
let blockers = 0x602000020;
|
||||
let attacks = fetch_bishop_attacks(blockers, bishop_d3_square);
|
||||
assert_eq!(attacks, 0x80402214001422);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rook_attacks() -> Result<(), String> {
|
||||
init_rook_attacks();
|
||||
let rook_d3_square = 0x80000_u64.trailing_zeros() as usize;
|
||||
let blockers = 0x800000000600800;
|
||||
let attacks = fetch_rook_attacks(blockers, rook_d3_square);
|
||||
assert_eq!(attacks, 0x808080808370800);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_queen_attacks() -> Result<(), String> {
|
||||
init_rook_attacks();
|
||||
init_bishop_attacks();
|
||||
|
||||
let queen_d3_square = 0x80000_u64.trailing_zeros() as usize;
|
||||
let blockers = 0x800000602600820;
|
||||
let queen_attacks = fetch_queen_attacks(blockers, queen_d3_square);
|
||||
assert_eq!(queen_attacks, 0x888482a1c371c22);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user