Refactor duplicate code and make it more readable
This commit is contained in:
259
src/attack.rs
259
src/attack.rs
@@ -1,13 +1,15 @@
|
||||
use crate::board::Color;
|
||||
use crate::{
|
||||
bitboard::{
|
||||
intersect, 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;
|
||||
|
||||
const NOT_A_FILE: Bitboard = 0xfefefefefefefefe;
|
||||
const NOT_AB_FILE: Bitboard = 0xfcfcfcfcfcfcfcfc;
|
||||
const NOT_H_FILE: Bitboard = 0x7f7f7f7f7f7f7f7f;
|
||||
const NOT_GH_FILE: Bitboard = 0x3f3f3f3f3f3f3f3f;
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub const ROOK_RELEVANT_BITS: [u8; 64] = [
|
||||
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,
|
||||
@@ -19,7 +21,7 @@ pub const ROOK_RELEVANT_BITS: [u8; 64] = [
|
||||
];
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub const BISHOP_RELEVANT_BITS: [u8; 64] = [
|
||||
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,
|
||||
@@ -37,16 +39,16 @@ 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 = 0_u64;
|
||||
let mut attacks = EMPTY;
|
||||
|
||||
match color {
|
||||
Color::Black => {
|
||||
attacks |= (bitboard & NOT_H_FILE) >> (7);
|
||||
attacks |= (bitboard & NOT_A_FILE) >> (9);
|
||||
attacks |= (bitboard & NOT_FILE_H) >> 7;
|
||||
attacks |= (bitboard & NOT_FILE_A) >> 9;
|
||||
}
|
||||
Color::White => {
|
||||
attacks |= (bitboard & NOT_A_FILE) << (7);
|
||||
attacks |= (bitboard & NOT_H_FILE) << (9);
|
||||
attacks |= (bitboard & NOT_FILE_A) << 7;
|
||||
attacks |= (bitboard & NOT_FILE_H) << 9;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -54,119 +56,101 @@ const fn pawn_attacks(bitboard: Bitboard, color: Color) -> Bitboard {
|
||||
}
|
||||
|
||||
const fn knight_attacks(bitboard: Bitboard) -> Bitboard {
|
||||
let mut attacks = 0_u64;
|
||||
let mut attacks = EMPTY;
|
||||
|
||||
attacks |= (bitboard & NOT_AB_FILE) << (6);
|
||||
attacks |= (bitboard & NOT_GH_FILE) << (10);
|
||||
attacks |= (bitboard & NOT_A_FILE) << (15);
|
||||
attacks |= (bitboard & NOT_H_FILE) << (17);
|
||||
attacks |= (bitboard & NOT_GH_FILE) >> (6);
|
||||
attacks |= (bitboard & NOT_AB_FILE) >> (10);
|
||||
attacks |= (bitboard & NOT_H_FILE) >> (15);
|
||||
attacks |= (bitboard & NOT_A_FILE) >> (17);
|
||||
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 = 0_u64;
|
||||
let mut attacks = EMPTY;
|
||||
|
||||
attacks |= (bitboard & NOT_H_FILE) << (1);
|
||||
attacks |= (bitboard & NOT_A_FILE) << (7);
|
||||
attacks |= bitboard << (8);
|
||||
attacks |= (bitboard & NOT_H_FILE) << (9);
|
||||
attacks |= (bitboard & NOT_A_FILE) >> (1);
|
||||
attacks |= (bitboard & NOT_H_FILE) >> (7);
|
||||
attacks |= bitboard >> (8);
|
||||
attacks |= (bitboard & NOT_A_FILE) >> (9);
|
||||
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 = 0_u64;
|
||||
let target_rank = if bitboard == 0 {
|
||||
0
|
||||
} else {
|
||||
bitboard.trailing_zeros() / 8
|
||||
};
|
||||
let target_file = if bitboard == 0 {
|
||||
0
|
||||
} else {
|
||||
bitboard.trailing_zeros() % 8
|
||||
let mut attacks = EMPTY;
|
||||
let (target_rank, target_file) = match bitboard {
|
||||
0 => (0, 0),
|
||||
_ => (lsb(bitboard) / 8, lsb(bitboard) % 8),
|
||||
};
|
||||
|
||||
let (mut rank, mut file) = (target_rank + 1, target_file + 1);
|
||||
for (rank, file) in (rank..=6).zip(file..=6) {
|
||||
attacks |= 1_u64 << (rank * 8 + file);
|
||||
attacks |= square_to_bitboard(coords_to_square(rank, file));
|
||||
}
|
||||
|
||||
(rank, file) = (target_rank.saturating_sub(1), target_file + 1);
|
||||
for (rank, file) in (1..=rank).rev().zip(file..=6) {
|
||||
attacks |= 1_u64 << (rank * 8 + file);
|
||||
attacks |= square_to_bitboard(coords_to_square(rank, file));
|
||||
}
|
||||
|
||||
(rank, file) = (target_rank + 1, target_file.saturating_sub(1));
|
||||
for (rank, file) in (rank..=6).zip((1..=file).rev()) {
|
||||
attacks |= 1_u64 << (rank * 8 + file);
|
||||
attacks |= square_to_bitboard(coords_to_square(rank, file));
|
||||
}
|
||||
|
||||
(rank, file) = (target_rank.saturating_sub(1), target_file.saturating_sub(1));
|
||||
for (rank, file) in (1..=rank).rev().zip((1..=file).rev()) {
|
||||
attacks |= 1_u64 << (rank * 8 + file);
|
||||
attacks |= square_to_bitboard(coords_to_square(rank, file));
|
||||
}
|
||||
|
||||
attacks
|
||||
}
|
||||
|
||||
pub fn mask_rook_attacks(bitboard: Bitboard) -> Bitboard {
|
||||
let mut attacks = 0_u64;
|
||||
let target_rank = if bitboard == 0 {
|
||||
0
|
||||
} else {
|
||||
bitboard.trailing_zeros() / 8
|
||||
};
|
||||
let target_file = if bitboard == 0 {
|
||||
0
|
||||
} else {
|
||||
bitboard.trailing_zeros() % 8
|
||||
let mut attacks = EMPTY;
|
||||
let (target_rank, target_file) = match bitboard {
|
||||
0 => (0, 0),
|
||||
_ => (lsb(bitboard) / 8, lsb(bitboard) % 8),
|
||||
};
|
||||
|
||||
let (mut rank, mut file) = (target_rank + 1, target_file + 1);
|
||||
for rank in rank..=6 {
|
||||
attacks |= 1_u64 << (rank * 8 + target_file);
|
||||
attacks |= square_to_bitboard(coords_to_square(rank, target_file));
|
||||
}
|
||||
for file in file..=6 {
|
||||
attacks |= 1_u64 << (target_rank * 8 + file);
|
||||
attacks |= square_to_bitboard(coords_to_square(target_rank, file));
|
||||
}
|
||||
|
||||
(rank, file) = (target_rank.saturating_sub(1), target_file.saturating_sub(1));
|
||||
for rank in (1..=rank).rev() {
|
||||
attacks |= 1_u64 << (rank * 8 + target_file);
|
||||
attacks |= square_to_bitboard(coords_to_square(rank, target_file));
|
||||
}
|
||||
for file in (1..=file).rev() {
|
||||
attacks |= 1_u64 << (target_rank * 8 + file);
|
||||
attacks |= square_to_bitboard(coords_to_square(target_rank, file));
|
||||
}
|
||||
|
||||
attacks
|
||||
}
|
||||
|
||||
pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard {
|
||||
let mut attacks = 0_u64;
|
||||
let target_rank = if bitboard == 0 {
|
||||
0
|
||||
} else {
|
||||
bitboard.trailing_zeros() / 8
|
||||
};
|
||||
let target_file = if bitboard == 0 {
|
||||
0
|
||||
} else {
|
||||
bitboard.trailing_zeros() % 8
|
||||
let mut attacks = EMPTY;
|
||||
let (target_rank, target_file) = match bitboard {
|
||||
0 => (0, 0),
|
||||
_ => (lsb(bitboard) / 8, lsb(bitboard) % 8),
|
||||
};
|
||||
|
||||
let (mut rank, mut file) = (target_rank + 1, target_file + 1);
|
||||
for (rank, file) in (rank..=7).zip(file..=7) {
|
||||
let attack_square = 1_u64 << (rank * 8 + file);
|
||||
let attack_square = square_to_bitboard(coords_to_square(rank, file));
|
||||
attacks |= attack_square;
|
||||
if attack_square & blocker != 0 {
|
||||
break;
|
||||
@@ -175,7 +159,7 @@ pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitbo
|
||||
|
||||
file = target_file + 1;
|
||||
for (rank, file) in (0..target_rank).rev().zip(file..=7) {
|
||||
let attack_square = 1_u64 << (rank * 8 + file);
|
||||
let attack_square = square_to_bitboard(coords_to_square(rank, file));
|
||||
attacks |= attack_square;
|
||||
if attack_square & blocker != 0 {
|
||||
break;
|
||||
@@ -184,7 +168,7 @@ pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitbo
|
||||
|
||||
rank = target_rank + 1;
|
||||
for (rank, file) in (rank..=7).zip((0..target_file).rev()) {
|
||||
let attack_square = 1_u64 << (rank * 8 + file);
|
||||
let attack_square = square_to_bitboard(coords_to_square(rank, file));
|
||||
attacks |= attack_square;
|
||||
if attack_square & blocker != 0 {
|
||||
break;
|
||||
@@ -192,7 +176,7 @@ pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitbo
|
||||
}
|
||||
|
||||
for (rank, file) in (0..target_rank).rev().zip((0..target_file).rev()) {
|
||||
let attack_square = 1_u64 << (rank * 8 + file);
|
||||
let attack_square = square_to_bitboard(coords_to_square(rank, file));
|
||||
attacks |= attack_square;
|
||||
if attack_square & blocker != 0 {
|
||||
break;
|
||||
@@ -203,28 +187,22 @@ pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitbo
|
||||
}
|
||||
|
||||
pub fn rook_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard {
|
||||
let mut attacks = 0_u64;
|
||||
let target_rank = if bitboard == 0 {
|
||||
0
|
||||
} else {
|
||||
bitboard.trailing_zeros() / 8
|
||||
};
|
||||
let target_file = if bitboard == 0 {
|
||||
0
|
||||
} else {
|
||||
bitboard.trailing_zeros() % 8
|
||||
let mut attacks = EMPTY;
|
||||
let (target_rank, target_file) = match bitboard {
|
||||
0 => (0, 0),
|
||||
_ => (lsb(bitboard) / 8, lsb(bitboard) % 8),
|
||||
};
|
||||
|
||||
let (mut rank, mut file) = (target_rank + 1, target_file + 1);
|
||||
for rank in rank..=7 {
|
||||
let attack_square = 1_u64 << (rank * 8 + target_file);
|
||||
let attack_square = square_to_bitboard(coords_to_square(rank, target_file));
|
||||
attacks |= attack_square;
|
||||
if attack_square & blocker != 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for file in file..=7 {
|
||||
let attack_square = 1_u64 << (target_rank * 8 + file);
|
||||
let attack_square = square_to_bitboard(coords_to_square(target_rank, file));
|
||||
attacks |= attack_square;
|
||||
if attack_square & blocker != 0 {
|
||||
break;
|
||||
@@ -233,14 +211,14 @@ pub fn rook_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboar
|
||||
|
||||
(rank, file) = (target_rank.saturating_sub(1), target_file.saturating_sub(1));
|
||||
for rank in (0..=rank).rev() {
|
||||
let attack_square = 1_u64 << (rank * 8 + target_file);
|
||||
let attack_square = square_to_bitboard(coords_to_square(rank, target_file));
|
||||
attacks |= attack_square;
|
||||
if attack_square & blocker != 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for file in (0..=file).rev() {
|
||||
let attack_square = 1_u64 << (target_rank * 8 + file);
|
||||
let attack_square = square_to_bitboard(coords_to_square(target_rank, file));
|
||||
attacks |= attack_square;
|
||||
if attack_square & blocker != 0 {
|
||||
break;
|
||||
@@ -250,96 +228,97 @@ pub fn rook_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboar
|
||||
attacks
|
||||
}
|
||||
|
||||
pub fn set_occupancy(index: u64, bits_in_mask: u8, mut attack_mask: Bitboard) -> Bitboard {
|
||||
let mut occupancy = 0_u64;
|
||||
pub fn set_occupancy(index: u64, relevant_bits: usize, mut attack_mask: Bitboard) -> Bitboard {
|
||||
let mut occupancy = EMPTY;
|
||||
|
||||
for count in 0..bits_in_mask {
|
||||
let square = attack_mask.trailing_zeros() as u8;
|
||||
attack_mask &= !(1_u64 << square);
|
||||
if index & (1_u64 << count) != 0 {
|
||||
occupancy |= 1_u64 << square;
|
||||
for bit in 0..relevant_bits {
|
||||
let square = lsb(attack_mask);
|
||||
attack_mask &= !square_to_bitboard(square);
|
||||
if intersect(index, square_to_bitboard(bit)) {
|
||||
occupancy |= square_to_bitboard(square);
|
||||
}
|
||||
}
|
||||
|
||||
occupancy
|
||||
}
|
||||
|
||||
pub fn get_pawn_attacks(sq: usize, color: Color) -> Bitboard {
|
||||
pub fn get_pawn_attacks(square: usize, color: Color) -> Bitboard {
|
||||
unsafe {
|
||||
match color {
|
||||
Color::White => PAWN_ATTACKS[sq][0],
|
||||
Color::Black => PAWN_ATTACKS[sq][1],
|
||||
Color::White => PAWN_ATTACKS[square][0],
|
||||
Color::Black => PAWN_ATTACKS[square][1],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_knight_attacks(sq: usize) -> Bitboard {
|
||||
unsafe { KNIGHT_ATTACKS[sq] }
|
||||
pub fn get_knight_attacks(square: usize) -> Bitboard {
|
||||
unsafe { KNIGHT_ATTACKS[square] }
|
||||
}
|
||||
|
||||
pub fn get_king_attacks(sq: usize) -> Bitboard {
|
||||
unsafe { KING_ATTACKS[sq] }
|
||||
pub fn get_king_attacks(square: usize) -> Bitboard {
|
||||
unsafe { KING_ATTACKS[square] }
|
||||
}
|
||||
|
||||
use crate::magic::{BISHOP_MAGIC, ROOK_MAGIC};
|
||||
|
||||
pub fn get_bishop_attacks(mut occupancy: Bitboard, sq: usize) -> Bitboard {
|
||||
pub fn get_bishop_attacks(mut occupancy: Bitboard, square: usize) -> Bitboard {
|
||||
unsafe {
|
||||
occupancy &= mask_bishop_attacks(1_u64.wrapping_shl(sq as u32));
|
||||
occupancy = occupancy.wrapping_mul(BISHOP_MAGIC[sq]);
|
||||
occupancy >>= 64 - BISHOP_RELEVANT_BITS[sq];
|
||||
BISHOP_ATTACKS[sq][occupancy as usize]
|
||||
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 get_rook_attacks(mut occupancy: Bitboard, sq: usize) -> Bitboard {
|
||||
pub fn get_rook_attacks(mut occupancy: Bitboard, square: usize) -> Bitboard {
|
||||
unsafe {
|
||||
occupancy &= mask_rook_attacks(1_u64.wrapping_shl(sq as u32));
|
||||
occupancy = occupancy.wrapping_mul(ROOK_MAGIC[sq]);
|
||||
occupancy >>= 64 - ROOK_RELEVANT_BITS[sq];
|
||||
ROOK_ATTACKS[sq][occupancy as usize]
|
||||
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 get_queen_attacks(occupancy: Bitboard, sq: usize) -> Bitboard {
|
||||
get_rook_attacks(occupancy, sq) | get_bishop_attacks(occupancy, sq)
|
||||
pub fn get_queen_attacks(occupancy: Bitboard, square: usize) -> Bitboard {
|
||||
get_rook_attacks(occupancy, square) | get_bishop_attacks(occupancy, square)
|
||||
}
|
||||
|
||||
pub fn init_pawn_attacks() {
|
||||
(0..64).for_each(|sq| unsafe {
|
||||
PAWN_ATTACKS[sq][0] = pawn_attacks(1_u64 << (sq), Color::White);
|
||||
PAWN_ATTACKS[sq][1] = pawn_attacks(1_u64 << (sq), Color::Black);
|
||||
(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(|sq| unsafe {
|
||||
KNIGHT_ATTACKS[sq] = knight_attacks(1_u64 << (sq));
|
||||
(0..64).for_each(|square| unsafe {
|
||||
KNIGHT_ATTACKS[square] = knight_attacks(square_to_bitboard(square));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn init_king_attacks() {
|
||||
(0..64).for_each(|sq| unsafe {
|
||||
KING_ATTACKS[sq] = king_attacks(1_u64 << (sq));
|
||||
(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 sq in 0..64 {
|
||||
bishop_masks[sq] = mask_bishop_attacks(1_u64 << (sq));
|
||||
let attack_mask = bishop_masks[sq];
|
||||
let occupancy_indices = 1_u64 << BISHOP_RELEVANT_BITS[sq];
|
||||
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 idx in 0..occupancy_indices {
|
||||
let occupancy = set_occupancy(idx, BISHOP_RELEVANT_BITS[sq], attack_mask);
|
||||
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[sq]) >> (64 - BISHOP_RELEVANT_BITS[sq]);
|
||||
let magic_index = (occupancy.wrapping_mul(BISHOP_MAGIC[square])
|
||||
>> (64 - BISHOP_RELEVANT_BITS[square]))
|
||||
as usize;
|
||||
|
||||
BISHOP_ATTACKS[sq][magic_index as usize] =
|
||||
bishop_attacks_on_the_fly(1_u64 << (sq), occupancy);
|
||||
BISHOP_ATTACKS[square][magic_index] =
|
||||
bishop_attacks_on_the_fly(square_to_bitboard(square), occupancy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,20 +327,20 @@ pub fn init_bishop_attacks() {
|
||||
pub fn init_rook_attacks() {
|
||||
let mut rook_masks = [0; 64];
|
||||
|
||||
for sq in 0..64 {
|
||||
rook_masks[sq] = mask_rook_attacks(1_u64 << (sq));
|
||||
let attack_mask = rook_masks[sq];
|
||||
let relevant_bits_count = attack_mask.count_ones() as u8;
|
||||
let occupancy_indices = 1_u64 << (relevant_bits_count);
|
||||
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, relevant_bits_count, attack_mask);
|
||||
let occupancy = set_occupancy(idx, ROOK_RELEVANT_BITS[square], attack_mask);
|
||||
unsafe {
|
||||
let magic_index =
|
||||
occupancy.wrapping_mul(ROOK_MAGIC[sq]) >> (64 - ROOK_RELEVANT_BITS[sq]);
|
||||
let magic_index = (occupancy.wrapping_mul(ROOK_MAGIC[square])
|
||||
>> (64 - ROOK_RELEVANT_BITS[square]))
|
||||
as usize;
|
||||
|
||||
ROOK_ATTACKS[sq][magic_index as usize] =
|
||||
rook_attacks_on_the_fly(1_u64 << (sq), occupancy);
|
||||
ROOK_ATTACKS[square][magic_index] =
|
||||
rook_attacks_on_the_fly(square_to_bitboard(square), occupancy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
src/bitboard.rs
Normal file
29
src/bitboard.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use u64 as Bitboard;
|
||||
|
||||
pub const EMPTY: Bitboard = 0x0;
|
||||
pub const FULL: Bitboard = 0xffffffffffffffff;
|
||||
pub const NOT_FILE_A: Bitboard = 0xfefefefefefefefe;
|
||||
pub const NOT_FILE_AB: Bitboard = 0xfcfcfcfcfcfcfcfc;
|
||||
pub const NOT_FILE_H: Bitboard = 0x7f7f7f7f7f7f7f7f;
|
||||
pub const NOT_FILE_GH: Bitboard = 0x3f3f3f3f3f3f3f3f;
|
||||
pub const RANK_8: Bitboard = 0x0ff00000000000000;
|
||||
|
||||
pub const fn square_to_bitboard(square: usize) -> Bitboard {
|
||||
1_u64 << square
|
||||
}
|
||||
|
||||
pub const fn square_to_bitboard_wrapping(square: usize) -> Bitboard {
|
||||
1_u64.wrapping_shl(square as u32)
|
||||
}
|
||||
|
||||
pub const fn lsb(bitboard: Bitboard) -> usize {
|
||||
bitboard.trailing_zeros() as usize
|
||||
}
|
||||
|
||||
pub const fn bit_count(bitboard: Bitboard) -> usize {
|
||||
bitboard.count_ones() as usize
|
||||
}
|
||||
|
||||
pub const fn intersect(bitboard_a: Bitboard, bitboard_b: Bitboard) -> bool {
|
||||
bitboard_a & bitboard_b != 0
|
||||
}
|
||||
34
src/board.rs
34
src/board.rs
@@ -180,21 +180,21 @@ impl Board {
|
||||
|
||||
match &mv.move_type {
|
||||
MoveType::Quiet => {
|
||||
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces);
|
||||
Self::move_piece(mv.source as u8, mv.target as u8, own_pieces);
|
||||
self.state.set_en_passant_target_square(None);
|
||||
}
|
||||
MoveType::Capture => {
|
||||
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces);
|
||||
Board::remove_piece(mv.target as u8, opponent_pieces);
|
||||
Self::move_piece(mv.source as u8, mv.target as u8, own_pieces);
|
||||
Self::remove_piece(mv.target as u8, opponent_pieces);
|
||||
self.state.set_en_passant_target_square(None);
|
||||
}
|
||||
MoveType::EnPassant => {
|
||||
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces);
|
||||
Board::remove_pawn_enpassant(mv.target as u8, opponent_pieces, color);
|
||||
Self::move_piece(mv.source as u8, mv.target as u8, own_pieces);
|
||||
Self::remove_pawn_enpassant(mv.target as u8, opponent_pieces, color);
|
||||
self.state.set_en_passant_target_square(None);
|
||||
}
|
||||
MoveType::DoublePush => {
|
||||
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces);
|
||||
Self::move_piece(mv.source as u8, mv.target as u8, own_pieces);
|
||||
let en_passant = match color {
|
||||
Color::White => Some((mv.source + 8) as u8),
|
||||
Color::Black => Some((mv.source - 8) as u8),
|
||||
@@ -202,19 +202,19 @@ impl Board {
|
||||
self.state.set_en_passant_target_square(en_passant);
|
||||
}
|
||||
MoveType::Promotion(promote) => {
|
||||
Board::remove_piece(mv.source as u8, own_pieces);
|
||||
Board::promote_piece(mv.target as u8, own_pieces, *promote);
|
||||
Self::remove_piece(mv.source as u8, own_pieces);
|
||||
Self::promote_piece(mv.target as u8, own_pieces, *promote);
|
||||
self.state.set_en_passant_target_square(None);
|
||||
}
|
||||
MoveType::PromotionCapture(promote) => {
|
||||
Board::remove_piece(mv.source as u8, own_pieces);
|
||||
Board::remove_piece(mv.target as u8, opponent_pieces);
|
||||
Board::promote_piece(mv.target as u8, own_pieces, *promote);
|
||||
Self::remove_piece(mv.source as u8, own_pieces);
|
||||
Self::remove_piece(mv.target as u8, opponent_pieces);
|
||||
Self::promote_piece(mv.target as u8, own_pieces, *promote);
|
||||
self.state.set_en_passant_target_square(None);
|
||||
}
|
||||
MoveType::Castle => {
|
||||
Board::move_piece(mv.source as u8, mv.target as u8, own_pieces);
|
||||
Board::move_rook_castle(mv.target as u8, own_pieces);
|
||||
Self::move_piece(mv.source as u8, mv.target as u8, own_pieces);
|
||||
Self::move_rook_castle(mv.target as u8, own_pieces);
|
||||
self.state.set_en_passant_target_square(None);
|
||||
self.state.set_castling_ability(color, Castle::None)
|
||||
}
|
||||
@@ -238,7 +238,7 @@ impl Board {
|
||||
_ => return,
|
||||
};
|
||||
|
||||
Board::move_piece(rook_source, rook_target, pieces);
|
||||
Self::move_piece(rook_source, rook_target, pieces);
|
||||
}
|
||||
|
||||
fn promote_piece(square: u8, pieces: &mut [Piece; 6], promote: Promote) {
|
||||
@@ -320,10 +320,10 @@ pub enum Color {
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub const fn opponent_color(color: Color) -> Color {
|
||||
pub const fn opponent_color(color: Self) -> Self {
|
||||
match color {
|
||||
Color::White => Color::Black,
|
||||
Color::Black => Color::White,
|
||||
Self::White => Self::Black,
|
||||
Self::Black => Self::White,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_turn(&self) -> Color {
|
||||
pub const fn next_turn(&self) -> Color {
|
||||
self.side_to_move
|
||||
}
|
||||
|
||||
|
||||
197
src/magic.rs
197
src/magic.rs
@@ -2,6 +2,8 @@ use crate::attack::{
|
||||
bishop_attacks_on_the_fly, mask_bishop_attacks, mask_rook_attacks, rook_attacks_on_the_fly,
|
||||
set_occupancy, BISHOP_RELEVANT_BITS, ROOK_RELEVANT_BITS,
|
||||
};
|
||||
use crate::bitboard::{bit_count, square_to_bitboard, EMPTY, RANK_8};
|
||||
use crate::square::Square;
|
||||
use rand::rngs::SmallRng;
|
||||
use rand::{RngCore, SeedableRng};
|
||||
use u64 as Bitboard;
|
||||
@@ -50,10 +52,10 @@ pub const BISHOP_MAGIC: [u64; 64] = [
|
||||
];
|
||||
|
||||
pub fn random_uint64(state: &mut SmallRng) -> u64 {
|
||||
let n1 = state.next_u32() as u64 & 0xFFFF;
|
||||
let n2 = state.next_u32() as u64 & 0xFFFF;
|
||||
let n3 = state.next_u32() as u64 & 0xFFFF;
|
||||
let n4 = state.next_u32() as u64 & 0xFFFF;
|
||||
let n1 = state.next_u32() as u64 & 0xffff;
|
||||
let n2 = state.next_u32() as u64 & 0xffff;
|
||||
let n3 = state.next_u32() as u64 & 0xffff;
|
||||
let n4 = state.next_u32() as u64 & 0xffff;
|
||||
|
||||
n1 | (n2 << (16)) | (n3 << (32)) | (n4 << (48))
|
||||
}
|
||||
@@ -62,120 +64,119 @@ pub fn generate_magic_number_candidate(state: &mut SmallRng) -> u64 {
|
||||
random_uint64(state) & random_uint64(state) & random_uint64(state)
|
||||
}
|
||||
|
||||
pub fn find_rook_magic_numbers(relevant_bits: u8, square: Bitboard, state: &mut SmallRng) -> u64 {
|
||||
let mut occupancies: [u64; 4096] = [0; 4096];
|
||||
let mut attacks: [u64; 4096] = [0; 4096];
|
||||
|
||||
let attack_mask = mask_rook_attacks(square);
|
||||
let occupancy_indices = 1_u64 << (relevant_bits);
|
||||
|
||||
for index in 0..occupancy_indices {
|
||||
occupancies[index as usize] =
|
||||
set_occupancy(index, attack_mask.count_ones() as u8, attack_mask);
|
||||
attacks[index as usize] = rook_attacks_on_the_fly(square, occupancies[index as usize]);
|
||||
}
|
||||
|
||||
for _ in 0..100_000_000 {
|
||||
let magic_number = generate_magic_number_candidate(state);
|
||||
if 6 > (attack_mask.wrapping_mul(magic_number) & 0xFF00000000000000_u64).count_ones() {
|
||||
continue;
|
||||
}
|
||||
let mut used_attacks = [0; 4096];
|
||||
|
||||
let mut fail = false;
|
||||
for idx in 0..occupancy_indices {
|
||||
let magic_idx = (occupancies[idx as usize].wrapping_mul(magic_number)
|
||||
>> ((64 - relevant_bits) as u32)) as usize;
|
||||
|
||||
if used_attacks[magic_idx] == 0_u64 {
|
||||
used_attacks[magic_idx] = attacks[idx as usize];
|
||||
} else if used_attacks[magic_idx] != attacks[idx as usize] {
|
||||
fail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !fail {
|
||||
return magic_number;
|
||||
}
|
||||
}
|
||||
|
||||
0_u64
|
||||
}
|
||||
|
||||
pub fn find_bishop_magic_numbers(relevant_bits: u8, square: Bitboard, state: &mut SmallRng) -> u64 {
|
||||
let mut occupancies: [u64; 4096] = [0; 4096];
|
||||
let mut attacks: [u64; 4096] = [0; 4096];
|
||||
|
||||
let attack_mask = mask_bishop_attacks(square);
|
||||
let occupancy_indices = 1_u64 << (relevant_bits);
|
||||
|
||||
for index in 0..occupancy_indices {
|
||||
occupancies[index as usize] =
|
||||
set_occupancy(index, attack_mask.count_ones() as u8, attack_mask);
|
||||
attacks[index as usize] = bishop_attacks_on_the_fly(square, occupancies[index as usize]);
|
||||
}
|
||||
|
||||
for _ in 0..100_000_000 {
|
||||
let magic_number = generate_magic_number_candidate(state);
|
||||
if 6 > (attack_mask.wrapping_mul(magic_number) & 0xFF00000000000000_u64).count_ones() {
|
||||
continue;
|
||||
}
|
||||
let mut used_attacks = [0; 4096];
|
||||
|
||||
let mut fail = false;
|
||||
for idx in 0..occupancy_indices {
|
||||
let magic_idx = (occupancies[idx as usize].wrapping_mul(magic_number)
|
||||
>> ((64 - relevant_bits) as u32)) as usize;
|
||||
|
||||
if used_attacks[magic_idx] == 0_u64 {
|
||||
used_attacks[magic_idx] = attacks[idx as usize];
|
||||
} else if used_attacks[magic_idx] != attacks[idx as usize] {
|
||||
fail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !fail {
|
||||
return magic_number;
|
||||
}
|
||||
}
|
||||
|
||||
0_u64
|
||||
}
|
||||
|
||||
pub fn init_magic_arrays() {
|
||||
let mut state = SmallRng::seed_from_u64(1804289383);
|
||||
|
||||
for idx in 0..64 {
|
||||
let rook_magic_number =
|
||||
find_rook_magic_numbers(ROOK_RELEVANT_BITS[idx], 1_u64 << (idx), &mut state);
|
||||
for square in Square::A1..=Square::H8 {
|
||||
let rook_magic_number = find_magic_numbers(
|
||||
ROOK_RELEVANT_BITS[square],
|
||||
square_to_bitboard(square),
|
||||
&mut state,
|
||||
mask_rook_attacks,
|
||||
rook_attacks_on_the_fly,
|
||||
);
|
||||
unsafe {
|
||||
ROOK_MAGIC_INIT[idx] = rook_magic_number;
|
||||
ROOK_MAGIC_INIT[square] = rook_magic_number;
|
||||
}
|
||||
}
|
||||
for idx in 0..64 {
|
||||
let bishop_magic_number =
|
||||
find_bishop_magic_numbers(BISHOP_RELEVANT_BITS[idx], 1_u64 << (idx), &mut state);
|
||||
for square in Square::A1..=Square::H8 {
|
||||
let bishop_magic_number = find_magic_numbers(
|
||||
BISHOP_RELEVANT_BITS[square],
|
||||
square_to_bitboard(square),
|
||||
&mut state,
|
||||
mask_bishop_attacks,
|
||||
bishop_attacks_on_the_fly,
|
||||
);
|
||||
unsafe {
|
||||
BISHOP_MAGIC_INIT[idx] = bishop_magic_number;
|
||||
BISHOP_MAGIC_INIT[square] = bishop_magic_number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_magic_numbers<F1, F2>(
|
||||
relevant_bits: usize,
|
||||
bitboard: Bitboard,
|
||||
state: &mut SmallRng,
|
||||
mask_attacks: F1,
|
||||
attacks_on_the_fly: F2,
|
||||
) -> u64
|
||||
where
|
||||
F1: Fn(Bitboard) -> Bitboard,
|
||||
F2: Fn(Bitboard, Bitboard) -> Bitboard,
|
||||
{
|
||||
let mut occupancies: [u64; 4096] = [0; 4096];
|
||||
let mut attacks: [u64; 4096] = [0; 4096];
|
||||
|
||||
let attack_mask = mask_attacks(bitboard);
|
||||
let occupancy_indices = square_to_bitboard(relevant_bits);
|
||||
|
||||
for index in 0..occupancy_indices {
|
||||
occupancies[index as usize] = set_occupancy(index, bit_count(attack_mask), attack_mask);
|
||||
attacks[index as usize] = attacks_on_the_fly(bitboard, occupancies[index as usize]);
|
||||
}
|
||||
|
||||
generate_magic_number(
|
||||
relevant_bits,
|
||||
state,
|
||||
attack_mask,
|
||||
occupancy_indices,
|
||||
occupancies,
|
||||
attacks,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn generate_magic_number(
|
||||
relevant_bits: usize,
|
||||
state: &mut SmallRng,
|
||||
attack_mask: u64,
|
||||
occupancy_indices: u64,
|
||||
occupancies: [u64; 4096],
|
||||
attacks: [u64; 4096],
|
||||
) -> u64 {
|
||||
for _ in 0..100000000 {
|
||||
let magic_number = generate_magic_number_candidate(state);
|
||||
if 6 > bit_count(attack_mask.wrapping_mul(magic_number) & RANK_8) {
|
||||
continue;
|
||||
}
|
||||
let mut used_attacks = [0; 4096];
|
||||
|
||||
let mut fail = false;
|
||||
for index in 0..occupancy_indices {
|
||||
let magic = (occupancies[index as usize].wrapping_mul(magic_number)
|
||||
>> ((64 - relevant_bits) as u32)) as usize;
|
||||
|
||||
if used_attacks[magic] == EMPTY {
|
||||
used_attacks[magic] = attacks[index as usize];
|
||||
} else if used_attacks[magic] != attacks[index as usize] {
|
||||
fail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !fail {
|
||||
return magic_number;
|
||||
}
|
||||
}
|
||||
|
||||
EMPTY
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::init_magic_arrays;
|
||||
use crate::magic::{BISHOP_MAGIC, BISHOP_MAGIC_INIT, ROOK_MAGIC, ROOK_MAGIC_INIT};
|
||||
use crate::{
|
||||
magic::{BISHOP_MAGIC, BISHOP_MAGIC_INIT, ROOK_MAGIC, ROOK_MAGIC_INIT},
|
||||
square::Square,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_init_magic_arrays() -> Result<(), String> {
|
||||
init_magic_arrays();
|
||||
|
||||
for idx in 0..64 {
|
||||
for index in Square::A1..=Square::H8 {
|
||||
unsafe {
|
||||
assert_eq!(ROOK_MAGIC[idx], ROOK_MAGIC_INIT[idx]);
|
||||
assert_eq!(BISHOP_MAGIC[idx], BISHOP_MAGIC_INIT[idx]);
|
||||
assert_eq!(ROOK_MAGIC[index], ROOK_MAGIC_INIT[index]);
|
||||
assert_eq!(BISHOP_MAGIC[index], BISHOP_MAGIC_INIT[index]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod attack;
|
||||
pub mod bitboard;
|
||||
pub mod board;
|
||||
pub mod fen;
|
||||
pub mod game;
|
||||
@@ -6,6 +7,7 @@ pub mod magic;
|
||||
pub mod r#move;
|
||||
pub mod movegen;
|
||||
pub mod perft;
|
||||
pub mod square;
|
||||
|
||||
fn main() {
|
||||
attack::init_attacks();
|
||||
|
||||
@@ -9,8 +9,8 @@ use u64 as Bitboard;
|
||||
|
||||
const NOT_A_FILE: Bitboard = 0xfefefefefefefefe;
|
||||
const NOT_H_FILE: Bitboard = 0x7f7f7f7f7f7f7f7f;
|
||||
const RANK_4: Bitboard = 0x00000000FF000000;
|
||||
const RANK_5: Bitboard = 0x000000FF00000000;
|
||||
const RANK_4: Bitboard = 0x00000000ff000000;
|
||||
const RANK_5: Bitboard = 0x000000ff00000000;
|
||||
|
||||
pub fn pawn_pseudo_moves(
|
||||
pawns: Bitboard,
|
||||
|
||||
@@ -24,7 +24,7 @@ const MAX_DEPTH: u8 = 3;
|
||||
|
||||
pub fn perftree_script() {
|
||||
let fen = "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1";
|
||||
let mut game = crate::fen::from_fen(fen).unwrap();
|
||||
let mut game = crate::fen::from_fen(fen).expect("Invalid FEN string");
|
||||
let (mut nodes, depth): (u64, u8) = (0, MAX_DEPTH);
|
||||
driver(&mut game, &mut nodes, depth);
|
||||
println!();
|
||||
@@ -52,7 +52,7 @@ pub fn square_to_notation(square: u8) -> &'static str {
|
||||
map.insert(52, "e7"); map.insert(53, "f7"); map.insert(54, "g7"); map.insert(55, "h7");
|
||||
map.insert(56, "a8"); map.insert(57, "b8"); map.insert(58, "c8"); map.insert(59, "d8");
|
||||
map.insert(60, "e8"); map.insert(61, "f8"); map.insert(62, "g8"); map.insert(63, "h8");
|
||||
map.get(&square).unwrap()
|
||||
map.get(&square).expect("Invalid square")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
87
src/square.rs
Normal file
87
src/square.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
use u64 as Bitboard;
|
||||
|
||||
pub struct Square {}
|
||||
|
||||
impl Square {
|
||||
pub const A1: usize = 0;
|
||||
pub const B1: usize = 1;
|
||||
pub const C1: usize = 2;
|
||||
pub const D1: usize = 3;
|
||||
pub const E1: usize = 4;
|
||||
pub const F1: usize = 5;
|
||||
pub const G1: usize = 6;
|
||||
pub const H1: usize = 7;
|
||||
|
||||
pub const A2: usize = 8;
|
||||
pub const B2: usize = 9;
|
||||
pub const C2: usize = 10;
|
||||
pub const D2: usize = 11;
|
||||
pub const E2: usize = 12;
|
||||
pub const F2: usize = 13;
|
||||
pub const G2: usize = 14;
|
||||
pub const H2: usize = 15;
|
||||
|
||||
pub const A3: usize = 16;
|
||||
pub const B3: usize = 17;
|
||||
pub const C3: usize = 18;
|
||||
pub const D3: usize = 19;
|
||||
pub const E3: usize = 20;
|
||||
pub const F3: usize = 21;
|
||||
pub const G3: usize = 22;
|
||||
pub const H3: usize = 23;
|
||||
|
||||
pub const A4: usize = 24;
|
||||
pub const B4: usize = 25;
|
||||
pub const C4: usize = 26;
|
||||
pub const D4: usize = 27;
|
||||
pub const E4: usize = 28;
|
||||
pub const F4: usize = 29;
|
||||
pub const G4: usize = 30;
|
||||
pub const H4: usize = 31;
|
||||
|
||||
pub const A5: usize = 32;
|
||||
pub const B5: usize = 33;
|
||||
pub const C5: usize = 34;
|
||||
pub const D5: usize = 35;
|
||||
pub const E5: usize = 36;
|
||||
pub const F5: usize = 37;
|
||||
pub const G5: usize = 38;
|
||||
pub const H5: usize = 39;
|
||||
|
||||
pub const A6: usize = 40;
|
||||
pub const B6: usize = 41;
|
||||
pub const C6: usize = 42;
|
||||
pub const D6: usize = 43;
|
||||
pub const E6: usize = 44;
|
||||
pub const F6: usize = 45;
|
||||
pub const G6: usize = 46;
|
||||
pub const H6: usize = 47;
|
||||
|
||||
pub const A7: usize = 48;
|
||||
pub const B7: usize = 49;
|
||||
pub const C7: usize = 50;
|
||||
pub const D7: usize = 51;
|
||||
pub const E7: usize = 52;
|
||||
pub const F7: usize = 53;
|
||||
pub const G7: usize = 54;
|
||||
pub const H7: usize = 55;
|
||||
|
||||
pub const A8: usize = 56;
|
||||
pub const B8: usize = 57;
|
||||
pub const C8: usize = 58;
|
||||
pub const D8: usize = 59;
|
||||
pub const E8: usize = 60;
|
||||
pub const F8: usize = 61;
|
||||
pub const G8: usize = 62;
|
||||
pub const H8: usize = 63;
|
||||
}
|
||||
|
||||
pub const fn coords_to_square(rank: usize, file: usize) -> usize {
|
||||
rank * 8 + file
|
||||
}
|
||||
|
||||
impl Square {
|
||||
pub const fn to_bitboard(sq: usize) -> Bitboard {
|
||||
1_u64 << sq
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user