#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum Piece { Black, White, } const BOARD_SIZE: usize = 8; #[derive(Copy, Clone)] pub struct Board { board: [[Option; BOARD_SIZE]; BOARD_SIZE], } impl Board { pub const fn new() -> Self { Self { board: [[None; BOARD_SIZE]; BOARD_SIZE], } } /// 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] } fn place_unchecked(&mut self, i: usize, j: usize, piece: Piece) { *self.get_mut(i, j) = Some(piece); } // TODO! make this better and actually readable // TODO! propegate from propegated pieces fn propegate_from(&mut self, i: usize, j: usize) { // returns if that place is empty let Some(starting_color) = *self.get(i, j) else { return; }; // both ranges expand __from__ the piece let i_ranges: Vec> = vec![(0..i).rev().collect(), ((i + 1)..BOARD_SIZE).collect()]; let j_ranges: Vec> = vec![(0..j).rev().collect(), ((j + 1)..BOARD_SIZE).collect()]; for i_range in i_ranges { let mut chain_length: usize = 0; for new_i in i_range.clone() { match self.get(new_i, j) { Some(piece) => { if piece == &starting_color { if chain_length > 0 { // fill all opposite colors with this color i_range.get(..chain_length).unwrap().iter().for_each(|i_o| { self.place_and_prop_unchecked(*i_o, j, starting_color) }); } break; } else { chain_length += 1; } } None => break, } } } for j_range in j_ranges { let mut chain_length: usize = 0; for new_j in j_range.clone() { match self.get(i, new_j) { Some(piece) => { if piece == &starting_color { if chain_length > 0 { // fill all opposite colors with this color j_range.get(..chain_length).unwrap().iter().for_each(|j_o| { self.place_and_prop_unchecked(i, *j_o, starting_color) }); } break; } else { chain_length += 1; } } None => break, } } } } fn place_and_prop_unchecked(&mut self, i: usize, j: usize, piece: Piece) { self.place_unchecked(i, j, piece); self.propegate_from(i, j); } /// Place a piece on the [`Board`] /// Returns an error if there already exists a piece there pub fn place(&mut self, i: usize, j: usize, piece: Piece) -> Result<(), String> { if self.get(i, j).is_some() { return Err(format!("Cannot place on ({},{}), piece exists", i, j)); } else { self.place_and_prop_unchecked(i, j, piece); } Ok(()) } } #[cfg(test)] mod test { use super::*; #[test] fn place_and_get() { let mut board = Board::new(); assert_eq!(board.get(0, 0), &None); assert_eq!( board.place(0, 0, Piece::Black), Ok(()), "placing black on (0, 0)" ); assert_eq!(board.get(0, 0), &Some(Piece::Black)); } #[test] fn place_and_capture_simple() { let mut board = Board::new(); assert_eq!( board.place(0, 0, Piece::Black), Ok(()), "first move of piece black to (0, 0)" ); assert_eq!( board.place(0, 1, Piece::White), Ok(()), "white move to (0, 1)" ); assert_eq!( board.place(0, 2, Piece::Black), Ok(()), "black counter, capturing white" ); assert_eq!(board.get(0, 1), &Some(Piece::Black)); } #[test] fn failed_capture() { let mut board = Board::new(); assert_eq!(board.place(0, 0, Piece::Black), Ok(())); assert_eq!(board.place(0, 2, Piece::White), Ok(())); assert_eq!(board.place(0, 3, Piece::Black), Ok(())); assert_eq!( board.get(0, 1), &None, "(0, 1) was overridden even though it's an empty space" ); } #[test] fn long_capture() { let mut board = Board::new(); assert_eq!(board.place(0, 0, Piece::Black), Ok(())); for j in 1..=6 { assert_eq!(board.place(0, j, Piece::White), Ok(())); } assert_eq!(board.place(0, 7, Piece::Black), Ok(())); for j in 2..=6 { assert_eq!( board.get(0, j), &Some(Piece::Black), "should be black at: ({}, {})", 0, j ); } } #[test] fn long_capture_vert() { let mut board = Board::new(); assert_eq!(board.place(0, 0, Piece::Black), Ok(())); for i in 1..=6 { assert_eq!(board.place(i, 0, Piece::White), Ok(())); } assert_eq!(board.place(7, 0, Piece::Black), Ok(())); for i in 2..=6 { assert_eq!( board.get(i, 0), &Some(Piece::Black), "should be black at: ({}, {})", i, 0 ); } } }