Add piece attack tables and magic bitboards
This commit is contained in:
68
Cargo.lock
generated
68
Cargo.lock
generated
@@ -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"
|
||||
|
||||
10
Cargo.toml
10
Cargo.toml
@@ -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
505
src/attack.rs
Normal 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
187
src/magic.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user