Use LazyLock for attack tables, remove all unsafe blocks
This commit is contained in:
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -59,9 +59,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.17"
|
version = "4.5.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
|
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -69,9 +69,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.17"
|
version = "4.5.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
|
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -81,9 +81,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.13"
|
version = "4.5.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
|
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.16", features = ["derive"] }
|
clap = { version = "4.5.20", features = ["derive"] }
|
||||||
rand = { version = "0.8.5", features = ["small_rng"] }
|
rand = { version = "0.8.5", features = ["small_rng"] }
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
# unsafe_code = "forbid"
|
unsafe_code = "forbid"
|
||||||
|
|
||||||
[lints.clippy]
|
[lints.clippy]
|
||||||
enum_glob_use = "deny"
|
enum_glob_use = "deny"
|
||||||
pedantic = "deny"
|
# pedantic = "deny"
|
||||||
nursery = { level = "deny", priority = -1 }
|
nursery = { level = "deny", priority = -1 }
|
||||||
unwrap_used = "deny"
|
unwrap_used = "deny"
|
||||||
|
|||||||
@@ -32,11 +32,11 @@ pub const BISHOP_RELEVANT_BITS: [usize; 64] = [
|
|||||||
6, 5, 5, 5, 5, 5, 5, 6,
|
6, 5, 5, 5, 5, 5, 5, 6,
|
||||||
];
|
];
|
||||||
|
|
||||||
static mut PAWN_ATTACKS: [[Bitboard; 2]; 64] = [[0; 2]; 64];
|
static PAWN_ATTACKS: LazyLock<[[Bitboard; 2]; 64]> = LazyLock::new(init_pawn_attacks);
|
||||||
static mut KNIGHT_ATTACKS: [Bitboard; 64] = [0; 64];
|
static KNIGHT_ATTACKS: LazyLock<[Bitboard; 64]> = LazyLock::new(init_knight_attacks);
|
||||||
static mut KING_ATTACKS: [Bitboard; 64] = [0; 64];
|
static KING_ATTACKS: LazyLock<[Bitboard; 64]> = LazyLock::new(init_king_attacks);
|
||||||
static mut BISHOP_ATTACKS: [[Bitboard; 512]; 64] = [[0; 512]; 64];
|
static BISHOP_ATTACKS: LazyLock<Box<[[Bitboard; 512]; 64]>> = LazyLock::new(init_bishop_attacks);
|
||||||
static mut ROOK_ATTACKS: [[Bitboard; 4096]; 64] = [[0; 4096]; 64];
|
static ROOK_ATTACKS: LazyLock<Box<[[Bitboard; 4096]; 64]>> = LazyLock::new(init_rook_attacks);
|
||||||
|
|
||||||
const fn pawn_attacks(bitboard: Bitboard, color: Color) -> Bitboard {
|
const fn pawn_attacks(bitboard: Bitboard, color: Color) -> Bitboard {
|
||||||
let mut attacks = EMPTY;
|
let mut attacks = EMPTY;
|
||||||
@@ -208,67 +208,60 @@ pub fn set_occupancy(index: u64, relevant_bits: usize, mut attack_mask: Bitboard
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch_pawn_attacks(square: usize, color: Color) -> Bitboard {
|
pub fn fetch_pawn_attacks(square: usize, color: Color) -> Bitboard {
|
||||||
unsafe {
|
|
||||||
match color {
|
match color {
|
||||||
Color::White => PAWN_ATTACKS[square][0],
|
Color::White => PAWN_ATTACKS[square][0],
|
||||||
Color::Black => PAWN_ATTACKS[square][1],
|
Color::Black => PAWN_ATTACKS[square][1],
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch_knight_attacks(square: usize) -> Bitboard {
|
pub fn fetch_knight_attacks(square: usize) -> Bitboard {
|
||||||
unsafe { KNIGHT_ATTACKS[square] }
|
KNIGHT_ATTACKS[square]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch_king_attacks(square: usize) -> Bitboard {
|
pub fn fetch_king_attacks(square: usize) -> Bitboard {
|
||||||
unsafe { KING_ATTACKS[square] }
|
KING_ATTACKS[square]
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::movegen::magic_bitboards::{BISHOP_MAGIC, ROOK_MAGIC};
|
use crate::movegen::magic_bitboards::{BISHOP_MAGIC, ROOK_MAGIC};
|
||||||
|
|
||||||
pub fn fetch_bishop_attacks(mut occupancy: Bitboard, square: usize) -> Bitboard {
|
pub fn fetch_bishop_attacks(mut occupancy: Bitboard, square: usize) -> Bitboard {
|
||||||
unsafe {
|
|
||||||
occupancy &= mask_bishop_attacks(square_to_bitboard_wrapping(square));
|
occupancy &= mask_bishop_attacks(square_to_bitboard_wrapping(square));
|
||||||
occupancy = occupancy.wrapping_mul(BISHOP_MAGIC[square]);
|
occupancy = occupancy.wrapping_mul(BISHOP_MAGIC[square]);
|
||||||
occupancy >>= 64 - BISHOP_RELEVANT_BITS[square];
|
occupancy >>= 64 - BISHOP_RELEVANT_BITS[square];
|
||||||
BISHOP_ATTACKS[square][occupancy as usize]
|
BISHOP_ATTACKS[square][occupancy as usize]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch_rook_attacks(mut occupancy: Bitboard, square: usize) -> Bitboard {
|
pub fn fetch_rook_attacks(mut occupancy: Bitboard, square: usize) -> Bitboard {
|
||||||
unsafe {
|
|
||||||
occupancy &= mask_rook_attacks(square_to_bitboard_wrapping(square));
|
occupancy &= mask_rook_attacks(square_to_bitboard_wrapping(square));
|
||||||
occupancy = occupancy.wrapping_mul(ROOK_MAGIC[square]);
|
occupancy = occupancy.wrapping_mul(ROOK_MAGIC[square]);
|
||||||
occupancy >>= 64 - ROOK_RELEVANT_BITS[square];
|
occupancy >>= 64 - ROOK_RELEVANT_BITS[square];
|
||||||
ROOK_ATTACKS[square][occupancy as usize]
|
ROOK_ATTACKS[square][occupancy as usize]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch_queen_attacks(occupancy: Bitboard, square: usize) -> Bitboard {
|
pub fn fetch_queen_attacks(occupancy: Bitboard, square: usize) -> Bitboard {
|
||||||
fetch_rook_attacks(occupancy, square) | fetch_bishop_attacks(occupancy, square)
|
fetch_rook_attacks(occupancy, square) | fetch_bishop_attacks(occupancy, square)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_pawn_attacks() {
|
pub fn init_pawn_attacks() -> [[Bitboard; 2]; 64] {
|
||||||
(0..64).for_each(|square| unsafe {
|
array::from_fn(|square| {
|
||||||
PAWN_ATTACKS[square][0] = pawn_attacks(square_to_bitboard(square), Color::White);
|
[
|
||||||
PAWN_ATTACKS[square][1] = pawn_attacks(square_to_bitboard(square), Color::Black);
|
pawn_attacks(square_to_bitboard(square), Color::White),
|
||||||
});
|
pawn_attacks(square_to_bitboard(square), Color::Black),
|
||||||
|
]
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_knight_attacks() {
|
pub fn init_knight_attacks() -> [Bitboard; 64] {
|
||||||
(0..64).for_each(|square| unsafe {
|
array::from_fn(|square| knight_attacks(square_to_bitboard(square)))
|
||||||
KNIGHT_ATTACKS[square] = knight_attacks(square_to_bitboard(square));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_king_attacks() {
|
pub fn init_king_attacks() -> [Bitboard; 64] {
|
||||||
(0..64).for_each(|square| unsafe {
|
array::from_fn(|square| king_attacks(square_to_bitboard(square)))
|
||||||
KING_ATTACKS[square] = king_attacks(square_to_bitboard(square));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_bishop_attacks() {
|
pub fn init_bishop_attacks() -> Box<[[Bitboard; 512]; 64]> {
|
||||||
let mut bishop_masks = [0; 64];
|
let mut bishop_masks = [0; 64];
|
||||||
|
let mut bishop_atks: Box<[[Bitboard; 512]; 64]> = Box::new([[0; 512]; 64]);
|
||||||
|
|
||||||
for square in 0..64 {
|
for square in 0..64 {
|
||||||
bishop_masks[square] = mask_bishop_attacks(square_to_bitboard(square));
|
bishop_masks[square] = mask_bishop_attacks(square_to_bitboard(square));
|
||||||
@@ -277,20 +270,20 @@ pub fn init_bishop_attacks() {
|
|||||||
|
|
||||||
for index in 0..occupancy_indices {
|
for index in 0..occupancy_indices {
|
||||||
let occupancy = set_occupancy(index, BISHOP_RELEVANT_BITS[square], attack_mask);
|
let occupancy = set_occupancy(index, BISHOP_RELEVANT_BITS[square], attack_mask);
|
||||||
unsafe {
|
|
||||||
let magic_index = (occupancy.wrapping_mul(BISHOP_MAGIC[square])
|
let magic_index = (occupancy.wrapping_mul(BISHOP_MAGIC[square])
|
||||||
>> (64 - BISHOP_RELEVANT_BITS[square]))
|
>> (64 - BISHOP_RELEVANT_BITS[square])) as usize;
|
||||||
as usize;
|
|
||||||
|
|
||||||
BISHOP_ATTACKS[square][magic_index] =
|
bishop_atks[square][magic_index] =
|
||||||
bishop_attacks_on_the_fly(square_to_bitboard(square), occupancy);
|
bishop_attacks_on_the_fly(square_to_bitboard(square), occupancy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
bishop_atks
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_rook_attacks() {
|
#[allow(clippy::large_stack_frames)]
|
||||||
|
pub fn init_rook_attacks() -> Box<[[Bitboard; 4096]; 64]> {
|
||||||
let mut rook_masks = [0; 64];
|
let mut rook_masks = [0; 64];
|
||||||
|
let mut rook_atks: Box<[[Bitboard; 4096]; 64]> = Box::new([[0; 4096]; 64]);
|
||||||
|
|
||||||
for square in 0..64 {
|
for square in 0..64 {
|
||||||
rook_masks[square] = mask_rook_attacks(square_to_bitboard(square));
|
rook_masks[square] = mask_rook_attacks(square_to_bitboard(square));
|
||||||
@@ -299,24 +292,24 @@ pub fn init_rook_attacks() {
|
|||||||
|
|
||||||
for idx in 0..occupancy_indices {
|
for idx in 0..occupancy_indices {
|
||||||
let occupancy = set_occupancy(idx, ROOK_RELEVANT_BITS[square], attack_mask);
|
let occupancy = set_occupancy(idx, ROOK_RELEVANT_BITS[square], attack_mask);
|
||||||
unsafe {
|
|
||||||
let magic_index = (occupancy.wrapping_mul(ROOK_MAGIC[square])
|
let magic_index = (occupancy.wrapping_mul(ROOK_MAGIC[square])
|
||||||
>> (64 - ROOK_RELEVANT_BITS[square]))
|
>> (64 - ROOK_RELEVANT_BITS[square])) as usize;
|
||||||
as usize;
|
|
||||||
|
|
||||||
ROOK_ATTACKS[square][magic_index] =
|
rook_atks[square][magic_index] =
|
||||||
rook_attacks_on_the_fly(square_to_bitboard(square), occupancy);
|
rook_attacks_on_the_fly(square_to_bitboard(square), occupancy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
rook_atks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use std::{array, sync::LazyLock};
|
||||||
|
|
||||||
pub fn init_attacks() {
|
pub fn init_attacks() {
|
||||||
init_pawn_attacks();
|
init_pawn_attacks();
|
||||||
init_knight_attacks();
|
init_knight_attacks();
|
||||||
init_king_attacks();
|
init_king_attacks();
|
||||||
init_bishop_attacks();
|
init_bishop_attacks();
|
||||||
init_rook_attacks()
|
init_rook_attacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -1,16 +1,3 @@
|
|||||||
use crate::board::bitboard::{bit_count, square_to_bitboard, EMPTY, RANK_8};
|
|
||||||
use crate::board::square::Square;
|
|
||||||
use crate::movegen::attack_generator::{
|
|
||||||
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 rand::rngs::SmallRng;
|
|
||||||
use rand::{RngCore, SeedableRng};
|
|
||||||
use u64 as Bitboard;
|
|
||||||
|
|
||||||
static mut _BISHOP_MAGIC_INIT: [Bitboard; 64] = [0; 64];
|
|
||||||
static mut _ROOK_MAGIC_INIT: [Bitboard; 64] = [0; 64];
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub const ROOK_MAGIC: [u64; 64] = [
|
pub const ROOK_MAGIC: [u64; 64] = [
|
||||||
0x80008010284000, 0x540024820001000, 0x1300092000124100, 0x480240800500080,
|
0x80008010284000, 0x540024820001000, 0x1300092000124100, 0x480240800500080,
|
||||||
@@ -50,138 +37,3 @@ pub const BISHOP_MAGIC: [u64; 64] = [
|
|||||||
0x1a802808062882, 0x80110101012100, 0xa40100200420890, 0x48025100208801,
|
0x1a802808062882, 0x80110101012100, 0xa40100200420890, 0x48025100208801,
|
||||||
0x4000000004208200, 0x804a10011602, 0x200a24c410041500, 0x8408080088061020,
|
0x4000000004208200, 0x804a10011602, 0x200a24c410041500, 0x8408080088061020,
|
||||||
];
|
];
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
n1 | (n2 << (16)) | (n3 << (32)) | (n4 << (48))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn __generate_magic_number_candidate(state: &mut SmallRng) -> u64 {
|
|
||||||
_random_uint64(state) & _random_uint64(state) & _random_uint64(state)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _init_magic_arrays() {
|
|
||||||
let mut state = SmallRng::seed_from_u64(1804289383);
|
|
||||||
|
|
||||||
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[square] = rook_magic_number;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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[square] = bishop_magic_number;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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::{
|
|
||||||
board::square::Square,
|
|
||||||
movegen::magic_bitboards::{
|
|
||||||
BISHOP_MAGIC, ROOK_MAGIC, _BISHOP_MAGIC_INIT, _ROOK_MAGIC_INIT,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_init_magic_arrays() -> Result<(), String> {
|
|
||||||
_init_magic_arrays();
|
|
||||||
|
|
||||||
for index in Square::A1..=Square::H8 {
|
|
||||||
unsafe {
|
|
||||||
assert_eq!(ROOK_MAGIC[index], _ROOK_MAGIC_INIT[index]);
|
|
||||||
assert_eq!(BISHOP_MAGIC[index], _BISHOP_MAGIC_INIT[index]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user