use super::circular_index; use crate::game_of_life::opts::Opts; use crate::game_of_life::simulation::plane::Plane; use std::{fmt, 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>, } /// Implement print for the universe impl fmt::Display for Universe { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut to_s = String::new(); for row in 0..self.current.len() { for col in 0..self.current[row].len() { match self[row as isize][col as isize] { true => to_s.push(self.opts.live_cell), false => to_s.push(self.opts.dead_cell), }; } to_s.push_str("\n"); } to_s.push_str(&format!("{}", self.opts.banner())); write!(f, "{}", to_s) } } #[cfg(test)] mod display { use super::*; fn universe(opts: Opts) -> Universe { Universe { opts, current: Plane(vec![ Plane(vec![false; 5]), Plane(vec![false, true, true, true, false]), Plane(vec![false; 5]), ]), future: Plane(vec![Plane(vec![])]), } } #[test] fn without_input() { let opts = Opts { input: None, seed: 1337, width: 13, height: 13, live_cell: 'L', dead_cell: 'D', delay: 13, }; let universe = universe(opts); assert_eq!(universe.to_string(), "DDDDD\nDLLLD\nDDDDD\nSeed: 1337") } #[test] fn with_input() { let opts = Opts { input: Some(String::from("http://example.com")), seed: 1337, width: 13, height: 13, live_cell: 'X', dead_cell: '.', delay: 13, }; let universe = universe(opts); assert_eq!(universe.to_string(), ".....\n.XXX.\n.....\nInput: http://example.com") } } 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]), ]), } ) } }