diff --git a/src/main.rs b/src/main.rs index 9a60ff0..fe99812 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ use crossterm::terminal::{ use log::LevelFilter; use rand::distributions::{Distribution, Standard}; use rand::Rng; -use servicepoint2::{ByteGrid, CompressionCode, Connection, FRAME_PACING, Grid, Origin, PixelGrid, TILE_HEIGHT, TILE_WIDTH}; +use servicepoint2::{ByteGrid, CompressionCode, Connection, Grid, Origin, PixelGrid, TILE_HEIGHT, TILE_WIDTH}; use servicepoint2::Command::{BitmapLinearWin, CharBrightness}; use crate::game::Game; @@ -33,19 +33,19 @@ fn main() { let connection = init(); let mut left_pixels = Game { - rules: Rules::day_and_night(), + rules: Rules::random_bb3(), field: PixelGrid::max_sized(), }; let mut right_pixels = Game { - rules: Rules::seeds(), + rules: Rules::random_bb3(), field: PixelGrid::max_sized(), }; let mut left_luma = Game { - rules: Rules::continuous_game_of_life(), + rules: Rules::random_u8b3(), field: ByteGrid::new(TILE_WIDTH, TILE_HEIGHT), }; let mut right_luma = Game { - rules: Rules::continuous_game_of_life(), + rules: Rules::random_u8b3(), field: ByteGrid::new(TILE_WIDTH, TILE_HEIGHT), }; @@ -75,16 +75,24 @@ fn main() { if split_speed > 0 && split_pixel == pixels.width() { split_pixel = 0; + (left_luma, right_luma) = (right_luma, left_luma); (left_pixels, right_pixels) = (right_pixels, left_pixels); + randomize(&mut left_pixels.field); randomize(&mut left_luma.field); + left_pixels.rules = Rules::random_bb3(); + left_luma.rules = Rules::random_u8b3(); } else if split_speed < 0 && split_pixel == 0 { split_pixel = pixels.width(); + (left_luma, right_luma) = (right_luma, left_luma); (left_pixels, right_pixels) = (right_pixels, left_pixels); + randomize(&mut right_pixels.field); randomize(&mut right_luma.field); + right_pixels.rules = Rules::random_bb3(); + right_luma.rules = Rules::random_u8b3(); } split_pixel = i32::clamp(split_pixel as i32 + split_speed, 0, pixels.width() as i32) as usize; @@ -122,9 +130,10 @@ fn main() { } + let wanted_time = Duration::from_millis(100); let tick_time = start.elapsed(); - if tick_time < FRAME_PACING { - thread::sleep(FRAME_PACING - tick_time); + if tick_time < wanted_time { + thread::sleep(wanted_time - tick_time); } } } @@ -211,11 +220,7 @@ fn draw_luma(luma: &mut ByteGrid, left: &ByteGrid, right: &ByteGrid, split_tile: for x in 0..luma.width() { let left_or_right = if x < split_tile { left } else { right }; for y in 0..luma.height() { - let set = if x == split_tile { - 255 - } else { - u8::max(48, left_or_right.get(x, y)) - }; + let set = u8::max(48, left_or_right.get(x, y)); luma.set(x, y, set); } } diff --git a/src/rules.rs b/src/rules.rs index 008fc7a..ffbec9d 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -1,3 +1,5 @@ +use rand::Rng; + pub struct Rules where TState: Copy + PartialEq, TKernel: Copy { @@ -12,12 +14,36 @@ pub const MOORE_NEIGHBORHOOD: [[bool; 3]; 3] = [ [true, true, true] ]; +pub const NEUMANN_NEIGHBORHOOD: [[bool; 3]; 3] = [ + [false, true, false], + [true, false, true], + [false, true, false] +]; + +pub const DIAGONALS_NEIGHBORHOOD: [[bool; 3]; 3] = [ + [true, false, true], + [false, true, false], + [true, false, true] +]; + pub fn count_true_neighbor(neighbor_state: bool, kernel_value: bool) -> i32 { if neighbor_state && kernel_value { 1 } else { 0 } } impl Rules { + #[must_use] + pub fn random_bb3() -> Self { + match rand::thread_rng().gen_range(0..=4) { + 0 => Self::game_of_life(), + 1 => Self::high_life(), + 2 => Self::seeds(), + 3 => Self::day_and_night(), + 4 => Self::mazecetric(), + _ => panic!(), + } + } + #[must_use] pub fn game_of_life() -> Self { Self { @@ -69,9 +95,35 @@ impl Rules { }, } } + + #[must_use] + pub fn mazecetric() -> Self { + Self { + kernel: MOORE_NEIGHBORHOOD, + count_neighbor: count_true_neighbor, + next_state: |state, neighbors| { + match (state, neighbors) { + (false, 3) => true, + (true, 0) => false, + (true, n) if n < 5 => true, + _ => false, + } + }, + } + } } impl Rules { + #[must_use] + pub fn random_u8b3() -> Self { + match rand::thread_rng().gen_range(0..3) { + 0 => Self::brians_brain(), + 1 => Self::continuous_game_of_life(), + 2 => Self::equalizer(), + _ => panic!(), + } + } + #[must_use] pub fn brians_brain() -> Self { const ALIVE: u8 = u8::MAX; @@ -98,6 +150,8 @@ impl Rules { } } + + #[must_use] pub fn continuous_game_of_life() -> Self { Self { kernel: MOORE_NEIGHBORHOOD, @@ -115,5 +169,38 @@ impl Rules { }, } } + + #[must_use] + pub fn equalizer() -> Self { + Self { + kernel: DIAGONALS_NEIGHBORHOOD, + count_neighbor: |state, kernel| { + let state = state as i32; + if kernel { + state + } else { + 0 + } + }, + next_state: |old_state, neighbors| { + if old_state % 42 == 0 { + return u8::MAX; + } + + if old_state % 23 == 0 { + return u8::MIN; + } + + let average_health = neighbors / 5; + let delta = if average_health > old_state as i32 { + 10 + } else { + -10 + }; + + i32::clamp(old_state as i32 + delta, u8::MIN as i32, u8::MAX as i32) as u8 + }, + } + } }