From fcc3494cb4a303a7b940e638c37cfe6d89eb89ef Mon Sep 17 00:00:00 2001 From: Jonathan LOQUET <8999079+JonathanLT@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:38:06 +0100 Subject: [PATCH] Split code --- README.md | 41 +++++++- src/classes.rs | 2 + src/classes/creature.rs | 161 ++++++++++++++++++++++++++++++ src/classes/matrix.rs | 88 ++++++++++++++++ src/main.rs | 216 ++-------------------------------------- 5 files changed, 296 insertions(+), 212 deletions(-) create mode 100644 src/classes.rs create mode 100644 src/classes/creature.rs create mode 100644 src/classes/matrix.rs diff --git a/README.md b/README.md index e00b439..683a464 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,47 @@ -# Creature +# Game of Life -## Description +## Matrix + +### Description +The `Matrix` struct represents a grid with size. It provides methods for matrix a new matrix, updating current matrix with reference and print current matrix. + +### Usage +You can use the `Matrix` struct to simulate Conway's Game of Life. + +```rust +use Creature; + +// Example usage +let mut matrix: Matrix = Matrix { + size: 2, + grid: vec![ + vec![Creature::new(0, 0, 2), Creature::new(0, 1, 2)], + vec![Creature::new(1, 0, 2), Creature::new(1, 1, 1)], + ], +}; +let ref_mat: Matrix = matrix.clone(); +matrix.update_matrix(&ref_mat); +assert_eq!( + matrix.grid, + vec![ + vec![Creature::new(0, 0, 2), Creature::new(0, 1, 2)], + vec![Creature::new(1, 0, 2), Creature::new(1, 1, 2)], + ] +); +``` +## Creature + +### Description The `Creature` struct represents a creature with coordinates and an alive state. It provides methods for creating a new creature, setting its alive state, checking its neighboring creatures, and counting the number of alive neighbors. -## Usage +### Usage You can use the `Creature` struct to simulate and manipulate creatures within a game matrix. ```rust // Example usage let matrix: Vec> = vec![ - vec![Creature::new(0, 0, 1), Creature::new(0, 0, 2)], - vec![Creature::new(0, 0, 1), Creature::new(0, 0, 1)] + vec![Creature::new(0, 0, 1), Creature::new(0, 1, 2)], + vec![Creature::new(1, 0, 1), Creature::new(1, 1, 1)] ]; let mut c_0 = matrix[0][0].clone(); c_0.check_still_alive(matrix); diff --git a/src/classes.rs b/src/classes.rs new file mode 100644 index 0000000..2c3090d --- /dev/null +++ b/src/classes.rs @@ -0,0 +1,2 @@ +pub mod creature; +pub mod matrix; diff --git a/src/classes/creature.rs b/src/classes/creature.rs new file mode 100644 index 0000000..6fc3689 --- /dev/null +++ b/src/classes/creature.rs @@ -0,0 +1,161 @@ +/// Struct representing a creature in the game of life. +/// +/// # Fields +/// +/// - `alive`: A boolean indicating whether the creature is alive or not. +/// - `x`: The x-coordinate of the creature. +/// - `y`: The y-coordinate of the creature. +#[derive(Debug, Clone, PartialEq)] +pub struct Creature { + alive: bool, + x: usize, + y: usize, +} + +impl Creature { + /// Creates a new creature with the given coordinates and alive state. + /// + /// # Arguments + /// + /// - `x`: The x-coordinate of the creature. + /// - `y`: The y-coordinate of the creature. + /// - `alive`: The alive state of the creature, represented as an i8. + /// 0 or 2 represents a live creature, while any other value represents a dead creature. + #[must_use] + pub fn new(x: usize, y: usize, alive: i8) -> Creature { + Creature { + alive: match alive { + 0 | 2 => true, + _ => false, + }, + x, + y, + } + } + + /// Sets the alive state of the creature. + /// + /// # Arguments + /// + /// - `alive`: The new alive state of the creature. + pub fn set_alive(&mut self, alive: bool) { + self.alive = alive; + } + + /// Checks the neighboring creatures of the current creature and updates its alive state accordingly. + /// + /// # Arguments + /// + /// - `matrix`: The game matrix containing all the creatures. + pub fn check_still_alive(&mut self, matrix: Vec>) { + match self.check_neighbors(matrix) { + 2 => { + if self.alive { + self.set_alive(true) + } + } + 3 => { + if !self.alive { + self.set_alive(true) + } + } + _ => self.set_alive(false), + }; + } + + /// Counts the number of alive neighbors of the current creature. + /// + /// # Arguments + /// + /// - `matrix`: The game matrix containing all the creatures. + /// + /// # Returns + /// + /// The number of alive neighbors of the current creature, represented as a u8. + #[must_use] + pub fn check_neighbors(&self, matrix: Vec>) -> u8 { + let max_i = matrix.len() - 1; + let max_j = matrix[0].len() - 1; + + let mut count: u8 = 0; + for x in (self.x.saturating_sub(1))..=(self.x + 1).min(max_i) { + for y in (self.y.saturating_sub(1))..=(self.y + 1).min(max_j) { + if x != self.x || y != self.y { + count += matrix[x][y].alive as u8; + } + } + } + count + } +} + +/// This struct implements the `Display` trait, which allows us to +/// print the `Creature` struct in a nice format. +impl std::fmt::Display for Creature { + /// Formats the `Creature` struct into a string, which is then + /// returned as a `Result` of type `std::fmt::Result`. The formatted + /// string represents the state of the creature, whether it is alive + /// or not, and is enclosed in square brackets. + /// + /// # Arguments + /// + /// * `f`: A formatter struct, which allows us to specify how to + /// format the data. + /// + /// # Returns + /// + /// A formatted string, representing the state of the creature. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // Format the string, representing the state of the creature. + write!(f, "[{}]", self.alive as u8) + } +} + +#[cfg(test)] +mod tests { + use super::Creature; + + #[test] + fn test_new_creature() { + let creature = Creature::new(0, 0, 2); + assert_eq!(creature.alive, true); + assert_eq!(creature.x, 0); + assert_eq!(creature.y, 0); + } + + #[test] + fn test_set_alive() { + let mut creature = Creature::new(0, 0, 2); + creature.set_alive(false); + assert_eq!(creature.alive, false); + } + + #[test] + fn test_check_neighbors() { + let matrix = vec![ + vec![Creature::new(0, 0, 2), Creature::new(0, 0, 2)], + vec![Creature::new(0, 0, 2), Creature::new(0, 0, 1)], + ]; + let c = matrix[0][0].clone(); + assert_eq!(c.check_neighbors(matrix), 2); + } + + #[test] + fn test_check_still_alive() { + let matrix: Vec> = vec![ + vec![Creature::new(0, 0, 2), Creature::new(0, 0, 2)], + vec![Creature::new(0, 0, 2), Creature::new(0, 0, 1)], + ]; + let mut c_0 = matrix[0][0].clone(); + c_0.check_still_alive(matrix); + assert_eq!(c_0.alive, true); + + let matrix: Vec> = vec![ + vec![Creature::new(0, 0, 1), Creature::new(0, 0, 2)], + vec![Creature::new(0, 0, 1), Creature::new(0, 0, 1)], + ]; + let mut c_0 = matrix[0][0].clone(); + c_0.check_still_alive(matrix); + assert_eq!(c_0.alive, false); + } +} diff --git a/src/classes/matrix.rs b/src/classes/matrix.rs new file mode 100644 index 0000000..a00d672 --- /dev/null +++ b/src/classes/matrix.rs @@ -0,0 +1,88 @@ +use rand::Rng; + +use crate::classes::creature::Creature; + +/// Struct representing a matrix in the game of life. +/// +/// # Fields +/// +/// - `size`: Size of the matrix. +#[derive(Debug, Clone, PartialEq)] +pub struct Matrix { + size: usize, + grid: Vec>, +} + +impl Matrix { + /// Creates a new square matrix with the given x and y size. + /// + /// # Arguments + /// + /// - `size`: Size. + #[must_use] + pub fn new(size: usize) -> Matrix { + // Create a new RNG for random number generation + let mut rng: rand::rngs::ThreadRng = rand::thread_rng(); + + Matrix { + size, + grid: (0..size) + .map(|x| { + (0..size) + .map(|y| Creature::new(x, y, rng.gen_range(0..=4))) + .collect() + }) + .collect(), + } + } + + /// Prints the matrix to the console. + pub fn print_matrix(&self) { + for row in self.grid.iter() { + for c in row.iter() { + print!("{}", c); + } + println!(); + } + } + + /// Updates the matrix by calling with ref_mat the check_still_alive + /// function on each creature. + /// + /// # Arguments + /// + /// - `ref_mat`: The cloned matrix to reference. + pub fn update_matrix(&mut self, ref_mat: &Matrix) { + for row in self.grid.iter_mut() { + for c in row.iter_mut() { + c.check_still_alive(ref_mat.grid.clone()); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::Creature; + use super::Matrix; + + #[test] + fn test_update_matrix() { + let mut matrix = Matrix { + size: 2, + grid: vec![ + vec![Creature::new(0, 0, 2), Creature::new(0, 1, 2)], + vec![Creature::new(1, 0, 2), Creature::new(1, 1, 1)], + ], + }; + let ref_mat: Matrix = matrix.clone(); + matrix.update_matrix(&ref_mat); + assert_eq!( + matrix.grid, + vec![ + vec![Creature::new(0, 0, 2), Creature::new(0, 1, 2)], + vec![Creature::new(1, 0, 2), Creature::new(1, 1, 2)], + ] + ); + } +} diff --git a/src/main.rs b/src/main.rs index 910eb77..7cd59cb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,117 +1,6 @@ -use rand::Rng; +use crate::classes::matrix::Matrix; -/// Struct representing a creature in the game of life. -/// -/// # Fields -/// -/// - `alive`: A boolean indicating whether the creature is alive or not. -/// - `x`: The x-coordinate of the creature. -/// - `y`: The y-coordinate of the creature. -#[derive(Debug, Clone, PartialEq)] -pub struct Creature { - alive: bool, - x: usize, - y: usize, -} - -impl Creature { - /// Creates a new creature with the given coordinates and alive state. - /// - /// # Arguments - /// - /// - `x`: The x-coordinate of the creature. - /// - `y`: The y-coordinate of the creature. - /// - `alive`: The alive state of the creature, represented as an i8. - /// 0 or 2 represents a live creature, while any other value represents a dead creature. - #[must_use] - pub fn new(x: usize, y: usize, alive: i8) -> Creature { - Creature { - alive: match alive { - 0 | 2 => true, - _ => false, - }, - x, - y, - } - } - - /// Sets the alive state of the creature. - /// - /// # Arguments - /// - /// - `alive`: The new alive state of the creature. - pub fn set_alive(&mut self, alive: bool) { - self.alive = alive; - } - - /// Checks the neighboring creatures of the current creature and updates its alive state accordingly. - /// - /// # Arguments - /// - /// - `matrix`: The game matrix containing all the creatures. - pub fn check_still_alive(&mut self, matrix: Vec>) { - match self.check_neighbors(matrix) { - 2 => { - if self.alive { - self.set_alive(true) - } - } - 3 => { - if !self.alive { - self.set_alive(true) - } - } - _ => self.set_alive(false), - }; - } - - /// Counts the number of alive neighbors of the current creature. - /// - /// # Arguments - /// - /// - `matrix`: The game matrix containing all the creatures. - /// - /// # Returns - /// - /// The number of alive neighbors of the current creature, represented as a u8. - #[must_use] - pub fn check_neighbors(&self, matrix: Vec>) -> u8 { - let max_i = matrix.len() - 1; - let max_j = matrix[0].len() - 1; - - let mut count: u8 = 0; - for x in (self.x.saturating_sub(1))..=(self.x + 1).min(max_i) { - for y in (self.y.saturating_sub(1))..=(self.y + 1).min(max_j) { - if x != self.x || y != self.y { - count += matrix[x][y].alive as u8; - } - } - } - count - } -} - -/// This struct implements the `Display` trait, which allows us to -/// print the `Creature` struct in a nice format. -impl std::fmt::Display for Creature { - /// Formats the `Creature` struct into a string, which is then - /// returned as a `Result` of type `std::fmt::Result`. The formatted - /// string represents the state of the creature, whether it is alive - /// or not, and is enclosed in square brackets. - /// - /// # Arguments - /// - /// * `f`: A formatter struct, which allows us to specify how to - /// format the data. - /// - /// # Returns - /// - /// A formatted string, representing the state of the creature. - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - // Format the string, representing the state of the creature. - write!(f, "[{}]", self.alive as u8) - } -} +mod classes; /// Main function that initializes and updates the game matrix. /// @@ -119,120 +8,33 @@ impl std::fmt::Display for Creature { /// and iteratively prints the matrix state, clones the matrix, /// updates the matrix state, and prints the updated matrix state. fn main() { - // Create a new RNG for random number generation - let mut rng = rand::thread_rng(); - // Initialize the game matrix with 5x5 dimensions, // and populate each cell with a new creature. - let mut matrix: Vec> = (0..5) - .map(|x| { - (0..5) - .map(|y| Creature::new(x, y, rng.gen_range(0..=4))) - .collect() - }) - .collect(); + let mut matrix: classes::matrix::Matrix = classes::matrix::Matrix::new(5); // Print the initial reference matrix state println!("Ref"); - print_matrix(&matrix); + matrix.print_matrix(); // Iterate 5 times for i in 0..5 { // Clone the matrix - let ref_mat: Vec> = matrix.clone(); + let ref_mat: Matrix = matrix.clone(); // Print the cloned matrix state println!("cloned {}", i); - print_matrix(&ref_mat); + ref_mat.print_matrix(); // Update the original matrix state by calling // the check_still_alive function on each creature - update_matrix(&mut matrix, &ref_mat); + matrix.update_matrix(&ref_mat); // Print the updated reference matrix state println!("ref {}", i); - print_matrix(&matrix); + matrix.print_matrix(); // Print the cloned matrix state again println!("cloned {}", i); - print_matrix(&ref_mat); - } -} - -/// Prints the given matrix to the console. -/// -/// # Arguments -/// -/// - `matrix`: The matrix to print. -fn print_matrix(matrix: &Vec>) { - for row in matrix.iter() { - for c in row.iter() { - print!("{}", c); - } - println!(); - } -} - -/// Updates the given matrix by calling the check_still_alive -/// function on each creature. -/// -/// # Arguments -/// -/// - `matrix`: The matrix to update. -/// - `ref_mat`: The cloned matrix to reference. -fn update_matrix(matrix: &mut Vec>, ref_mat: &Vec>) { - for row in matrix.iter_mut() { - for c in row.iter_mut() { - c.check_still_alive(ref_mat.clone()); - } - } -} - -#[cfg(test)] -mod tests { - use super::Creature; - - #[test] - fn test_new_creature() { - let creature = Creature::new(0, 0, 2); - assert_eq!(creature.alive, true); - assert_eq!(creature.x, 0); - assert_eq!(creature.y, 0); - } - - #[test] - fn test_set_alive() { - let mut creature = Creature::new(0, 0, 2); - creature.set_alive(false); - assert_eq!(creature.alive, false); - } - - #[test] - fn test_check_neighbors() { - let matrix = vec![ - vec![Creature::new(0, 0, 2), Creature::new(0, 0, 2)], - vec![Creature::new(0, 0, 2), Creature::new(0, 0, 1)], - ]; - let c = matrix[0][0].clone(); - assert_eq!(c.check_neighbors(matrix), 2); - } - - #[test] - fn test_check_still_alive() { - let matrix: Vec> = vec![ - vec![Creature::new(0, 0, 2), Creature::new(0, 0, 2)], - vec![Creature::new(0, 0, 2), Creature::new(0, 0, 1)], - ]; - let mut c_0 = matrix[0][0].clone(); - c_0.check_still_alive(matrix); - assert_eq!(c_0.alive, true); - - let matrix: Vec> = vec![ - vec![Creature::new(0, 0, 1), Creature::new(0, 0, 2)], - vec![Creature::new(0, 0, 1), Creature::new(0, 0, 1)], - ]; - let mut c_0 = matrix[0][0].clone(); - c_0.check_still_alive(matrix); - assert_eq!(c_0.alive, false); + ref_mat.print_matrix(); } }