diff options
author | eug-vs <eugene@eug-vs.xyz> | 2023-01-21 22:39:54 +0300 |
---|---|---|
committer | eug-vs <eugene@eug-vs.xyz> | 2023-01-21 22:47:14 +0300 |
commit | 4843b21d66ebee4350f1ffb59a9aebd351d16263 (patch) | |
tree | 6ff895a0a60f6561efcd96339ff68e801e94008a /src | |
parent | 8cf8f33ebc968d10bb949a82298a44ac4f6f176f (diff) | |
download | chessnost-4843b21d66ebee4350f1ffb59a9aebd351d16263.tar.gz |
feat: compute bishop attacks
Diffstat (limited to 'src')
-rw-r--r-- | src/attacks.rs | 92 |
1 files changed, 73 insertions, 19 deletions
diff --git a/src/attacks.rs b/src/attacks.rs index 787f200..4d826f6 100644 --- a/src/attacks.rs +++ b/src/attacks.rs @@ -1,4 +1,4 @@ -use crate::bitboard::{Bitboard, ls1b}; +use crate::bitboard::Bitboard; static NOT_A_FILE: Bitboard = 0xFEFEFEFEFEFEFEFE; static NOT_B_FILE: Bitboard = 0xFDFDFDFDFDFDFDFD; @@ -15,12 +15,13 @@ type AttackTable = [Bitboard; 64]; 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 +/// and the second index is the file of the piece on the first rank /// ``` /// rank_attacks = first_rank_attacks[occupancy][piece_file]; /// ``` type FirstRankAttacks = [[Byte; 8]; 256]; +#[allow(dead_code)] enum Direction { Nort, NoEa, @@ -92,6 +93,8 @@ impl Attacks { } + // Compute rook-like attacks on the first rank based on + // occupancy and rook file. fn precompute_first_rank_attacks() -> FirstRankAttacks { let mut attacks = [[0; 8]; 256]; @@ -101,21 +104,20 @@ impl Attacks { } 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 - }; + let mut left_bound = 0; + let mut right_bound = 7; + + for bit in 0..8 { + if (occupancy & (1 << bit)) > 0 { + if (bit > left_bound) && (bit < file) { + left_bound = bit + } + if (bit < right_bound) && (bit > file) { + right_bound = bit + } + } + } for index in left_bound..right_bound+1 { if index != file { @@ -208,6 +210,32 @@ impl Attacks { } [nort, noea, east, soea, sout, sowe, west, nowe] } + + /// https://www.chessprogramming.org/Kindergarten_Bitboards + /// + /// Given a square and occupancy masked for rank, diagonal or anti-diagonal (note: not a file!) + /// return an attack bitboard that considers blocking pieces + fn kingergarten_attacks_base(&self, occupancy: Bitboard, mask: Bitboard, square: u8) -> Bitboard { + let file = square % 8; + let masked_occupancy = occupancy & mask; + let occupancy_rank = ((masked_occupancy as u128 * B_FILE as u128) >> 58 & 0b111111) << 1; + let rank_attacks = self.first_rank_attacks[occupancy_rank as usize][file as usize] as Bitboard; + + let mut filled_up_attacks = 0; + for rank in 0..8 { + filled_up_attacks |= rank_attacks << (rank * 8); + } + filled_up_attacks & mask + } + + fn bishop(&self, occupancy: Bitboard, square: u8) -> Bitboard { + let diagonal_mask = self.ray_attacks[Direction::NoEa as usize][square as usize] | + self.ray_attacks[Direction::SoWe as usize][square as usize]; + let anti_diagonal_mask = self.ray_attacks[Direction::NoWe as usize][square as usize] | + self.ray_attacks[Direction::SoEa as usize][square as usize]; + + self.kingergarten_attacks_base(occupancy, diagonal_mask, square) | self.kingergarten_attacks_base(occupancy, anti_diagonal_mask, square) + } } #[cfg(test)] @@ -236,7 +264,7 @@ mod tests { assert_eq!(pop_count(attacks[Square::G1 as usize]), 3); assert_eq!(pop_count(attacks[Square::H8 as usize]), 2); - print(attacks[Square::E4 as usize]); + print(e4_attacks); } #[test] @@ -259,14 +287,18 @@ mod tests { #[test] fn test_first_rank_attacks() { let attacks = Attacks::precompute_first_rank_attacks(); - // HGFEDCBA HGFEDCBA - assert_eq!(attacks[0b00010000][4], 0b11101111, "If rook is the only one on the file, it should be able to attack all rank"); + // HGFEDCBA HGFEDCBA + assert_eq!(attacks[0b00010000][4], 0b11101111, "If rook is the only piece on a rank, it should be able to attack all rank"); + assert_eq!(attacks[0b00000000][4], 0b11101111, "Even with 0 occupancy rook should be able to attack all rank"); + assert_eq!(attacks[0b00000000][0], 0b11111110, "Even with 0 occupancy rook should be able to attack all rank"); assert_eq!(attacks[0b00010001][4], 0b11101111, "If only other piece is on A rank, rook should be able to attack all rank"); assert_eq!(attacks[0b10010000][4], 0b11101111, "If only other piece is on H rank, rook should be able to attack all rank"); assert_eq!(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[0b00010100][4], 0b11101100); assert_eq!(attacks[0b01010100][4], 0b01101100); assert_eq!(attacks[0b01010010][4], 0b01101110); + assert_eq!(attacks[0b00000010][4], 0b11101110); + assert_eq!(attacks[0b01011000][5], 0b01010000); } #[test] @@ -284,4 +316,26 @@ mod tests { attacks[7][square]; print(bitboard); } + + #[test] + fn test_diagonal_attacks() { + let attacks = Attacks::new(); + let square = Square::E4 as u8; + let occupancy = + 1 << Square::B7 as usize | + 1 << Square::B1 as usize | + 1 << Square::C2 as usize | + 1 << Square::F3 as usize; + let bb = attacks.bishop(occupancy, square); + + assert_ne!(bb & 1 << Square::C2 as u8, 0); + assert_eq!(bb & 1 << Square::B1 as u8, 0); + assert_ne!(bb & 1 << Square::F3 as u8, 0); + assert_eq!(bb & 1 << Square::G2 as u8, 0); + assert_ne!(bb & 1 << Square::H7 as u8, 0); + assert_ne!(bb & 1 << Square::B7 as u8, 0); + assert_eq!(bb & 1 << Square::A8 as u8, 0); + + print(bb); + } } |