diff --git a/Cargo.lock b/Cargo.lock index 2c1efbf..a397605 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -264,12 +264,6 @@ dependencies = [ "windows-targets 0.52.5", ] -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -363,16 +357,6 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" -[[package]] -name = "rust-lzma" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d62915608f6cee1d7f2fc00f28b4f058ff79d6e4ec3c2fe0006b09b52437c84" -dependencies = [ - "pkg-config", - "vcpkg", -] - [[package]] name = "scopeguard" version = "1.2.0" @@ -398,7 +382,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94d9aecc4d31a71578481de6c6d64383d374126c38469c9689067579c1d910fd" dependencies = [ "log", - "rust-lzma", ] [[package]] @@ -466,12 +449,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 2311197..9b88435 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" clap = { version = "4.5.4", features = ["derive"] } rand = "0.9.0-alpha.1" env_logger = "0.11.3" -servicepoint2 = "0.4.2" +servicepoint2 = { version = "0.4.2", default-features = false } crossterm = "0.27.0" log = "0.4.21" diff --git a/src/main.rs b/src/main.rs index fe99812..1b651a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ use servicepoint2::Command::{BitmapLinearWin, CharBrightness}; use crate::game::Game; use crate::print::{println_debug, println_info, println_warning}; -use crate::rules::Rules; +use crate::rules::{generate_bb3, generate_u8b3}; mod game; mod rules; @@ -33,19 +33,19 @@ fn main() { let connection = init(); let mut left_pixels = Game { - rules: Rules::random_bb3(), + rules: generate_bb3(), field: PixelGrid::max_sized(), }; let mut right_pixels = Game { - rules: Rules::random_bb3(), + rules: generate_bb3(), field: PixelGrid::max_sized(), }; let mut left_luma = Game { - rules: Rules::random_u8b3(), + rules: generate_u8b3(), field: ByteGrid::new(TILE_WIDTH, TILE_HEIGHT), }; let mut right_luma = Game { - rules: Rules::random_u8b3(), + rules: generate_u8b3(), field: ByteGrid::new(TILE_WIDTH, TILE_HEIGHT), }; @@ -65,8 +65,10 @@ fn main() { loop { let start = Instant::now(); - left_pixels.step(); - right_pixels.step(); + if iteration % Wrapping(5) == Wrapping(0) { + left_pixels.step(); + right_pixels.step(); + } left_luma.step(); right_luma.step(); @@ -81,8 +83,8 @@ fn main() { randomize(&mut left_pixels.field); randomize(&mut left_luma.field); - left_pixels.rules = Rules::random_bb3(); - left_luma.rules = Rules::random_u8b3(); + left_pixels.rules = generate_bb3(); + left_luma.rules = generate_u8b3(); } else if split_speed < 0 && split_pixel == 0 { split_pixel = pixels.width(); @@ -91,8 +93,8 @@ fn main() { randomize(&mut right_pixels.field); randomize(&mut right_luma.field); - right_pixels.rules = Rules::random_bb3(); - right_luma.rules = Rules::random_u8b3(); + right_pixels.rules = generate_bb3(); + right_luma.rules = generate_u8b3(); } split_pixel = i32::clamp(split_pixel as i32 + split_speed, 0, pixels.width() as i32) as usize; diff --git a/src/rules.rs b/src/rules.rs index d453631..a54b280 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -1,5 +1,3 @@ -use std::collections::HashSet; - use rand::{Rng, thread_rng}; use rand::rngs::ThreadRng; @@ -9,7 +7,7 @@ pub struct Rules where TState: Copy + PartialEq, TKernel: Copy { pub kernel: [[TKernel; KERNEL_SIZE]; KERNEL_SIZE], - pub count_neighbor: fn(neighbor_state: TState, kernel_value: TKernel) -> i32, + pub count_neighbor: Box i32>, pub next_state: Box TState>, } @@ -27,7 +25,7 @@ pub const NEUMANN_NEIGHBORHOOD: [[bool; 3]; 3] = [ pub const DIAGONALS_NEIGHBORHOOD: [[bool; 3]; 3] = [ [true, false, true], - [false, true, false], + [false, false, false], [true, false, true] ]; @@ -36,252 +34,73 @@ 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 { - Self::generate_bb3() +#[must_use] +pub fn generate_bb3() -> Rules { + let mut rng = thread_rng(); - /* - match rand::thread_rng().gen_range(0..=5) { - 0 => Self::game_of_life(), - 1 => Self::high_life(), - 2 => Self::seeds(), - 3 => Self::day_and_night(), - 4 => Self::mazecetric(), - 5 => Self::generate_bb3(), - _ => panic!(), - }*/ - } + 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 }; - #[must_use] - pub fn game_of_life() -> Self { - println_info("game of life"); - Self { - kernel: MOORE_NEIGHBORHOOD, - count_neighbor: count_true_neighbor, - next_state: Box::new(|old_state, neighbors| - matches!((old_state, neighbors), (true, 2) | (true, 3) | (false, 3))), - } - } + 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, &[]); - #[must_use] - pub fn high_life() -> Self { - println_info("high life"); - Self { - kernel: MOORE_NEIGHBORHOOD, - count_neighbor: count_true_neighbor, - next_state: Box::new(|old_state, neighbors| - matches!((old_state, neighbors), (true, 2) | (true, 3) | (false, 3)| (false, 6))), - } - } + println_info(format!("generated bb3: Birth {birth:?} Survival {survive:?}, kernel: {kernel:?}")); - #[must_use] - pub fn seeds() -> Self { - println_info("seeds"); - Self { - kernel: MOORE_NEIGHBORHOOD, - count_neighbor: count_true_neighbor, - next_state: Box::new(|state, neighbors| - matches!((state, neighbors), (false, 2))), - } - } - - #[must_use] - pub fn day_and_night() -> Self { - println_info("day_and_night"); - Self { - kernel: MOORE_NEIGHBORHOOD, - count_neighbor: count_true_neighbor, - next_state: Box::new(|state, neighbors| { - match (state, neighbors) { - (false, 3) => true, - (false, 6) => true, - (false, 7) => true, - (false, 8) => true, - (true, 3) => true, - (true, 4) => true, - (true, 6) => true, - (true, 7) => true, - (true, 8) => true, - _ => false, - } - }), - } - } - - #[must_use] - pub fn mazecetric() -> Self { - println_info("mazecetric"); - Self { - kernel: MOORE_NEIGHBORHOOD, - count_neighbor: count_true_neighbor, - next_state: Box::new(|state, neighbors| { - match (state, neighbors) { - (false, 3) => true, - (true, 0) => false, - (true, n) if n < 5 => true, - _ => false, - } - }), - } - } - - #[must_use] - pub fn generate_bb3() -> Self { - let mut rng = thread_rng(); - - 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); - let survive = generate_neighbor_counts(rng.gen_range(1..=max_neighbors), &mut rng); - - println_info(format!("generated bb3 moore: Birth {birth:?} Survival {survive:?}, is moore: {is_moore}")); - - Self { - kernel, - count_neighbor: count_true_neighbor, - next_state: Box::new(move |old_state, neighbors| { - old_state && survive.contains(&neighbors) - || !old_state && birth.contains(&neighbors) - }), - } + Rules { + kernel, + count_neighbor: Box::new(count_true_neighbor), + next_state: Box::new(move |old_state, neighbors| { + old_state && survive.contains(&neighbors) + || !old_state && birth.contains(&neighbors) + }), } } -fn generate_neighbor_counts(count: u8, rng: &mut ThreadRng) -> HashSet { - let mut result = HashSet::new(); +fn generate_neighbor_counts(count: u8, rng: &mut ThreadRng, exclude: &[i32]) -> Vec { + let mut result = vec!(); for _ in 0..count { - result.insert(rng.gen_range(0..=count) as i32); + let value = rng.gen_range(0..=count) as i32; + if !exclude.contains(&value) { + result.push(value); + } } result } -impl Rules { - #[must_use] - pub fn random_u8b3() -> Self { - Self::generate_u8b3() - /* - 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 generate_u8b3() -> Rules { + let mut rng = thread_rng(); - #[must_use] - pub fn brians_brain() -> Self { - const ALIVE: u8 = u8::MAX; - const DYING: u8 = ALIVE / 2; - const DEAD: u8 = 0; + let kernel = match rng.gen_range(0..3) { + 0 => MOORE_NEIGHBORHOOD, + 1 => NEUMANN_NEIGHBORHOOD, + 2 => DIAGONALS_NEIGHBORHOOD, + _ => panic!() + }; - Self { - kernel: MOORE_NEIGHBORHOOD, - count_neighbor: |state, kernel| { - if kernel && state == u8::MAX { 1 } else { 0 } - }, - next_state: Box::new(|state, neighbors| { - match (state, neighbors) { - (ALIVE, _) => DYING, - (DYING, _) => DEAD, - (DEAD, 2) => ALIVE, - (random_state, _) => if random_state > DYING { - ALIVE - } else { - DEAD - } - } - }), - } - } + let alive_threshold = rng.gen(); - #[must_use] - pub fn continuous_game_of_life() -> Self { - Self { - kernel: MOORE_NEIGHBORHOOD, - count_neighbor: |state, kernel| { - if kernel && state >= u8::MAX / 2 { 1 } else { 0 } - }, - next_state: Box::new(|old_state, neighbors| { - let is_alive = old_state >= u8::MAX / 2; - let delta = match (is_alive, neighbors) { - (true, 2) | (true, 3) | (false, 3) => 10, - _ => -10, - }; + let birth = generate_neighbor_counts(rng.gen_range(1..=9), &mut rng, &[0]); + let survive = generate_neighbor_counts(rng.gen_range(1..=9 - birth.len()) as u8, &mut rng, &[]); - i32::clamp(old_state as i32 + delta, u8::MIN as i32, u8::MAX as i32) as u8 - }), - } - } + let add = rng.gen_range(5..40); + let sub = rng.gen_range(5..40); - #[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: Box::new(|old_state, neighbors| { - if old_state % 42 == 0 { - return u8::MAX; - } + println_info(format!("generated u8b3: Birth {birth:?} Survival {survive:?}, kernel: {kernel:?}, alive_thresh: {alive_threshold}, delta: {add}/{sub}")); - 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 - }), - } - } - - #[must_use] - pub fn generate_u8b3() -> Self { - let mut rng = thread_rng(); - - 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 alive_threshold = rng.gen(); - - let birth = generate_neighbor_counts(rng.gen_range(1..=max_neighbors), &mut rng); - let survive = generate_neighbor_counts(rng.gen_range(1..=max_neighbors), &mut rng); - - let add= rng.gen_range(5..40); - let sub = rng.gen_range(5..40); - - println_info(format!("generated bb3 moore: Birth {birth:?} Survival {survive:?}, is moore: {is_moore}")); - - Self { - kernel, - count_neighbor: |state, kernel| { - if kernel { state as i32 } else { 0 } - }, - next_state: Box::new(move |old_state, neighbors| { - let neighbors = neighbors / alive_threshold as i32; - let old_is_alive = old_state >= alive_threshold; - let new_is_alive = old_is_alive && survive.contains(&neighbors) - || !old_is_alive && birth.contains(&neighbors); - let delta = if new_is_alive { add as i32 } else { -(sub as i32) }; - i32::clamp(old_state as i32 + delta, u8::MIN as i32, u8::MAX as i32) as u8 - }), - } + Rules { + kernel, + count_neighbor: Box::new(|state, kernel| { + if kernel { state as i32 } else { 0 } + }), + next_state: Box::new(move |old_state, neighbors| { + let neighbors = neighbors / alive_threshold as i32; + let old_is_alive = old_state >= alive_threshold; + let new_is_alive = old_is_alive && survive.contains(&neighbors) + || !old_is_alive && birth.contains(&neighbors); + let delta = if new_is_alive { add } else { -sub }; + i32::clamp(old_state as i32 + delta, u8::MIN as i32, u8::MAX as i32) as u8 + }), } } -