summaryrefslogtreecommitdiffstats
path: root/CLI
diff options
context:
space:
mode:
authora14m <[email protected]>2021-04-07 14:01:42 +0200
committera14m <[email protected]>2021-04-07 14:01:42 +0200
commitbd0ef4dcf5b46664c9e0b206f6acd8cf9b3532da (patch)
treeae632b71112538349743bc22e98e46bfba9abb44 /CLI
parent61c49255eba642ee342aee5f7b02a57eec2cd84f (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.rs5
-rw-r--r--CLI/rust/src/game_of_life/generators/input.rs44
-rw-r--r--CLI/rust/src/game_of_life/universe.rs186
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);
+ }
+ }
}