aboutsummaryrefslogtreecommitdiff
path: root/src/attacks.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/attacks.rs')
-rw-r--r--src/attacks.rs236
1 files changed, 147 insertions, 89 deletions
diff --git a/src/attacks.rs b/src/attacks.rs
index f666bae..bec2b62 100644
--- a/src/attacks.rs
+++ b/src/attacks.rs
@@ -1,15 +1,15 @@
-use crate::{bitboard::Bitboard, square::Square, board::color::Color};
+use crate::{bitboard::Bitboard, board::color::Color, square::Square};
static NOT_A_FILE: Bitboard = 0xFEFEFEFEFEFEFEFE;
static NOT_B_FILE: Bitboard = 0xFDFDFDFDFDFDFDFD;
static NOT_G_FILE: Bitboard = 0xBFBFBFBFBFBFBFBF;
static NOT_H_FILE: Bitboard = 0x7F7F7F7F7F7F7F7F;
-static B_FILE: Bitboard = 0x0202020202020202;
-static H_FILE: Bitboard = 0x8080808080808080;
+static B_FILE: Bitboard = 0x0202020202020202;
+static H_FILE: Bitboard = 0x8080808080808080;
static DIAG_C2_H7: Bitboard = 0x0080402010080400;
static DIAG_A1_H8: Bitboard = 0x8040201008040201;
-static RANK_2: Bitboard = 0x000000000000FF00;
-static RANK_7: Bitboard = 0x00FF000000000000;
+static RANK_2: Bitboard = 0x000000000000FF00;
+static RANK_7: Bitboard = 0x00FF000000000000;
/// An array where N-th item is an attack bitboard
/// of a piece on N-th square
@@ -80,8 +80,10 @@ impl Attacks {
let mut attacks = [[0; 64]; 2];
for index in 0..64 {
let square = 1u64 << index;
- attacks[Color::White as usize][index] = ((square & NOT_A_FILE) << 7) | ((square & NOT_H_FILE) << 9);
- attacks[Color::Black as usize][index] = ((square & NOT_A_FILE) >> 9) | ((square & NOT_H_FILE) >> 7);
+ attacks[Color::White as usize][index] =
+ ((square & NOT_A_FILE) << 7) | ((square & NOT_H_FILE) << 9);
+ attacks[Color::Black as usize][index] =
+ ((square & NOT_A_FILE) >> 9) | ((square & NOT_H_FILE) >> 7);
}
attacks
}
@@ -92,10 +94,10 @@ impl Attacks {
for index in 0..64 {
let square = 1u64 << index;
- pushes [Color::White as usize][index] = (square << 8);
+ pushes[Color::White as usize][index] = (square << 8);
double_pushes[Color::White as usize][index] = ((square & RANK_2) << 16);
- pushes [Color::Black as usize][index] = (square >> 8);
+ pushes[Color::Black as usize][index] = (square >> 8);
double_pushes[Color::Black as usize][index] = ((square & RANK_7) >> 16);
}
(pushes, double_pushes)
@@ -106,15 +108,14 @@ impl Attacks {
for (index, bitboard) in attacks.iter_mut().enumerate() {
let square_bb = Square::from(index as u8).to_bitboard();
- *bitboard =
- ((square_bb & NOT_A_FILE & NOT_B_FILE) << 6) |
- ((square_bb & NOT_G_FILE & NOT_H_FILE) << 10) |
- ((square_bb & NOT_A_FILE) << 15) |
- ((square_bb & NOT_H_FILE) << 17) |
- ((square_bb & NOT_G_FILE & NOT_H_FILE) >> 6) |
- ((square_bb & NOT_A_FILE & NOT_B_FILE) >> 10) |
- ((square_bb & NOT_H_FILE) >> 15) |
- ((square_bb & NOT_A_FILE) >> 17);
+ *bitboard = ((square_bb & NOT_A_FILE & NOT_B_FILE) << 6)
+ | ((square_bb & NOT_G_FILE & NOT_H_FILE) << 10)
+ | ((square_bb & NOT_A_FILE) << 15)
+ | ((square_bb & NOT_H_FILE) << 17)
+ | ((square_bb & NOT_G_FILE & NOT_H_FILE) >> 6)
+ | ((square_bb & NOT_A_FILE & NOT_B_FILE) >> 10)
+ | ((square_bb & NOT_H_FILE) >> 15)
+ | ((square_bb & NOT_A_FILE) >> 17);
}
attacks
}
@@ -124,20 +125,18 @@ impl Attacks {
for (index, bitboard) in attacks.iter_mut().enumerate() {
let square_bb = Square::from(index as u8).to_bitboard();
- *bitboard =
- ((square_bb & NOT_A_FILE) >> 1) |
- ((square_bb & NOT_A_FILE) << 7) |
- ((square_bb & NOT_A_FILE) >> 9) |
- ((square_bb & NOT_H_FILE) << 1) |
- ((square_bb & NOT_H_FILE) >> 7) |
- ((square_bb & NOT_H_FILE) << 9) |
- (square_bb << 8) |
- (square_bb >> 8);
+ *bitboard = ((square_bb & NOT_A_FILE) >> 1)
+ | ((square_bb & NOT_A_FILE) << 7)
+ | ((square_bb & NOT_A_FILE) >> 9)
+ | ((square_bb & NOT_H_FILE) << 1)
+ | ((square_bb & NOT_H_FILE) >> 7)
+ | ((square_bb & NOT_H_FILE) << 9)
+ | (square_bb << 8)
+ | (square_bb >> 8);
}
attacks
}
-
// Compute rook-like attacks on the first rank based on
// occupancy and rook file.
fn precompute_first_rank_attacks() -> FirstRankAttacks {
@@ -164,7 +163,7 @@ impl Attacks {
}
}
- for index in left_bound..right_bound+1 {
+ for index in left_bound..right_bound + 1 {
if index != file {
attacks[occupancy as usize][file as usize] |= 1 << index;
}
@@ -260,11 +259,17 @@ impl Attacks {
///
/// 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 kindergarten_attacks_base(&self, occupancy: Bitboard, mask: Bitboard, square: Square) -> Bitboard {
+ fn kindergarten_attacks_base(
+ &self,
+ occupancy: Bitboard,
+ mask: Bitboard,
+ square: Square,
+ ) -> Bitboard {
let file = square.file();
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 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 {
@@ -274,38 +279,43 @@ impl Attacks {
}
/// https://www.chessprogramming.org/Kindergarten_Bitboards
- fn kindergarten_attacks_file(&self, occupancy: Bitboard, mask: Bitboard, square: Square) -> Bitboard {
+ fn kindergarten_attacks_file(
+ &self,
+ occupancy: Bitboard,
+ mask: Bitboard,
+ square: Square,
+ ) -> Bitboard {
let file = square.file();
let rank = square.rank();
let masked_occupancy = (occupancy & mask) >> file; // Shift occupancy to A file
- let occupancy_rank = ((masked_occupancy as u128 * DIAG_C2_H7 as u128) >> 58 & 0b111111) << 1;
+ let occupancy_rank =
+ ((masked_occupancy as u128 * DIAG_C2_H7 as u128) >> 58 & 0b111111) << 1;
// Use reversed rank as index, since occupancy is reversed
- let rank_attacks = self.first_rank_attacks[occupancy_rank as usize][7 - rank as usize] as Bitboard;
+ let rank_attacks =
+ self.first_rank_attacks[occupancy_rank as usize][7 - rank as usize] as Bitboard;
((rank_attacks as u128 * DIAG_A1_H8 as u128) as Bitboard & H_FILE) >> (7 - file)
}
pub fn bishop(&self, occupancy: Bitboard, square: Square) -> 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.kindergarten_attacks_base(occupancy, diagonal_mask, square) | self.kindergarten_attacks_base(occupancy, anti_diagonal_mask, square)
+ 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.kindergarten_attacks_base(occupancy, diagonal_mask, square)
+ | self.kindergarten_attacks_base(occupancy, anti_diagonal_mask, square)
}
pub fn rook(&self, occupancy: Bitboard, square: Square) -> Bitboard {
- let vertical =
- self.ray_attacks[Direction::Nort as usize][square as usize] |
- self.ray_attacks[Direction::Sout as usize][square as usize];
- let horizontal =
- self.ray_attacks[Direction::West as usize][square as usize] |
- self.ray_attacks[Direction::East as usize][square as usize];
-
- self.kindergarten_attacks_file(occupancy, vertical, square) | self.kindergarten_attacks_base(occupancy, horizontal, square)
+ let vertical = self.ray_attacks[Direction::Nort as usize][square as usize]
+ | self.ray_attacks[Direction::Sout as usize][square as usize];
+ let horizontal = self.ray_attacks[Direction::West as usize][square as usize]
+ | self.ray_attacks[Direction::East as usize][square as usize];
+
+ self.kindergarten_attacks_file(occupancy, vertical, square)
+ | self.kindergarten_attacks_base(occupancy, horizontal, square)
}
pub fn queen(&self, occupancy: Bitboard, square: Square) -> Bitboard {
@@ -319,11 +329,10 @@ mod tests {
use super::*;
- static DEFAULT_OCCUPANCY: Bitboard =
- 1 << Square::B7 as usize |
- 1 << Square::B1 as usize |
- 1 << Square::C2 as usize |
- 1 << Square::F3 as usize;
+ static DEFAULT_OCCUPANCY: Bitboard = 1 << Square::B7 as usize
+ | 1 << Square::B1 as usize
+ | 1 << Square::C2 as usize
+ | 1 << Square::F3 as usize;
#[test]
fn pawn_attacks() {
@@ -333,27 +342,60 @@ mod tests {
white_attacks.print("Pawn e4");
- assert_eq!(white_attacks, Square::D5.to_bitboard() | Square::F5.to_bitboard());
-
- assert_eq!(attacks[Color::White as usize][Square::H4 as usize], Square::G5.to_bitboard());
- assert_eq!(attacks[Color::White as usize][Square::A4 as usize], Square::B5.to_bitboard());
-
- assert_eq!(attacks[Color::White as usize][Square::E8 as usize].pop_count(), 0);
- assert_eq!(attacks[Color::Black as usize][Square::E1 as usize].pop_count(), 0);
+ assert_eq!(
+ white_attacks,
+ Square::D5.to_bitboard() | Square::F5.to_bitboard()
+ );
+
+ assert_eq!(
+ attacks[Color::White as usize][Square::H4 as usize],
+ Square::G5.to_bitboard()
+ );
+ assert_eq!(
+ attacks[Color::White as usize][Square::A4 as usize],
+ Square::B5.to_bitboard()
+ );
+
+ assert_eq!(
+ attacks[Color::White as usize][Square::E8 as usize].pop_count(),
+ 0
+ );
+ assert_eq!(
+ attacks[Color::Black as usize][Square::E1 as usize].pop_count(),
+ 0
+ );
}
#[test]
fn pawn_pushes() {
let (pushes, double_pushes) = Attacks::precompute_pawn_pushes();
- assert_eq!(pushes[Color::White as usize][Square::E4 as usize], Square::E5.to_bitboard());
- assert_eq!(pushes[Color::White as usize][Square::A2 as usize], Square::A3.to_bitboard());
- assert_eq!(pushes[Color::Black as usize][Square::E4 as usize], Square::E3.to_bitboard());
- assert_eq!(pushes[Color::Black as usize][Square::H6 as usize], Square::H5.to_bitboard());
+ assert_eq!(
+ pushes[Color::White as usize][Square::E4 as usize],
+ Square::E5.to_bitboard()
+ );
+ assert_eq!(
+ pushes[Color::White as usize][Square::A2 as usize],
+ Square::A3.to_bitboard()
+ );
+ assert_eq!(
+ pushes[Color::Black as usize][Square::E4 as usize],
+ Square::E3.to_bitboard()
+ );
+ assert_eq!(
+ pushes[Color::Black as usize][Square::H6 as usize],
+ Square::H5.to_bitboard()
+ );
assert_eq!(double_pushes[Color::White as usize][Square::E4 as usize], 0);
- assert_eq!(double_pushes[Color::White as usize][Square::A2 as usize], Square::A4.to_bitboard());
+ assert_eq!(
+ double_pushes[Color::White as usize][Square::A2 as usize],
+ Square::A4.to_bitboard()
+ );
assert_eq!(double_pushes[Color::Black as usize][Square::E4 as usize], 0);
- assert_eq!(double_pushes[Color::Black as usize][Square::H7 as usize], Square::H5.to_bitboard());
+ assert_eq!(
+ double_pushes[Color::Black as usize][Square::H7 as usize],
+ Square::H5.to_bitboard()
+ );
}
#[test]
@@ -403,12 +445,30 @@ mod tests {
fn first_rank_attacks() {
let attacks = Attacks::precompute_first_rank_attacks();
// 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[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);
@@ -420,15 +480,14 @@ mod tests {
fn ray_attacks() {
let attacks = Attacks::precompute_ray_attacks();
let square = Square::E4 as usize;
- let bitboard =
- attacks[0][square] |
- attacks[1][square] |
- attacks[2][square] |
- attacks[3][square] |
- attacks[4][square] |
- attacks[5][square] |
- attacks[6][square] |
- attacks[7][square];
+ let bitboard = attacks[0][square]
+ | attacks[1][square]
+ | attacks[2][square]
+ | attacks[3][square]
+ | attacks[4][square]
+ | attacks[5][square]
+ | attacks[6][square]
+ | attacks[7][square];
bitboard.print("Rays from e4");
}
@@ -454,12 +513,11 @@ mod tests {
fn rook_attacks() {
let attacks = Attacks::new();
let square = Square::E4;
- let occupancy =
- Square::B7.to_bitboard() |
- Square::B1.to_bitboard() |
- Square::C2.to_bitboard() |
- Square::E3.to_bitboard() |
- Square::F3.to_bitboard();
+ let occupancy = Square::B7.to_bitboard()
+ | Square::B1.to_bitboard()
+ | Square::C2.to_bitboard()
+ | Square::E3.to_bitboard()
+ | Square::F3.to_bitboard();
let bb = attacks.rook(occupancy, square);
occupancy.print("Occupancy");