diff --git a/Cargo.lock b/Cargo.lock index b79430e..095f1cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,9 +59,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.17" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -69,9 +69,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.17" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -81,9 +81,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 667470a..8855e75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,14 +4,14 @@ version = "0.1.0" edition = "2021" [dependencies] -clap = { version = "4.5.16", features = ["derive"] } +clap = { version = "4.5.20", features = ["derive"] } rand = { version = "0.8.5", features = ["small_rng"] } [lints.rust] -# unsafe_code = "forbid" +unsafe_code = "forbid" [lints.clippy] enum_glob_use = "deny" -pedantic = "deny" +# pedantic = "deny" nursery = { level = "deny", priority = -1 } unwrap_used = "deny" diff --git a/src/movegen/attack_generator.rs b/src/movegen/attack_generator.rs index cb68ab4..bfc320b 100644 --- a/src/movegen/attack_generator.rs +++ b/src/movegen/attack_generator.rs @@ -32,11 +32,11 @@ pub const BISHOP_RELEVANT_BITS: [usize; 64] = [ 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]; +static PAWN_ATTACKS: LazyLock<[[Bitboard; 2]; 64]> = LazyLock::new(init_pawn_attacks); +static KNIGHT_ATTACKS: LazyLock<[Bitboard; 64]> = LazyLock::new(init_knight_attacks); +static KING_ATTACKS: LazyLock<[Bitboard; 64]> = LazyLock::new(init_king_attacks); +static BISHOP_ATTACKS: LazyLock> = LazyLock::new(init_bishop_attacks); +static ROOK_ATTACKS: LazyLock> = LazyLock::new(init_rook_attacks); const fn pawn_attacks(bitboard: Bitboard, color: Color) -> Bitboard { 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 { - unsafe { - match color { - Color::White => PAWN_ATTACKS[square][0], - Color::Black => PAWN_ATTACKS[square][1], - } + 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] } + KNIGHT_ATTACKS[square] } pub fn fetch_king_attacks(square: usize) -> Bitboard { - unsafe { KING_ATTACKS[square] } + 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] - } + 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] - } + 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_pawn_attacks() -> [[Bitboard; 2]; 64] { + array::from_fn(|square| { + [ + pawn_attacks(square_to_bitboard(square), Color::White), + 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_knight_attacks() -> [Bitboard; 64] { + array::from_fn(|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_king_attacks() -> [Bitboard; 64] { + array::from_fn(|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_atks: Box<[[Bitboard; 512]; 64]> = Box::new([[0; 512]; 64]); for square in 0..64 { 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 { 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; + 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); - } + bishop_atks[square][magic_index] = + 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_atks: Box<[[Bitboard; 4096]; 64]> = Box::new([[0; 4096]; 64]); for square in 0..64 { 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 { 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; + 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); - } + rook_atks[square][magic_index] = + rook_attacks_on_the_fly(square_to_bitboard(square), occupancy); } } + rook_atks } +use std::{array, sync::LazyLock}; + pub fn init_attacks() { init_pawn_attacks(); init_knight_attacks(); init_king_attacks(); init_bishop_attacks(); - init_rook_attacks() + init_rook_attacks(); } #[cfg(test)] diff --git a/src/movegen/magic_bitboards.rs b/src/movegen/magic_bitboards.rs index 901b6cd..b3c964a 100644 --- a/src/movegen/magic_bitboards.rs +++ b/src/movegen/magic_bitboards.rs @@ -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] pub const ROOK_MAGIC: [u64; 64] = [ 0x80008010284000, 0x540024820001000, 0x1300092000124100, 0x480240800500080, @@ -50,138 +37,3 @@ pub const BISHOP_MAGIC: [u64; 64] = [ 0x1a802808062882, 0x80110101012100, 0xa40100200420890, 0x48025100208801, 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( - 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(()) - } -}