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::precompute_first_rank_attacks(&mut first_rank_attacks); Self { knight, king, first_rank_attacks } } fn precompute_knight_attacks(attacks: &mut AttackTable) { for index in 0..64 { let position = 1u64 << index; attacks[index] = ((position & NOT_A_FILE & NOT_B_FILE) << 6) | ((position & NOT_G_FILE & NOT_H_FILE) << 10) | ((position & NOT_A_FILE) << 15) | ((position & NOT_H_FILE) << 17) | ((position & NOT_G_FILE & NOT_H_FILE) >> 6) | ((position & NOT_A_FILE & NOT_B_FILE) >> 10) | ((position & NOT_H_FILE) >> 15) | ((position & NOT_A_FILE) >> 17); } } fn precompute_king_attacks(attacks: &mut AttackTable) { for index in 0..64 { let position = 1u64 << index; attacks[index] = ((position & NOT_A_FILE) >> 1) | ((position & NOT_A_FILE) << 7) | ((position & NOT_A_FILE) >> 9) | ((position & NOT_H_FILE) << 1) | ((position & NOT_H_FILE) >> 7) | ((position & NOT_H_FILE) << 9) | (position << 8) | (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)] mod tests { use crate::{bitboard::pop_count, board::Square}; use super::*; #[test] fn test_knight_attacks() { let attacks = Attacks::new(); let e4_attacks = attacks.knight[Square::E4 as usize]; assert_ne!(e4_attacks & 1 << Square::G5 as usize, 0); assert_ne!(e4_attacks & 1 << Square::G3 as usize, 0); assert_ne!(e4_attacks & 1 << Square::C5 as usize, 0); assert_ne!(e4_attacks & 1 << Square::C3 as usize, 0); assert_ne!(e4_attacks & 1 << Square::D2 as usize, 0); assert_ne!(e4_attacks & 1 << Square::F2 as usize, 0); assert_ne!(e4_attacks & 1 << Square::D6 as usize, 0); assert_ne!(e4_attacks & 1 << Square::F6 as usize, 0); assert_eq!(e4_attacks & 1 << Square::E5 as usize, 0); assert_eq!(e4_attacks & 1 << Square::D4 as usize, 0); assert_eq!(e4_attacks & 1 << Square::A1 as usize, 0); assert_eq!(pop_count(attacks.knight[Square::G1 as usize]), 3); assert_eq!(pop_count(attacks.knight[Square::H8 as usize]), 2); } #[test] fn test_king_attacks() { let attacks = Attacks::new(); assert_eq!(pop_count(attacks.king[Square::E4 as usize]), 8); assert_eq!(pop_count(attacks.king[Square::A1 as usize]), 3); assert_eq!(pop_count(attacks.king[Square::A8 as usize]), 3); assert_eq!(pop_count(attacks.king[Square::H1 as usize]), 3); assert_eq!(pop_count(attacks.king[Square::H8 as usize]), 3); assert_eq!(pop_count(attacks.king[Square::E1 as usize]), 5); assert_eq!(pop_count(attacks.king[Square::E8 as usize]), 5); 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); } }