diff options
| author | a14m <[email protected]> | 2021-04-07 14:01:42 +0200 |
|---|---|---|
| committer | a14m <[email protected]> | 2021-04-07 14:01:42 +0200 |
| commit | bd0ef4dcf5b46664c9e0b206f6acd8cf9b3532da (patch) | |
| tree | ae632b71112538349743bc22e98e46bfba9abb44 /CLI | |
| parent | 61c49255eba642ee342aee5f7b02a57eec2cd84f (diff) | |
Add Plane (circular array) implementation w/testing
Refactor the code to use Plane struct instead of Vec<bool>
Diffstat (limited to 'CLI')
| -rw-r--r-- | CLI/rust/src/game_of_life.rs | 5 | ||||
| -rw-r--r-- | CLI/rust/src/game_of_life/generators/input.rs | 44 | ||||
| -rw-r--r-- | CLI/rust/src/game_of_life/universe.rs | 186 |
3 files changed, 209 insertions, 26 deletions
diff --git a/CLI/rust/src/game_of_life.rs b/CLI/rust/src/game_of_life.rs index 6b52034..96ec3d9 100644 --- a/CLI/rust/src/game_of_life.rs +++ b/CLI/rust/src/game_of_life.rs @@ -175,12 +175,13 @@ pub(crate) fn generate(opts: Opts) -> Result<Universe, Error> { mod generate { use super::*; use mocktopus::mocking::*; + use crate::game_of_life::universe::Plane; fn universe_mock(opts: Opts) -> Universe { Universe { opts, - current: vec![vec![]], - future: vec![vec![]], + current: vec![Plane(vec![])], + future: vec![Plane(vec![])], } } diff --git a/CLI/rust/src/game_of_life/generators/input.rs b/CLI/rust/src/game_of_life/generators/input.rs index 26c0537..710ac8f 100644 --- a/CLI/rust/src/game_of_life/generators/input.rs +++ b/CLI/rust/src/game_of_life/generators/input.rs @@ -1,5 +1,5 @@ use crate::game_of_life::opts::Opts; -use crate::game_of_life::universe::Universe; +use crate::game_of_life::universe::{Universe, Plane}; use clap::{Error, ErrorKind}; use curl::easy::Easy; use regex::Regex; @@ -30,8 +30,8 @@ mod new { fn universe_mock(opts: Opts) -> Universe { Universe { opts, - current: vec![vec![]], - future: vec![vec![]], + current: vec![Plane(vec![])], + future: vec![Plane(vec![])], } } @@ -114,11 +114,11 @@ mod generate_file_data { universe, Universe { opts, - future: vec![vec![false; 4]; 3], + future: vec![Plane(vec![false; 4]); 3], current: vec![ - vec![false, false, true, true], - vec![false, false, true, true], - vec![false, false, false, false], + Plane(vec![false, false, true, true]), + Plane(vec![false, false, true, true]), + Plane(vec![false, false, false, false]), ], } ) @@ -191,11 +191,11 @@ mod generate_url_data { universe, Universe { opts, - future: vec![vec![false; 4]; 3], + future: vec![Plane(vec![false; 4]); 3], current: vec![ - vec![false, false, true, true], // <!do - vec![false, true, true, true], // <htm - vec![false, true, true, true], // <hea + Plane(vec![false, false, true, true]), // <!do + Plane(vec![false, true, true, true]), // <htm + Plane(vec![false, true, true, true]), // <hea ], } ) @@ -222,11 +222,11 @@ fn populate(opts: Opts, raw_data: String) -> Universe { }) .collect(); - let mut current = vec![vec![false; width]; height]; + let mut current = vec![Plane(vec![false; width]); height]; for i in 0..height { for j in 0..width { - current[i][j] = data + current[i][j as isize] = data .get(i) .unwrap_or(&vec![]) .get(j) @@ -238,7 +238,7 @@ fn populate(opts: Opts, raw_data: String) -> Universe { Universe { opts, current, - future: vec![vec![false; width]; height], + future: vec![Plane(vec![false; width]); height], } } @@ -268,13 +268,13 @@ mod populate { universe, Universe { opts, - future: vec![vec![false; 5]; 5], + future: vec![Plane(vec![false; 5]); 5], current: vec![ - vec![true, false, true, false, false], - vec![true, false, true, false, false], - vec![true, false, true, false, false], - vec![false, false, false, false, false], - vec![false, false, false, false, false], + Plane(vec![true, false, true, false, false]), + Plane(vec![true, false, true, false, false]), + Plane(vec![true, false, true, false, false]), + Plane(vec![false, false, false, false, false]), + Plane(vec![false, false, false, false, false]), ] } ) @@ -290,8 +290,8 @@ mod populate { universe, Universe { opts, - future: vec![vec![false; 2]; 2], - current: vec![vec![true, false], vec![true, false]] + future: vec![Plane(vec![false; 2]); 2], + current: vec![Plane(vec![true, false]), Plane(vec![true, false])] } ) } diff --git a/CLI/rust/src/game_of_life/universe.rs b/CLI/rust/src/game_of_life/universe.rs index 83cb7d7..11912fd 100644 --- a/CLI/rust/src/game_of_life/universe.rs +++ b/CLI/rust/src/game_of_life/universe.rs @@ -1,9 +1,191 @@ use crate::game_of_life::opts::Opts; +use std::ops::{Index, IndexMut}; + +/// Implement memeory safe indexing (circular indexing) +/// +/// When the index is out of bound (being negative or out of bound), the index +/// will try to fetch the item from the other end of the vector as if it's a +/// cylinder or circular array +fn circular_index(mut idx: isize, length: isize) -> usize { + while idx.is_negative() { + idx += length; + } + (idx % length) as usize +} + +#[cfg(test)] +mod circular_index { + use super::*; + + #[test] + fn negative_index() { + assert_eq!(circular_index(-1, 3), 2); + assert_eq!(circular_index(-2, 3), 1); + assert_eq!(circular_index(-3, 3), 0); + assert_eq!(circular_index(-4, 3), 2); + assert_eq!(circular_index(-5, 3), 1); + assert_eq!(circular_index(-6, 3), 0); + assert_eq!(circular_index(-7, 3), 2); + assert_eq!(circular_index(-8, 3), 1); + assert_eq!(circular_index(-9, 3), 0); + } + + #[test] + fn positve_index() { + assert_eq!(circular_index(0, 3), 0); + assert_eq!(circular_index(1, 3), 1); + assert_eq!(circular_index(2, 3), 2); + assert_eq!(circular_index(3, 3), 0); + assert_eq!(circular_index(4, 3), 1); + assert_eq!(circular_index(5, 3), 2); + assert_eq!(circular_index(6, 3), 0); + assert_eq!(circular_index(7, 3), 1); + assert_eq!(circular_index(8, 3), 2); + } +} + +/// Game of Life universe building blocks (a plane is a circular Vec) +/// +/// This is used internally in the implementation of the Universe to allow +/// more readable and safer traversing of the universe blocks +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct Plane(pub Vec<bool>); + +impl Index<isize> for Plane { + type Output = bool; + fn index(&self, idx: isize) -> &bool { + let len = self.0.len() as isize; + &self.0[circular_index(idx, len)] + } +} + +impl IndexMut<isize> for Plane { + fn index_mut(&mut self, idx: isize) -> &mut Self::Output { + let len = self.0.len() as isize; + &mut self.0[circular_index(idx, len)] + } +} + +#[cfg(test)] +mod plane { + use super::*; + + mod index { + use super::*; + + #[test] + fn get_element() { + let p = Plane(vec![false, false, true, false, false]); + assert_eq!(p[0], false); + assert_eq!(p[1], false); + assert_eq!(p[3], false); + assert_eq!(p[4], false); + + assert_eq!(p[-3], true); + assert_eq!(p[2], true); + assert_eq!(p[7], true); + } + } + + #[cfg(test)] + mod index_mut { + use super::*; + + #[test] + fn set_negative_index_element() { + let mut p = Plane(vec![false, false, false, false, false]); + p[-3] = true; + assert_eq!(p, Plane(vec![false, false, true, false, false])); + } + + #[test] + fn set_element() { + let mut p = Plane(vec![false, false, false, false, false]); + p[2] = true; + assert_eq!(p, Plane(vec![false, false, true, false, false])); + } + + #[test] + fn set_out_of_bound_element() { + let mut p = Plane(vec![false, false, false, false, false]); + p[7] = true; + assert_eq!(p, Plane(vec![false, false, true, false, false])); + } + } +} + /// Game of Life universe struct for running simulations #[derive(Debug, Clone, PartialEq)] pub(crate) struct Universe { pub opts: Opts, - pub current: Vec<Vec<bool>>, - pub future: Vec<Vec<bool>>, + pub current: Vec<Plane>, + pub future: Vec<Plane>, +} + +impl Index<isize> for Universe +{ + type Output = Plane; + fn index(&self, idx: isize) -> &Plane { + let len = self.current.len() as isize; + &self.current[circular_index(idx, len)] + } +} + +#[cfg(test)] +mod universe { + use super::*; + + 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: vec![ + Plane(vec![false; 5]), + Plane(vec![false, false, true, false, false]), + Plane(vec![false; 5]), + ], + future: 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); + } + } } |
