use crate::{utils, NUM_SPACES};
use derive_more::{
BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, From, Into, Not,
};
use packed_simd::{u64x4, u64x8};
use std::fmt::{self, Display, Formatter};
#[derive(
Clone,
Copy,
Debug,
Eq,
PartialEq,
PartialOrd,
Ord,
Default,
From,
Into,
BitAnd,
BitAndAssign,
BitOr,
BitOrAssign,
BitXor,
BitXorAssign,
Not,
)]
pub struct Bitboard(u64);
pub const BLACK_START: Bitboard = Bitboard(0x0000000810000000);
pub const WHITE_START: Bitboard = Bitboard(0x0000001008000000);
impl Display for Bitboard {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
utils::format_grid(
self.into_iter().map(|bit| match bit {
false => '.',
true => '#',
}),
f,
)
}
}
impl Bitboard {
#[inline]
pub fn count_occupied(self) -> u8 {
self.0.count_ones() as u8
}
#[inline]
pub fn count_empty(self) -> u8 {
self.0.count_zeros() as u8
}
#[inline]
pub fn is_empty(self) -> bool {
self.0 == 0
}
}
#[inline]
pub fn score_absolute_difference(active: Bitboard, opponent: Bitboard) -> i8 {
(active.0.count_ones() as i8) - (opponent.0.count_ones() as i8)
}
#[inline]
pub fn score_winner_gets_empties(active: Bitboard, opponent: Bitboard) -> i8 {
let absolute_difference = score_absolute_difference(active, opponent);
if absolute_difference.is_positive() {
absolute_difference + ((active | opponent).0.count_zeros() as i8)
} else if absolute_difference.is_negative() {
absolute_difference - ((active | opponent).0.count_zeros() as i8)
} else {
0
}
}
#[inline]
pub fn get_move_mask(active: Bitboard, opponent: Bitboard) -> Bitboard {
const SHIFTS_1: u64x4 = u64x4::new(1, 8, 7, 9);
const SHIFTS_2: u64x4 = u64x4::new(2, 16, 14, 18);
const SHIFTS_4: u64x4 = u64x4::new(4, 32, 28, 36);
const EDGE_MASK: u64 = 0x7E7E7E7E7E7E7E7Eu64;
let opponent_edge_mask = EDGE_MASK & opponent.0;
let masks: u64x4 = u64x4::new(
opponent_edge_mask,
opponent.0,
opponent_edge_mask,
opponent_edge_mask,
);
let mut flip_l = u64x4::splat(active.0);
let mut flip_r = flip_l;
let mut masks_l = masks & (masks << SHIFTS_1);
let mut masks_r = masks & (masks >> SHIFTS_1);
flip_l |= masks & (flip_l << SHIFTS_1);
flip_l |= masks_l & (flip_l << SHIFTS_2);
masks_l &= masks_l << SHIFTS_2;
flip_l |= masks_l & (flip_l << SHIFTS_4);
flip_r |= masks & (flip_r >> SHIFTS_1);
flip_r |= masks_r & (flip_r >> SHIFTS_2);
masks_r &= masks_r >> SHIFTS_2;
flip_r |= masks_r & (flip_r >> SHIFTS_4);
let empties = !(active | opponent).0;
let captures_l = (flip_l & masks) << SHIFTS_1;
let captures_r = (flip_r & masks) >> SHIFTS_1;
Bitboard(empties & (captures_l | captures_r).or())
}
#[inline]
pub fn apply_move(
active: Bitboard,
opponent: Bitboard,
move_mask: Bitboard,
) -> (Bitboard, Bitboard) {
const NOT_A_FILE: u64 = 0xfefefefefefefefe;
const NOT_H_FILE: u64 = 0x7f7f7f7f7f7f7f7f;
const FULL_MASK: u64 = 0xffffffffffffffff;
const LEFT_MASKS: u64x8 = u64x8::new(
NOT_A_FILE, FULL_MASK, NOT_H_FILE, NOT_A_FILE, NOT_A_FILE, FULL_MASK, NOT_H_FILE,
NOT_A_FILE,
);
const RIGHT_MASKS: u64x8 = u64x8::new(
NOT_H_FILE, FULL_MASK, NOT_A_FILE, NOT_H_FILE, NOT_H_FILE, FULL_MASK, NOT_A_FILE,
NOT_H_FILE,
);
const SHIFTS_1: u64x8 = u64x8::new(1, 8, 7, 9, 1, 8, 7, 9);
const SHIFTS_2: u64x8 = u64x8::new(2, 16, 14, 18, 2, 16, 14, 18);
const SHIFTS_4: u64x8 = u64x8::new(4, 32, 28, 36, 4, 32, 28, 36);
let mut gen_left = u64x8::new(
move_mask.0,
move_mask.0,
move_mask.0,
move_mask.0,
active.0,
active.0,
active.0,
active.0,
);
let mut pro_left = u64x8::splat(opponent.0);
let mut gen_right = u64x8::new(
active.0,
active.0,
active.0,
active.0,
move_mask.0,
move_mask.0,
move_mask.0,
move_mask.0,
);
let mut pro_right = pro_left;
pro_left &= LEFT_MASKS;
gen_left |= pro_left & (gen_left << SHIFTS_1);
pro_left &= pro_left << SHIFTS_1;
gen_left |= pro_left & (gen_left << SHIFTS_2);
pro_left &= pro_left << SHIFTS_2;
gen_left |= pro_left & (gen_left << SHIFTS_4);
pro_right &= RIGHT_MASKS;
gen_right |= pro_right & (gen_right >> SHIFTS_1);
pro_right &= pro_right >> SHIFTS_1;
gen_right |= pro_right & (gen_right >> SHIFTS_2);
pro_right &= pro_right >> SHIFTS_2;
gen_right |= pro_right & (gen_right >> SHIFTS_4);
let flip_mask = (gen_left & gen_right).or();
let new_active = Bitboard((active.0 ^ flip_mask) | move_mask.0);
let new_opponent = Bitboard(opponent.0 ^ flip_mask);
(new_active, new_opponent)
}
#[derive(Clone, Copy, Debug)]
pub struct Bits {
remaining: usize,
bitboard: Bitboard,
}
impl Iterator for Bits {
type Item = bool;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let bitmask = Bitboard::from(1u64 << (self.remaining - 1));
let bit = !(self.bitboard & bitmask).is_empty();
self.remaining -= 1;
Some(bit)
}
}
impl ExactSizeIterator for Bits {
fn len(&self) -> usize {
self.remaining
}
}
impl IntoIterator for Bitboard {
type Item = bool;
type IntoIter = Bits;
fn into_iter(self) -> Self::IntoIter {
Bits {
remaining: NUM_SPACES,
bitboard: self,
}
}
}