#[cfg(not(feature = "advanced_threading"))] use std::thread; #[cfg(feature = "advanced_threading")] use rayon::prelude::*; #[cfg(feature = "advanced_hashing")] type Board = rustc_hash::FxHashSet; #[cfg(not(feature = "advanced_hashing"))] type Board = std::collections::HashSet; pub type Coord = (isize, isize); /// An error in the game's logic #[derive(Debug)] pub enum GameError { NoPreviousTurn } /// A representation of the game's state. #[derive(Default)] pub struct Game { primary_board: Board, initial_state: Board, count: usize, #[cfg(not(feature = "advanced_threading"))] parallelism: usize, } impl Game { /// Constructs a new board with all dead cells pub fn new() -> Self { Self { primary_board: Board::default(), initial_state: Board::default(), count: 0, #[cfg(not(feature = "advanced_threading"))] parallelism: std::thread::available_parallelism().unwrap().into(), } } /// Returns whether the cell at the given coordinate is alive pub fn get_state(&self, coord: Coord) -> bool { self.primary_board.contains(&coord) } /// Gets all live cells on the board pub fn cells(&self) -> Vec { self.primary_board.iter().cloned().collect() } /// Flips the cell at the given coordinate betweeen alive and dead. Will return whether the new /// cell is alive pub fn flip_state(&mut self, coord: Coord) -> bool { self.count = 0; if self.primary_board.contains(&coord) { self.primary_board.remove(&coord); self.initial_state = self.primary_board.clone(); false } else { self.primary_board.insert(coord); self.initial_state = self.primary_board.clone(); true } } /// Advances the board by one turn pub fn advance_board(&mut self) { // Get all the cells to check let mut check_set = Board::default(); for (x, y) in &self.primary_board { let x = x.clone(); let y = y.clone(); check_set.insert((x, y)); check_set.insert((x, y+1)); check_set.insert((x, y-1)); check_set.insert((x+1, y)); check_set.insert((x+1, y+1)); check_set.insert((x+1, y-1)); check_set.insert((x-1, y)); check_set.insert((x-1, y+1)); check_set.insert((x-1, y-1)); } // Iterate through the cells and modify the other HashSet //let old_board = std::mem::take(&mut self.secondary_board); let mut new_board = Board::default(); #[cfg(not(feature = "advanced_threading"))] { let binding = check_set.iter().collect::>(); let chunks = binding.chunks((binding.len()/self.parallelism)+1); thread::scope(|s| { let mut handles = vec![]; for chunk in chunks { let handle = s.spawn(|| { let owned_chunk = chunk.iter().map(|x| x.to_owned().to_owned()); let mut return_set = Board::default(); for coord in owned_chunk { if get_next_state(&self.primary_board, coord) { return_set.insert(coord); } } return_set }); handles.push(handle); } for handle in handles { new_board.extend(handle.join().unwrap()); } }); } #[cfg(feature = "advanced_threading")] { new_board.par_extend(check_set.par_iter().filter(|coord| get_next_state(&self.primary_board, coord.to_owned().to_owned()))); } // Swap the HashSets self.primary_board = new_board; self.count += 1; } /// Returns a vector of the values between the given top-left and bottom-right /// coordinate pub fn get_view(&self, coord1: Coord, coord2: Coord) -> Vec<&Coord>{ self.primary_board.iter() .filter(|(x, y)| x >= &coord1.0 && y >= &coord1.1 && x <= &coord2.0 && y <= &coord2.1) .collect() } /// Sets the board to its previous state. The board's history will only go back to the last /// manual intervention done by a player (such as loading a file or toggling a cell). Returns /// true if it reverted, and false if there was no previous revert. pub fn revert_board(&mut self) -> bool { // Check if there have been any turns since the initial state if self.count == 0 { return false; } self.primary_board = self.initial_state.clone(); // Check if we can simply swap out the old board. /*if self.easy_buffer_revert { std::mem::swap(&mut self.primary_board, &mut self.secondary_board); self.easy_buffer_revert = false; return; }*/ let count = self.count; for _ in 0..count-1 { self.advance_board(); } // let set = self.into_iter().nth(count).unwrap(); //self.primary_board = set; self.count = count - 1; true } } impl Iterator for Game { type Item = Board; fn next(&mut self) -> Option { self.advance_board(); Some(self.primary_board.clone()) } } /// Given a board and a coordinate, return whether the cell at that coordinate should be live next /// round fn get_next_state(board: &Board, coord: Coord) -> bool { let current_state = board.contains(&coord); let (x, y) = coord; let mut count: u8 = 0; for coord in [(x, y+1), (x, y-1), (x+1, y), (x+1, y+1), (x+1, y-1), (x-1, y), (x-1, y+1), (x-1, y-1)] { if board.contains(&coord) { count += 1; } } if current_state { (2 <= count) && (count <= 3) } else { count == 3 } }