From 59944e1e9ee8aaf4af0400279228f46e5762056c Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Wed, 12 Feb 2025 01:02:51 -0500 Subject: [PATCH] initial bit board storage --- src/bitboard.rs | 44 ++++++++++++++++++++++++ src/board.rs | 83 +++++++++++++++++++++++++-------------------- src/complexagent.rs | 2 +- src/main.rs | 5 +-- 4 files changed, 94 insertions(+), 40 deletions(-) create mode 100644 src/bitboard.rs diff --git a/src/bitboard.rs b/src/bitboard.rs new file mode 100644 index 0000000..afd0d32 --- /dev/null +++ b/src/bitboard.rs @@ -0,0 +1,44 @@ +use crate::board::BOARD_SIZE; + +/// 8x8 +/// TODO! look into variable length bit arrays in rust +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct BitBoard(u64); + +impl BitBoard { + pub const fn new() -> Self { + BitBoard(0) + } + + pub const fn get(&self, row: usize, col: usize) -> bool { + ((self.0 >> (row * BOARD_SIZE + col)) & 1) != 0 + } + + pub const fn set(&mut self, row: usize, col: usize, value: bool) { + let index = row * BOARD_SIZE + col; + if value { + self.0 |= 1 << index; // Set the bit at (row, col) to 1 + } else { + self.0 &= !(1 << index); // Clear the bit at (row, col) + } + } + + pub const fn count(&self) -> u32 { + self.0.count_ones() + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn set_and_get() { + let mut b = BitBoard::new(); + assert!(!b.get(2, 4)); + b.set(2, 4, true); + assert!(b.get(2, 4)); + b.set(2, 4, false); + assert!(!b.get(2, 4)); + } +} diff --git a/src/board.rs b/src/board.rs index 6912ec0..1751ab9 100644 --- a/src/board.rs +++ b/src/board.rs @@ -1,10 +1,11 @@ use crate::{ + bitboard::BitBoard, misc::{diag_raw, split_from}, piece::Piece, }; use arrayvec::ArrayVec; use lazy_static::lazy_static; -use std::{cmp::Ordering, fmt, mem::MaybeUninit}; +use std::{cmp::Ordering, fmt}; pub const BOARD_SIZE: usize = 8; @@ -60,7 +61,8 @@ lazy_static! { #[derive(Copy, Clone, PartialEq, Eq)] pub struct Board { - board: [[Option; BOARD_SIZE]; BOARD_SIZE], + white_board: BitBoard, + black_board: BitBoard, } impl fmt::Display for Board { @@ -108,7 +110,8 @@ impl fmt::Display for Board { impl Board { pub const fn new() -> Self { Self { - board: [[None; BOARD_SIZE]; BOARD_SIZE], + white_board: BitBoard::new(), + black_board: BitBoard::new(), } } @@ -128,20 +131,31 @@ impl Board { Self::all_positions().filter(move |(i, j)| self.would_prop(*i, *j, color)) } - /// Returns a mutable reference to a place on the [`Board`] - /// at (i, j) - pub const fn get_mut(&mut self, i: usize, j: usize) -> &mut Option { - &mut self.board[i][j] - } - /// Returns a reference to a place on the [`Board`] /// at (i, j) - pub const fn get(&self, i: usize, j: usize) -> &Option { - &self.board[i][j] + pub const fn get(&self, i: usize, j: usize) -> Option { + let white = self.white_board.get(i, j); + let black = self.black_board.get(i, j); + if white { + Some(Piece::White) + } else if black { + Some(Piece::Black) + } else { + None + } } const fn place_unchecked(&mut self, i: usize, j: usize, piece: Piece) { - *self.get_mut(i, j) = Some(piece); + match piece { + Piece::Black => { + self.black_board.set(i, j, true); + self.white_board.set(i, j, false); + } + Piece::White => { + self.black_board.set(i, j, false); + self.white_board.set(i, j, true); + } + } } pub fn what_if(&self, i: usize, j: usize, piece: Piece) -> Option<(Self, usize)> { @@ -172,7 +186,7 @@ impl Board { } fn propegate_from(&mut self, i: usize, j: usize) -> usize { - let Some(starting_color) = *self.get(i, j) else { + let Some(starting_color) = self.get(i, j) else { return 0; }; @@ -202,7 +216,7 @@ impl Board { break; }; - if piece == &starting_color { + if piece == starting_color { // get history of this chain if let Some(history) = chain.get(..chain_length) { // fill all opposite colors with this color @@ -220,15 +234,10 @@ impl Board { /// Returns (White score, Black score) pub fn get_score(&self) -> (usize, usize) { - self.board - .iter() - .flatten() - .flatten() - .map(|cell| match cell { - Piece::White => (1, 0), - Piece::Black => (0, 1), - }) - .fold((0_usize, 0usize), |(a, b), (c, d)| (a + c, b + d)) + ( + self.white_board.count() as usize, + self.black_board.count() as usize, + ) } pub fn game_winner(&self, turn: Piece) -> Option { @@ -258,9 +267,9 @@ mod test { #[test] fn place_and_get() { let mut board = Board::new(); - assert_eq!(board.get(0, 0), &None); + assert_eq!(board.get(0, 0), None); board.place_unchecked(0, 0, Piece::Black); - assert_eq!(board.get(0, 0), &Some(Piece::Black)); + assert_eq!(board.get(0, 0), Some(Piece::Black)); } #[test] @@ -271,7 +280,7 @@ mod test { board.place_unchecked(0, 2, Piece::Black); board.propegate_from(0, 2); - assert_eq!(board.get(0, 1), &Some(Piece::Black)); + assert_eq!(board.get(0, 1), Some(Piece::Black)); } #[test] @@ -287,7 +296,7 @@ mod test { assert_eq!( board.get(0, 1), - &None, + None, "(0, 1) was overridden even though it's an empty space" ); } @@ -308,7 +317,7 @@ mod test { for j in 2..=6 { assert_eq!( board.get(0, j), - &Some(Piece::Black), + Some(Piece::Black), "should be black at: ({}, {})", 0, j @@ -332,7 +341,7 @@ mod test { for i in 2..=6 { assert_eq!( board.get(i, 0), - &Some(Piece::Black), + Some(Piece::Black), "should be black at: ({}, {})", i, 0 @@ -365,7 +374,7 @@ mod test { board.propegate_from(0, 0); // Capture white piece at (1,1) - assert_eq!(board.get(1, 1), &Some(Piece::Black), "\n{}", board); + assert_eq!(board.get(1, 1), Some(Piece::Black), "\n{}", board); } // Test corner capture from top-right corner @@ -381,7 +390,7 @@ mod test { board.propegate_from(2, 5); // Capture white piece at (1, 6) - assert_eq!(board.get(1, 6), &Some(Piece::Black), "\n{}", board); + assert_eq!(board.get(1, 6), Some(Piece::Black), "\n{}", board); } // Test corner capture from bottom-left corner @@ -397,7 +406,7 @@ mod test { board.propegate_from(5, 2); // Capture white piece at (6, 1) - assert_eq!(board.get(6, 1), &Some(Piece::Black), "\n{}", board); + assert_eq!(board.get(6, 1), Some(Piece::Black), "\n{}", board); } // Test corner capture from bottom-right corner @@ -413,7 +422,7 @@ mod test { board.propegate_from(5, 5); // Capture white piece at (6, 6) - assert_eq!(board.get(6, 6), &Some(Piece::Black), "\n{}", board); + assert_eq!(board.get(6, 6), Some(Piece::Black), "\n{}", board); } // Test capture from top-left corner (horizontal) @@ -428,7 +437,7 @@ mod test { board.propegate_from(0, 2); - assert_eq!(board.get(0, 1), &Some(Piece::Black), "\n{}", board); + assert_eq!(board.get(0, 1), Some(Piece::Black), "\n{}", board); } // Test capture from top-right corner (horizontal) @@ -443,7 +452,7 @@ mod test { board.propegate_from(0, 5); - assert_eq!(board.get(0, 6), &Some(Piece::Black), "\n{}", board); + assert_eq!(board.get(0, 6), Some(Piece::Black), "\n{}", board); } // Test capture from top-left corner (vertical) @@ -458,7 +467,7 @@ mod test { board.propegate_from(2, 0); - assert_eq!(board.get(1, 0), &Some(Piece::Black), "\n{}", board); + assert_eq!(board.get(1, 0), Some(Piece::Black), "\n{}", board); } // Test capture from bottom-left corner (vertical) @@ -473,6 +482,6 @@ mod test { board.propegate_from(5, 0); - assert_eq!(board.get(6, 0), &Some(Piece::Black), "\n{}", board); + assert_eq!(board.get(6, 0), Some(Piece::Black), "\n{}", board); } } diff --git a/src/complexagent.rs b/src/complexagent.rs index 2e3ad0b..119fb0b 100644 --- a/src/complexagent.rs +++ b/src/complexagent.rs @@ -253,7 +253,7 @@ pub struct ComplexAgent { impl ComplexAgent { pub const fn new(color: Piece) -> Self { - const MAX_DEPTH: usize = 8; + const MAX_DEPTH: usize = 7; Self { color, future_moves: FutureMoves::new(color, MAX_DEPTH), diff --git a/src/main.rs b/src/main.rs index 5c9bb14..37db162 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use game::Game; use piece::Piece; mod agent; +mod bitboard; mod board; mod complexagent; mod game; @@ -11,8 +12,8 @@ mod piece; fn main() { let player1 = complexagent::ComplexAgent::new(Piece::Black); // let player2 = complexagent::ComplexAgent::new(Piece::White); - let player2 = agent::ManualAgent::new(Piece::White); - // let player2 = agent::RandomAgent::new(Piece::White); + // let player2 = agent::ManualAgent::new(Piece::White); + let player2 = agent::RandomAgent::new(Piece::White); let mut game = Game::new(Box::new(player1), Box::new(player2)); game.game_loop(); }