From 74973416aa6a7b0fae9ed2b9911fa18fe87c4e1c Mon Sep 17 00:00:00 2001 From: eug-vs Date: Sat, 21 Jan 2023 18:24:52 +0300 Subject: feat: precompute first rank attacks --- src/attacks.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/bitboard.rs | 3 +++ 2 files changed, 68 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/attacks.rs b/src/attacks.rs index ece87b6..141ba44 100644 --- a/src/attacks.rs +++ b/src/attacks.rs @@ -1,25 +1,42 @@ -use crate::bitboard::Bitboard; +use crate::bitboard::{Bitboard, ls1b, pop_count}; static NOT_A_FILE: Bitboard = 0xFEFEFEFEFEFEFEFE; static NOT_B_FILE: Bitboard = 0xFDFDFDFDFDFDFDFD; static NOT_G_FILE: Bitboard = 0xBFBFBFBFBFBFBFBF; static NOT_H_FILE: Bitboard = 0x7F7F7F7F7F7F7F7F; +/// An array where N-th item is an attack bitboard +/// of a piece on N-th square type AttackTable = [Bitboard; 64]; +/// One byte stores exactly 256 states which is every +/// possible state of the occupancy on a rank +pub type Byte = u8; + +/// First index is *occupancy* of the first rank (all possible states) +/// and the second index is position of the piece on the first rank +/// ``` +/// rank_attacks = first_rank_attacks[occupancy][piece_file]; +/// ``` +type FirstRankAttacks = [[Byte; 8]; 256]; + + #[derive(Debug)] pub struct Attacks { pub knight: AttackTable, pub king: AttackTable, + pub first_rank_attacks: FirstRankAttacks, } impl Attacks { pub fn new() -> Self { let mut knight = [0; 64]; let mut king = [0; 64]; + let mut first_rank_attacks = [[0; 8]; 256]; Self::precompute_knight_attacks(&mut knight); Self::precompute_king_attacks(&mut king); - Self { knight, king } + Self::precompute_first_rank_attacks(&mut first_rank_attacks); + Self { knight, king, first_rank_attacks } } fn precompute_knight_attacks(attacks: &mut AttackTable) { @@ -51,6 +68,38 @@ impl Attacks { (position >> 8); } } + + fn precompute_first_rank_attacks(attacks: &mut FirstRankAttacks) { + /// Really slow implementation of Most Significant One Bit which is fine to use when pre-computing + fn log_base_2(bb: Bitboard) -> u64 { + (bb as f64).log2() as u64 + } + + for occupancy in 0..256 { + let right_most_piece = log_base_2(occupancy); + let left_most_piece = log_base_2(ls1b(occupancy)); + + for file in 0..8 { + let left_bound = if left_most_piece == file { + 0 + } else { + left_most_piece + }; + + let right_bound = if right_most_piece == file { + 7 + } else { + right_most_piece + }; + + for index in left_bound..right_bound+1 { + if index != file { + attacks[occupancy as usize][file as usize] |= 1 << index; + } + } + } + } + } } #[cfg(test)] @@ -96,4 +145,18 @@ mod tests { assert_eq!(pop_count(attacks.king[Square::A4 as usize]), 5); assert_eq!(pop_count(attacks.king[Square::H4 as usize]), 5); } + + #[test] + fn test_first_rank_attacks() { + let attacks = Attacks::new(); + // HGFEDCBA HGFEDCBA + assert_eq!(attacks.first_rank_attacks[0b00010000][4], 0b11101111, "If rook is the only one on the file, it should be able to attack all rank"); + assert_eq!(attacks.first_rank_attacks[0b00010001][4], 0b11101111, "If only other piece is on A rank, rook should be able to attack all rank"); + assert_eq!(attacks.first_rank_attacks[0b10010000][4], 0b11101111, "If only other piece is on H rank, rook should be able to attack all rank"); + assert_eq!(attacks.first_rank_attacks[0b10010001][4], 0b11101111, "If only other pieces are on A and H ranks, rook should be able to attack all rank"); + assert_eq!(attacks.first_rank_attacks[0b00010100][4], 0b11101100); + assert_eq!(attacks.first_rank_attacks[0b01010100][4], 0b01101100); + assert_eq!(attacks.first_rank_attacks[0b01010010][4], 0b01101110); + + } } diff --git a/src/bitboard.rs b/src/bitboard.rs index 6481863..646006c 100644 --- a/src/bitboard.rs +++ b/src/bitboard.rs @@ -24,6 +24,9 @@ pub fn pop_count(bb: Bitboard) -> u8 { /// Return Bitboard with only Least Single Bit pub fn ls1b(bb: Bitboard) -> u64 { + if bb == 0 { + return 0 + } bb & !(bb - 1) } -- cgit v1.2.3