animate.rs
3.2 kB · rust · 104 lines
1use super::models::{Config, Life};2use super::step::next_grid;3use super::Fate;4use crate::two::Cell2d;5use mrlycore::errors::Result;6use std::collections::HashMap;78fn prepare(seed: &Cell2d, config: &Config) -> Cell2d {9 let mut grid = seed.clone();10 if config.grid_size > 1 {11 grid = grid.tile(config.grid_size, config.grid_size);12 }13 if config.padding > 0 {14 grid = grid.pad(config.padding, 0);15 }16 grid17}1819/// Runs a seed under a config until it fixes, loops or times out, recording every generation.20pub fn animate(seed: &Cell2d, config: &Config) -> Result<Life> {21 let mask = config.mask.types().clone();22 let (birth, survive) = config.counts()?;23 let mut current = prepare(seed, config);24 let mut grids = vec![current.clone()];25 let mut history: HashMap<Vec<u8>, usize> = HashMap::new();26 history.insert(current.types().bytes().to_vec(), 0);27 let mut fate = Fate::Timeout;28 let mut loop_length = 0;29 for i in 1..config.max_generations {30 let next = next_grid(¤t, &birth, &survive, &mask, config.boundary)?;31 if next.types() == current.types() {32 fate = if next.types().sum() == 0 {33 Fate::Dead34 } else {35 Fate::Alive36 };37 break;38 }39 let key = next.types().bytes().to_vec();40 if let Some(&seen_at) = history.get(&key) {41 loop_length = i - seen_at;42 fate = Fate::Loop;43 break;44 }45 history.insert(key, i);46 current = next;47 grids.push(current.clone());48 }49 let count = grids.len();50 Ok(Life {51 grids,52 fate,53 count,54 loop_length,55 })56}5758#[cfg(test)]59mod tests {60 use super::*;61 use crate::life::{moore, Boundary};62 use mrlycore::tensor::Tensor;63 fn conway(mask: Cell2d) -> Config {64 Config {65 boundary: Boundary::Constant,66 max_generations: 16,67 ..Config::new(mask, vec![3], vec![2, 3])68 }69 }70 #[test]71 fn blinker_is_a_loop_of_two() {72 let mut t = Tensor::new(vec![5, 5]);73 t.set(&[1, 2], 1);74 t.set(&[2, 2], 1);75 t.set(&[3, 2], 1);76 let life = animate(&Cell2d::new(t), &conway(moore())).unwrap();77 assert_eq!(life.fate, Fate::Loop);78 assert_eq!(life.loop_length, 2);79 }80 #[test]81 fn block_is_alive_still_life() {82 let mut t = Tensor::new(vec![4, 4]);83 for (y, x) in [(1, 1), (1, 2), (2, 1), (2, 2)] {84 t.set(&[y, x], 1);85 }86 let life = animate(&Cell2d::new(t), &conway(moore())).unwrap();87 assert_eq!(life.fate, Fate::Alive);88 assert_eq!(life.count, 1);89 }90 #[test]91 fn binarize_on_life_grids_stays_pointwise() {92 let mut t = Tensor::new(vec![5, 5]);93 t.set(&[1, 2], 1);94 t.set(&[2, 2], 1);95 t.set(&[3, 2], 1);96 let life = animate(&Cell2d::new(t), &conway(moore())).unwrap();97 for grid in &life.grids {98 let binarized = grid.clone().binarize(1);99 assert_eq!(binarized.types(), grid.types());100 let twice = binarized.clone().binarize(1);101 assert_eq!(twice.types(), binarized.types());102 }103 }104}