#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct PlayerStones(u64);
impl PlayerStones {
pub fn new() -> PlayerStones {
PlayerStones(0)
}
pub fn is_empty(self, row: u8, column: u8) -> bool {
(cell(row, column) & self.0) == 0
}
#[cfg(test)]
pub fn place_stone(&mut self, row: u8, column: u8) {
self.0 |= cell(row, column)
}
pub fn is_win(self) -> bool {
let y = self.0 & (self.0 >> 6);
if (y & (y >> (2 * 6))) != 0 {
return true;
}
let y = self.0 & (self.0 >> 7);
if (y & (y >> (2 * 7))) != 0 {
return true;
}
let y = self.0 & (self.0 >> 8);
if (y & (y >> (2 * 8))) != 0 {
return true;
}
let y = self.0 & (self.0 >> 1);
if (y & (y >> 2)) != 0 {
return true;
}
false
}
pub fn flip(&mut self, mask: AllStones) {
self.0 ^= mask.0
}
pub fn key(self, mask: AllStones) -> u64 {
self.0 + mask.0
}
pub fn winning_positions(self) -> u64 {
let mut winning = (self.0 << 1) & (self.0 << 2) & (self.0 << 3);
let add_left_right_gaps = |shift| {
let mut w = 0u64;
let two_to_the_left = self.0 << shift & self.0 << (2 * shift);
w |= two_to_the_left & self.0 << (3 * shift);
w |= two_to_the_left & self.0 >> shift;
let two_to_the_right = self.0 >> shift & self.0 >> (2 * shift);
w |= two_to_the_right & self.0 << shift;
w |= two_to_the_right & self.0 >> (3 * shift);
w
};
winning |= add_left_right_gaps(6 + 1);
winning |= add_left_right_gaps(6 + 1 + 1);
winning |= add_left_right_gaps(6 + 1 - 1);
winning & FULL
}
}
const fn cell(row: u8, column: u8) -> u64 {
1u64 << (7 * column + row)
}
#[derive(Clone, Copy, PartialEq, Eq, Default, Hash)]
pub struct AllStones(u64);
impl AllStones {
#[allow(clippy::unusual_byte_groupings)] const BOTTOM: u64 = 0b0000001_0000001_0000001_0000001_0000001_0000001_0000001u64;
pub fn is_full(self, column: u8) -> bool {
!self.is_empty(5, column)
}
pub fn insert(&mut self, column: u8) {
self.0 |= self.0 + cell(0, column);
}
pub fn stones(self) -> u8 {
self.0.count_ones() as u8
}
pub fn is_empty(self, row: u8, column: u8) -> bool {
(cell(row, column) & self.0) == 0
}
pub fn possible(self) -> u64 {
(self.0 + Self::BOTTOM) & FULL
}
}
#[derive(Clone, Copy)]
pub struct NonLoosingMoves(u64);
impl NonLoosingMoves {
pub (crate) fn new(opponent: PlayerStones, both: AllStones) -> Self {
let openings = opponent.winning_positions();
let mut possible = both.possible();
let forced_moves = openings & possible;
if forced_moves != 0 {
if forced_moves & (forced_moves - 1) != 0 {
return Self(0);
}
possible = forced_moves;
};
possible &= !(openings >> 1);
Self(possible)
}
pub fn is_empty(self) -> bool {
self.0 == 0
}
pub fn contains(self, index: u8) -> bool {
self.0 & column(index) != 0
}
}
pub fn heuristic(player: PlayerStones, both: AllStones) -> u32 {
let openings = player.winning_positions();
let true_openings = openings & !(both.0);
true_openings.count_ones()
}
fn column(index: u8) -> u64 {
0b111111 << (index * (6 + 1))
}
#[allow(clippy::unusual_byte_groupings)] const FULL: u64 = 0b0111111_0111111_0111111_0111111_0111111_0111111_0111111u64;
#[cfg(test)]
mod test {
use super::PlayerStones;
#[test]
fn place_stones() {
let mut board = PlayerStones::new();
assert!(board.is_empty(0, 2));
board.place_stone(0, 2);
assert!(!board.is_empty(0, 2));
assert!(board.is_empty(3, 3));
board.place_stone(3, 3);
assert!(!board.is_empty(3, 3));
}
#[test]
fn horizontal() {
let mut board = PlayerStones::new();
board.place_stone(0, 1);
assert!(!board.is_win());
board.place_stone(0, 2);
assert!(!board.is_win());
board.place_stone(0, 3);
assert!(!board.is_win());
board.place_stone(0, 4);
assert!(board.is_win());
}
#[test]
fn vertical() {
let mut board = PlayerStones::new();
board.place_stone(1, 2);
assert!(!board.is_win());
board.place_stone(2, 2);
assert!(!board.is_win());
board.place_stone(3, 2);
assert!(!board.is_win());
board.place_stone(4, 2);
assert!(board.is_win());
}
#[test]
fn diagonal1() {
let mut board = PlayerStones::new();
board.place_stone(1, 1);
assert!(!board.is_win());
board.place_stone(2, 2);
assert!(!board.is_win());
board.place_stone(3, 3);
assert!(!board.is_win());
board.place_stone(4, 4);
assert!(board.is_win());
}
#[test]
fn diagonal2() {
let mut board = PlayerStones::new();
board.place_stone(1, 4);
assert!(!board.is_win());
board.place_stone(2, 3);
assert!(!board.is_win());
board.place_stone(3, 2);
assert!(!board.is_win());
board.place_stone(4, 1);
assert!(board.is_win());
}
}