Add piece attack tables and magic bitboards

This commit is contained in:
2024-05-29 20:37:15 +03:00
parent c00c6a7b15
commit 9876fc0465
5 changed files with 772 additions and 0 deletions

68
Cargo.lock generated
View File

@@ -2,6 +2,74 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "getrandom"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06fddc2749e0528d2813f95e050e87e52c8cbbae56223b9babf73b3e53b0cc6"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "ippos"
version = "0.1.0"
dependencies = [
"rand",
]
[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"

View File

@@ -6,3 +6,13 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = { version = "0.8.5", features = ["small_rng"] }
[lints.rust]
# unsafe_code = "forbid"
[lints.clippy]
enum_glob_use = "deny"
#pedantic = "deny"
#nursery = "deny"
#unwrap_used = "deny"

505
src/attack.rs Normal file
View File

@@ -0,0 +1,505 @@
use crate::board::Color;
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] = [
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: [u8; 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];
fn pawn_attacks(bitboard: Bitboard, color: Color) -> Bitboard {
let mut attacks = 0_u64;
match color {
Color::Black => {
attacks |= (bitboard & NOT_H_FILE) >> (7);
attacks |= (bitboard & NOT_A_FILE) >> (9);
}
Color::White => {
attacks |= (bitboard & NOT_A_FILE) << (7);
attacks |= (bitboard & NOT_H_FILE) << (9);
}
};
attacks
}
fn knight_attacks(bitboard: Bitboard) -> Bitboard {
let mut attacks = 0_u64;
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
}
fn king_attacks(bitboard: Bitboard) -> Bitboard {
let mut attacks = 0_u64;
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
}
pub fn mask_bishop_attacks(bitboard: Bitboard) -> Bitboard {
let mut attacks = 0_u64;
let target_rank = (bitboard == 0)
.then(|| 0)
.unwrap_or_else(|| bitboard.trailing_zeros() / 8);
let target_file = (bitboard == 0)
.then(|| 0)
.unwrap_or_else(|| bitboard.trailing_zeros() % 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);
}
(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);
}
(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);
}
(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
}
pub fn mask_rook_attacks(bitboard: Bitboard) -> Bitboard {
let mut attacks = 0_u64;
let target_rank = (bitboard == 0)
.then(|| 0)
.unwrap_or_else(|| bitboard.trailing_zeros() / 8);
let target_file = (bitboard == 0)
.then(|| 0)
.unwrap_or_else(|| bitboard.trailing_zeros() % 8);
let (mut rank, mut file) = (target_rank + 1, target_file + 1);
for rank in rank..=6 {
attacks |= 1_u64 << (rank * 8 + target_file);
}
for file in file..=6 {
attacks |= 1_u64 << (target_rank * 8 + 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);
}
for file in (1..=file).rev() {
attacks |= 1_u64 << (target_rank * 8 + file);
}
attacks
}
pub fn bishop_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard {
let mut attacks = 0_u64;
let target_rank = (bitboard == 0)
.then(|| 0)
.unwrap_or_else(|| bitboard.trailing_zeros() / 8);
let target_file = (bitboard == 0)
.then(|| 0)
.unwrap_or_else(|| bitboard.trailing_zeros() % 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);
attacks |= attack_square;
if attack_square & blocker != 0 {
break;
}
}
(rank, file) = (target_rank.saturating_sub(1), target_file + 1);
for (rank, file) in (0..=rank).rev().zip(file..=7) {
let attack_square = 1_u64 << (rank * 8 + file);
attacks |= attack_square;
if attack_square & blocker != 0 {
break;
}
}
(rank, file) = (target_rank + 1, target_file.saturating_sub(1));
for (rank, file) in (rank..=7).zip((0..=file).rev()) {
let attack_square = 1_u64 << (rank * 8 + file);
attacks |= attack_square;
if attack_square & blocker != 0 {
break;
}
}
(rank, file) = (target_rank.saturating_sub(1), target_file.saturating_sub(1));
for (rank, file) in (0..=rank).rev().zip((0..=file).rev()) {
let attack_square = 1_u64 << (rank * 8 + file);
attacks |= attack_square;
if attack_square & blocker != 0 {
break;
}
}
attacks
}
pub fn rook_attacks_on_the_fly(bitboard: Bitboard, blocker: Bitboard) -> Bitboard {
let mut attacks = 0_u64;
let target_rank = (bitboard == 0)
.then(|| 0)
.unwrap_or_else(|| bitboard.trailing_zeros() / 8);
let target_file = (bitboard == 0)
.then(|| 0)
.unwrap_or_else(|| bitboard.trailing_zeros() % 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);
attacks |= attack_square;
if attack_square & blocker != 0 {
break;
}
}
for file in file..=7 {
let attack_square = 1_u64 << (target_rank * 8 + file);
attacks |= attack_square;
if attack_square & blocker != 0 {
break;
}
}
(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);
attacks |= attack_square;
if attack_square & blocker != 0 {
break;
}
}
for file in (0..=file).rev() {
let attack_square = 1_u64 << (target_rank * 8 + file);
attacks |= attack_square;
if attack_square & blocker != 0 {
break;
}
}
attacks
}
pub fn set_occupancy(index: u64, bits_in_mask: u8, mut attack_mask: Bitboard) -> Bitboard {
let mut occupancy = 0_u64;
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);
}
}
occupancy
}
pub fn get_pawn_attacks(sq: usize, color: Color) -> Bitboard {
unsafe {
match color {
Color::White => PAWN_ATTACKS[sq][0],
Color::Black => PAWN_ATTACKS[sq][1],
}
}
}
pub fn get_knight_attacks(sq: usize) -> Bitboard {
unsafe { KNIGHT_ATTACKS[sq] }
}
pub fn get_king_attacks(sq: usize) -> Bitboard {
unsafe { KING_ATTACKS[sq] }
}
use crate::magic::{BISHOP_MAGIC, ROOK_MAGIC};
pub fn get_bishop_attacks(mut occupancy: Bitboard, sq: 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]
}
}
pub fn get_rook_attacks(mut occupancy: Bitboard, sq: 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]
}
}
pub fn get_queen_attacks(occupancy: Bitboard, sq: usize) -> Bitboard {
get_rook_attacks(occupancy, sq) | get_bishop_attacks(occupancy, sq)
}
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);
});
}
pub fn init_knight_attacks() {
(0..64).for_each(|sq| unsafe {
KNIGHT_ATTACKS[sq] = knight_attacks(1_u64 << (sq));
});
}
pub fn init_king_attacks() {
(0..64).for_each(|sq| unsafe {
KING_ATTACKS[sq] = king_attacks(1_u64 << (sq));
});
}
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 relevant_bits_count = attack_mask.count_ones() as u8;
let occupancy_indices = 1_u64 << (relevant_bits_count);
for idx in 0..occupancy_indices {
let occupancy = set_occupancy(idx, relevant_bits_count, attack_mask);
unsafe {
let magic_index =
occupancy.wrapping_mul(BISHOP_MAGIC[sq]) >> (64 - BISHOP_RELEVANT_BITS[sq]);
BISHOP_ATTACKS[sq][magic_index as usize] =
bishop_attacks_on_the_fly(1_u64 << (sq), occupancy);
}
}
}
}
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 idx in 0..occupancy_indices {
let occupancy = set_occupancy(idx, relevant_bits_count, attack_mask);
unsafe {
let magic_index =
occupancy.wrapping_mul(ROOK_MAGIC[sq]) >> (64 - ROOK_RELEVANT_BITS[sq]);
ROOK_ATTACKS[sq][magic_index as usize] =
rook_attacks_on_the_fly(1_u64 << (sq), occupancy);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::board::{Color, Kind, Piece};
#[test]
fn test_pawn_attacks() -> Result<(), String> {
let white_pawn_a_file = Piece::new(0x100, Kind::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, Kind::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, Kind::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, Kind::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, Kind::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, Kind::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, Kind::Knight, Color::White);
let attacks = knight_attacks(knight_two_attacks.bitboard);
assert_eq!(attacks, 0x20400);
let knight_three_attacks = Piece::new(0x2, Kind::Knight, Color::White);
let attacks = knight_attacks(knight_three_attacks.bitboard);
assert_eq!(attacks, 0x50800);
let knight_three_attacks = Piece::new(0x4, Kind::Knight, Color::White);
let attacks = knight_attacks(knight_three_attacks.bitboard);
assert_eq!(attacks, 0xa1100);
let knight_six_attacks = Piece::new(0x400, Kind::Knight, Color::White);
let attacks = knight_attacks(knight_six_attacks.bitboard);
assert_eq!(attacks, 0xa110011);
let knight_eight_attacks = Piece::new(0x40000, Kind::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, Kind::King, Color::White);
let attacks = king_attacks(king_three_attacks.bitboard);
assert_eq!(attacks, 0x302);
let king_five_attacks = Piece::new(0x2, Kind::King, Color::White);
let attacks = king_attacks(king_five_attacks.bitboard);
assert_eq!(attacks, 0x705);
let king_eight_attacks = Piece::new(0x200, Kind::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, Kind::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, Kind::Bishop, Color::White);
let blocker_c5 = 0x400000000_u64;
let attacks = bishop_attacks_on_the_fly(bishop_d4.bitboard, blocker_c5);
assert_eq!(attacks, 0x8040201400142241);
Ok(())
}
#[test]
fn test_mask_rook_attacks() -> Result<(), String> {
let rook_d4 = Piece::new(0x8000000, Kind::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, Kind::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 = get_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 = get_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 = get_queen_attacks(blockers, queen_d3_square);
assert_eq!(queen_attacks, 0x888482a1c371c22);
Ok(())
}
}

187
src/magic.rs Normal file
View File

@@ -0,0 +1,187 @@
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 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,
0x1001004020081080, 0x500040021000208, 0x6280010000800200, 0x8006a100104180,
0x420800080204000, 0x2802000804008, 0x101805000600080, 0x1000801000080082,
0x102808044000800, 0x280800200040080, 0x1003000100040200, 0xd201000092005100,
0x80004000200041, 0x8400810040002100, 0x210048020008010, 0x220042001008,
0x20850011000800, 0x80808004000200, 0x100c440008424110, 0x2020010885904,
0x120400080002080, 0x40400100210888, 0x4860080040100041, 0x10100080080084,
0x8008080080400, 0x124008080020004, 0x100010400021008, 0x820008200004401,
0x404000a2800080, 0x802000804008, 0x8040809002802004, 0x1000801000800800,
0x40080800800, 0x202800200800400, 0x100204000108, 0x48010c44020000b1,
0x800040008024, 0x402010004000, 0x4209002000110040, 0x12002040aa0050,
0x2002008260010, 0x80a001008020004, 0x701000a00050004, 0x241a040080420001,
0x50043a0800100, 0x8021c002a0148080, 0x200010008080, 0x8080410008080,
0x5180080004110100, 0x1164000402008080, 0x1002800100020080, 0x4100840200,
0x82829101412206, 0x2209004000801021, 0x84010802202, 0x1a208900100005,
0x1001002880005, 0x9008204000841, 0x2c9508221014, 0x8010470400402092,
];
#[rustfmt::skip]
pub const BISHOP_MAGIC: [u64; 64] = [
0x10041000802d00, 0x3410820e4100b0, 0x1008080120200000, 0x4080a0020104000,
0x14152000000050, 0x1010841202844, 0x80480210100208, 0x8042410050022000,
0x2400204810a00, 0x1003020204010e, 0xa012044104010800, 0x804280a042108c2,
0x11040010810, 0x2010228820298000, 0x21440100c210400c, 0x910002104108414,
0x249012002102220, 0x210000421082108, 0xa001001083040100, 0x880c007801441100,
0xe00082ac00a01800, 0x40020004a2032001, 0x82aa016048040580, 0x40205301291000,
0x1060110004100200, 0x8002035010040800, 0x8401001a080508, 0xc0044004050020,
0x409010080104008, 0x28029232004400, 0x4008888002121000, 0x8041220041004118,
0x8018241008404200, 0x108021820348e, 0x108c0104100040, 0x8320080800460a00,
0x10008200802200, 0x284208080980800, 0x42020402122080, 0x9702004040410400,
0x1092070012122, 0x2214024619001121, 0x491420140208d000, 0x7460009414003802,
0x4022084104000040, 0x1084248085020200, 0x8808102400602480, 0x120410a400428508,
0x4014808050804, 0x408406804101008, 0x41081044d500031, 0x421080284044020,
0x4060188813041110, 0x40202041024080, 0xa0212002108100a4, 0x3020202020480,
0x1a802808062882, 0x80110101012100, 0xa40100200420890, 0x48025100208801,
0x4000000004208200, 0x804a10011602, 0x200a24c410041500, 0x8408080088061020,
];
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;
n1 | (n2 << (16)) | (n3 << (32)) | (n4 << (48))
}
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);
println!("0x{:x},", rook_magic_number);
unsafe {
ROOK_MAGIC_INIT[idx] = rook_magic_number;
}
}
println!("\n\n");
for idx in 0..64 {
let bishop_magic_number =
find_bishop_magic_numbers(BISHOP_RELEVANT_BITS[idx], 1_u64 << (idx), &mut state);
println!("0x{:x},", bishop_magic_number);
unsafe {
BISHOP_MAGIC_INIT[idx] = bishop_magic_number;
}
}
}
#[cfg(test)]
mod tests {
use super::init_magic_arrays;
use crate::magic::{BISHOP_MAGIC, BISHOP_MAGIC_INIT, ROOK_MAGIC, ROOK_MAGIC_INIT};
#[test]
fn test_init_magic_arrays() -> Result<(), String> {
init_magic_arrays();
for idx in 0..64 {
unsafe {
assert_eq!(ROOK_MAGIC[idx], ROOK_MAGIC_INIT[idx]);
assert_eq!(BISHOP_MAGIC[idx], BISHOP_MAGIC_INIT[idx]);
}
}
Ok(())
}
}

View File

@@ -1,6 +1,8 @@
pub mod attack;
pub mod board;
pub mod fen;
pub mod game;
pub mod magic;
pub mod r#move;
use game::Game;