diff --git a/src/app.rs b/src/app.rs index 78a0474..1b5ee67 100644 --- a/src/app.rs +++ b/src/app.rs @@ -45,7 +45,7 @@ impl App { connection, sim: Simulation::new(), terminated: false, - target_duration: FRAME_PACING * 4, + target_duration: FRAME_PACING, pixels: Bitmap::max_sized(), luma: BrightnessGrid::new(TILE_WIDTH, TILE_HEIGHT), } diff --git a/src/game.rs b/src/game.rs index 9bf39f4..9caa85b 100644 --- a/src/game.rs +++ b/src/game.rs @@ -2,19 +2,23 @@ use servicepoint::{Grid, Value, ValueGrid}; use crate::rules::Rules; -pub(crate) struct Game +pub(crate) struct Game +where + TState: Value + PartialEq, + TKernel: Value, { - pub field: ValueGrid, - pub rules: Rules, + pub field: ValueGrid, + pub rules: Rules, } -impl Game +impl + Game { pub fn step(&mut self) { self.field = self.field_iteration(); } - fn field_iteration(&self) -> ValueGrid { + fn field_iteration(&self) -> ValueGrid { let mut next = ValueGrid::new(self.field.width(), self.field.height()); for x in 0..self.field.width() { for y in 0..self.field.height() { @@ -33,12 +37,14 @@ impl Game let mut count = 0; let kernel = &self.rules.kernel; + assert_eq!(KERNEL_SIZE % 2, 1); + let offset = KERNEL_SIZE as i32 / 2; for (kernel_y, kernel_row) in kernel.iter().enumerate() { - let offset_y = kernel_y as i32 - 1; + let offset_y = kernel_y as i32 - offset; for (kernel_x, kernel_value) in kernel_row.iter().enumerate() { - let offset_x = kernel_x as i32 - 1; + let offset_x = kernel_x as i32 - offset; let neighbor_x = x + offset_x; let neighbor_y = y + offset_y; diff --git a/src/rules.rs b/src/rules.rs index 40b984f..bd06cdd 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -1,30 +1,28 @@ use rand::rngs::ThreadRng; use rand::{thread_rng, Rng}; -use servicepoint::Value; + use crate::print::println_info; -pub struct Rules +pub struct Rules +where + TState: Copy + PartialEq, + TKernel: Copy, { - pub kernel: Kernel3x3, - pub count_neighbor: Box i32>, - pub next_state: Box T>, + pub kernel: [[TKernel; KERNEL_SIZE]; KERNEL_SIZE], + pub count_neighbor: Box i32>, + pub next_state: Box TState>, } -type Kernel3x3 = [[bool; 3]; 3]; +pub const MOORE_NEIGHBORHOOD: [[bool; 3]; 3] = + [[true, true, true], [true, false, true], [true, true, true]]; -pub const MOORE_NEIGHBORHOOD: Kernel3x3 = [ - [true, true, true], - [true, false, true], - [true, true, true] -]; - -pub const NEUMANN_NEIGHBORHOOD: Kernel3x3 = [ +pub const NEUMANN_NEIGHBORHOOD: [[bool; 3]; 3] = [ [false, true, false], [true, false, true], [false, true, false], ]; -pub const DIAGONALS_NEIGHBORHOOD: Kernel3x3 = [ +pub const DIAGONALS_NEIGHBORHOOD: [[bool; 3]; 3] = [ [true, false, true], [false, false, false], [true, false, true], @@ -39,11 +37,16 @@ pub fn count_true_neighbor(neighbor_state: bool, kernel_value: bool) -> i32 { } #[must_use] -pub fn generate_bb3() -> Rules { +pub fn generate_bb3() -> Rules { let mut rng = thread_rng(); - let kernel = choose_neighborhood(&mut rng); - let max_neighbors = count_max_neighbors(kernel); + let is_moore = rng.gen_bool(1.0 / 2.0); + let kernel = if is_moore { + MOORE_NEIGHBORHOOD + } else { + NEUMANN_NEIGHBORHOOD + }; + let max_neighbors = if is_moore { 8 } else { 4 }; let birth = generate_neighbor_counts(rng.gen_range(1..=max_neighbors), &mut rng, &[0]); let survive = generate_neighbor_counts(rng.gen_range(1..=max_neighbors), &mut rng, &[]); @@ -73,10 +76,16 @@ fn generate_neighbor_counts(count: u8, rng: &mut ThreadRng, exclude: &[i32]) -> } #[must_use] -pub fn generate_u8b3() -> Rules { +pub fn generate_u8b3() -> Rules { let mut rng = thread_rng(); - let kernel = choose_neighborhood(&mut rng); + let kernel = match rng.gen_range(0..3) { + 0 => MOORE_NEIGHBORHOOD, + 1 => NEUMANN_NEIGHBORHOOD, + 2 => DIAGONALS_NEIGHBORHOOD, + _ => panic!(), + }; + let alive_threshold = u8::max(1, rng.gen()); let birth = generate_neighbor_counts(rng.gen_range(1..=9), &mut rng, &[0]); @@ -86,8 +95,8 @@ pub fn generate_u8b3() -> Rules { &[], ); - let add = rng.gen_range(1..15); - let sub = rng.gen_range(1..15); + let add = rng.gen_range(5..40); + let sub = rng.gen_range(5..40); println_info(format!("generated u8b3: Birth {birth:?} Survival {survive:?}, kernel: {kernel:?}, alive_thresh: {alive_threshold}, delta: {add}/{sub}")); @@ -104,24 +113,3 @@ pub fn generate_u8b3() -> Rules { }), } } - -fn choose_neighborhood(rng: &mut ThreadRng) -> Kernel3x3 { - match rng.gen_range(0..3) { - 0 => MOORE_NEIGHBORHOOD, - 1 => NEUMANN_NEIGHBORHOOD, - 2 => DIAGONALS_NEIGHBORHOOD, - _ => unreachable!(), - } -} - -fn count_max_neighbors(kernel: [[bool; SIZE]; SIZE]) -> u8 { - let mut result = 0; - for row in kernel { - for cell in row { - if cell { - result += 1; - } - } - } - result -} diff --git a/src/simulation.rs b/src/simulation.rs index 03e03d2..579f645 100644 --- a/src/simulation.rs +++ b/src/simulation.rs @@ -11,10 +11,10 @@ use servicepoint::{ use std::num::Wrapping; pub(crate) struct Simulation { - pub(crate) left_pixels: Game, - pub(crate) right_pixels: Game, - pub(crate) left_luma: Game, - pub(crate) right_luma: Game, + pub(crate) left_pixels: Game, + pub(crate) right_pixels: Game, + pub(crate) left_luma: Game, + pub(crate) right_luma: Game, split_pixel: usize, pub(crate) split_speed: i32, iteration: Wrapping, @@ -64,7 +64,7 @@ impl Simulation { std::mem::swap(&mut self.left_pixels, &mut self.right_pixels); } - fn regenerate(pixels: &mut Game, luma: &mut Game) { + fn regenerate(pixels: &mut Game, luma: &mut Game) { randomize(&mut pixels.field); randomize(&mut luma.field); pixels.rules = generate_bb3(); @@ -75,8 +75,10 @@ impl Simulation { self.left_pixels.step(); self.right_pixels.step(); - self.left_luma.step(); - self.right_luma.step(); + if self.iteration % Wrapping(10) == Wrapping(0) { + self.left_luma.step(); + self.right_luma.step(); + } self.iteration += Wrapping(1u8); @@ -118,8 +120,7 @@ impl Simulation { &self.right_luma.field }; for y in 0..luma.height() { - let set = left_or_right.get(x, y) as f32 / u8::MAX as f32 * u8::from(Brightness::MAX) as f32; - let set = (set as u8).max(1); + let set: u8 = left_or_right.get(x, y) / u8::MAX * u8::from(Brightness::MAX); let set = Brightness::try_from(set).unwrap(); luma.set(x, y, set); } @@ -160,9 +161,9 @@ fn make_randomized(width: usize, height: usize) -> ValueGrid where Standard: Distribution, { - let mut grid = ValueGrid::new(width, height); - randomize(&mut grid); - grid + let mut pixels = ValueGrid::new(width, height); + randomize(&mut pixels); + pixels } fn randomize(field: &mut ValueGrid)