use super::circular_index; use crate::game_of_life::opts::Opts; use crate::game_of_life::simulation::plane::Plane; use std::ops::Index; /// Game of Life universe struct for running simulations #[derive(Debug, Clone, PartialEq)] pub(crate) struct Universe { pub opts: Opts, pub current: Plane>, pub future: Plane>, } impl Index for Universe { type Output = Plane; fn index(&self, idx: isize) -> &Plane { let len = self.current.0.len() as isize; &self.current[circular_index(idx, len) as isize] } } #[cfg(test)] mod index { use super::*; #[test] fn get_element() { let opts = Opts { seed: 1337, input: None, width: 13, height: 13, live_cell: 'L', dead_cell: 'D', delay: 13, }; let u = Universe { opts, current: Plane(vec![ Plane(vec![false; 5]), Plane(vec![false, false, true, false, false]), Plane(vec![false; 5]), ]), future: Plane(vec![Plane(vec![])]), }; assert_eq!(u[-3][0], false); assert_eq!(u[-3][1], false); assert_eq!(u[-3][3], false); assert_eq!(u[-3][4], false); assert_eq!(u[0][0], false); assert_eq!(u[0][1], false); assert_eq!(u[0][3], false); assert_eq!(u[0][4], false); assert_eq!(u[2][0], false); assert_eq!(u[2][1], false); assert_eq!(u[2][3], false); assert_eq!(u[2][4], false); assert_eq!(u[5][0], false); assert_eq!(u[5][1], false); assert_eq!(u[5][3], false); assert_eq!(u[5][4], false); assert_eq!(u[-2][-3], true); assert_eq!(u[-2][2], true); assert_eq!(u[-2][7], true); assert_eq!(u[1][-3], true); assert_eq!(u[1][2], true); assert_eq!(u[1][7], true); assert_eq!(u[4][-3], true); assert_eq!(u[4][2], true); assert_eq!(u[4][7], true); } } impl Universe { /// Return the neighboring cells to the cell in index (Y,X) on a plane /// /// given a cell "C" in a universe of cells "." the immediate neighbors would be "x" as in /// the following different examples /// /// ..... | .xxx. | .xCx. | xCx.. | Cx..x | xx..x | /// .xxx. | .xCx. | .xxx. | xxx.. | xx..x | Cx..x | /// .xCx. | .xxx. | ..... | ..... | ..... | xx..x | /// .xxx. | ..... | ..... | ..... | ..... | ..... | /// ..... | ..... | .xxx. | xxx.. | xx..x | ..... | /// fn neighbors(&self, y: isize, x: isize) -> Vec { vec![ self[y - 1][x - 1], self[y - 1][x], self[y - 1][x + 1], self[y][x - 1], self[y][x + 1], self[y + 1][x - 1], self[y + 1][x], self[y + 1][x + 1], ] } /// Update the current state of the universe (according to the game of life laws) and /// set the current state to the next future generation pub(crate) fn evolve(mut self) -> Self { for row in 0..self.current.len() { for col in 0..self.current.0.len() as isize { let living_neighbors = self .neighbors(row, col) .iter() .filter(|&c| *c == true) .count(); match self[row][col] { true if living_neighbors == 2 || living_neighbors == 3 => { self.future[row][col] = true } false if living_neighbors == 3 => self.future[row][col] = true, _ => self.future[row][col] = false, } } } self.current = self.future.clone(); self } } #[cfg(test)] mod neighbors { use super::*; fn opts() -> Opts { Opts { seed: 1337, input: None, width: 13, height: 13, live_cell: 'L', dead_cell: 'D', delay: 13, } } #[test] fn center() { let u = Universe { opts: opts(), future: Plane(vec![Plane(vec![])]), current: Plane(vec![ Plane(vec![false, false, false, false, false]), Plane(vec![false, true, true, true, false]), Plane(vec![false, true, true, true, false]), Plane(vec![false, true, true, true, false]), Plane(vec![false, false, false, false, false]), ]), }; assert_eq!(u.neighbors(2, 2), vec![true; 8]); } #[test] fn top_edge() { let u = Universe { opts: opts(), future: Plane(vec![Plane(vec![])]), current: Plane(vec![ Plane(vec![false, true, true, true, false]), Plane(vec![false, true, true, true, false]), Plane(vec![false, false, false, false, false]), Plane(vec![false, false, false, false, false]), Plane(vec![false, true, true, true, false]), ]), }; assert_eq!(u.neighbors(0, 2), vec![true; 8]); } #[test] fn bottom_edge() { let u = Universe { opts: opts(), future: Plane(vec![Plane(vec![])]), current: Plane(vec![ Plane(vec![false, true, true, true, false]), Plane(vec![false, false, false, false, false]), Plane(vec![false, false, false, false, false]), Plane(vec![false, true, true, true, false]), Plane(vec![false, true, true, true, false]), ]), }; assert_eq!(u.neighbors(4, 2), vec![true; 8]); } #[test] fn left_edge() { let u = Universe { opts: opts(), future: Plane(vec![Plane(vec![])]), current: Plane(vec![ Plane(vec![false, false, false, false, false]), Plane(vec![true, true, false, false, true]), Plane(vec![true, true, false, false, true]), Plane(vec![true, true, false, false, true]), Plane(vec![false, false, false, false, false]), ]), }; assert_eq!(u.neighbors(2, 0), vec![true; 8]); } #[test] fn right_edge() { let u = Universe { opts: opts(), future: Plane(vec![Plane(vec![])]), current: Plane(vec![ Plane(vec![false, false, false, false, false]), Plane(vec![true, false, false, true, true]), Plane(vec![true, false, false, true, true]), Plane(vec![true, false, false, true, true]), Plane(vec![false, false, false, false, false]), ]), }; assert_eq!(u.neighbors(2, 4), vec![true; 8]); } #[test] fn bottom_corner_edge() { let u = Universe { opts: opts(), future: Plane(vec![Plane(vec![])]), current: Plane(vec![ Plane(vec![true, false, false, true, true]), Plane(vec![false, false, false, false, false]), Plane(vec![false, false, false, false, false]), Plane(vec![true, false, false, true, true]), Plane(vec![true, false, false, true, true]), ]), }; assert_eq!(u.neighbors(4, 4), vec![true; 8]); } #[test] fn top_corner_edge() { let u = Universe { opts: opts(), future: Plane(vec![Plane(vec![])]), current: Plane(vec![ Plane(vec![true, true, false, false, true]), Plane(vec![true, true, false, false, true]), Plane(vec![false, false, false, false, false]), Plane(vec![false, false, false, false, false]), Plane(vec![true, true, false, false, true]), ]), }; assert_eq!(u.neighbors(0, 0), vec![true; 8]); } } #[cfg(test)] mod evolve { use super::*; fn opts() -> Opts { Opts { seed: 1337, input: None, width: 13, height: 13, live_cell: 'L', dead_cell: 'D', delay: 13, } } #[test] fn blinker() { let universe = Universe { opts: opts(), future: Plane(vec![Plane(vec![false; 5]); 3]), current: Plane(vec![ Plane(vec![false, false, false, false, false]), Plane(vec![false, true, true, true, false]), Plane(vec![false, false, false, false, false]), ]), }; assert_eq!( universe.evolve(), Universe { opts: opts(), future: Plane(vec![ Plane(vec![false, false, true, false, false]), Plane(vec![false, false, true, false, false]), Plane(vec![false, false, true, false, false]), ]), current: Plane(vec![ Plane(vec![false, false, true, false, false]), Plane(vec![false, false, true, false, false]), Plane(vec![false, false, true, false, false]), ]), } ) } #[test] fn toad() { let universe = Universe { opts: opts(), future: Plane(vec![Plane(vec![false; 6]); 6]), current: Plane(vec![ Plane(vec![false, false, false, false, false, false]), Plane(vec![false, false, false, false, false, false]), Plane(vec![false, false, true, true, true, false]), Plane(vec![false, true, true, true, false, false]), Plane(vec![false, false, false, false, false, false]), Plane(vec![false, false, false, false, false, false]), ]), }; assert_eq!( universe.evolve(), Universe { opts: opts(), future: Plane(vec![ Plane(vec![false, false, false, false, false, false]), Plane(vec![false, false, false, true, false, false]), Plane(vec![false, true, false, false, true, false]), Plane(vec![false, true, false, false, true, false]), Plane(vec![false, false, true, false, false, false]), Plane(vec![false, false, false, false, false, false]), ]), current: Plane(vec![ Plane(vec![false, false, false, false, false, false]), Plane(vec![false, false, false, true, false, false]), Plane(vec![false, true, false, false, true, false]), Plane(vec![false, true, false, false, true, false]), Plane(vec![false, false, true, false, false, false]), Plane(vec![false, false, false, false, false, false]), ]), } ) } }