next #1

Merged
vinzenz merged 9 commits from next into main 2025-05-03 11:53:41 +02:00
3 changed files with 53 additions and 50 deletions
Showing only changes of commit 3b5872c8ea - Show all commits

View file

@ -2,23 +2,19 @@ use servicepoint::{Grid, Value, ValueGrid};
use crate::rules::Rules; use crate::rules::Rules;
pub(crate) struct Game<TState, TKernel, const KERNEL_SIZE: usize> pub(crate) struct Game<T: Value>
where
TState: Value + PartialEq,
TKernel: Value,
{ {
pub field: ValueGrid<TState>, pub field: ValueGrid<T>,
pub rules: Rules<TState, TKernel, KERNEL_SIZE>, pub rules: Rules<T>,
} }
impl<TState: Value + PartialEq, TKernel: Value, const KERNEL_SIZE: usize> impl<T: Value> Game<T>
Game<TState, TKernel, KERNEL_SIZE>
{ {
pub fn step(&mut self) { pub fn step(&mut self) {
self.field = self.field_iteration(); self.field = self.field_iteration();
} }
fn field_iteration(&self) -> ValueGrid<TState> { fn field_iteration(&self) -> ValueGrid<T> {
let mut next = ValueGrid::new(self.field.width(), self.field.height()); let mut next = ValueGrid::new(self.field.width(), self.field.height());
for x in 0..self.field.width() { for x in 0..self.field.width() {
for y in 0..self.field.height() { for y in 0..self.field.height() {
@ -37,14 +33,12 @@ impl<TState: Value + PartialEq, TKernel: Value, const KERNEL_SIZE: usize>
let mut count = 0; let mut count = 0;
let kernel = &self.rules.kernel; 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() { for (kernel_y, kernel_row) in kernel.iter().enumerate() {
let offset_y = kernel_y as i32 - offset; let offset_y = kernel_y as i32 - 1;
for (kernel_x, kernel_value) in kernel_row.iter().enumerate() { for (kernel_x, kernel_value) in kernel_row.iter().enumerate() {
let offset_x = kernel_x as i32 - offset; let offset_x = kernel_x as i32 - 1;
let neighbor_x = x + offset_x; let neighbor_x = x + offset_x;
let neighbor_y = y + offset_y; let neighbor_y = y + offset_y;

View file

@ -1,28 +1,27 @@
use rand::rngs::ThreadRng; use rand::rngs::ThreadRng;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use servicepoint::Value;
use crate::print::println_info; use crate::print::println_info;
pub struct Rules<TState, TKernel, const KERNEL_SIZE: usize> pub struct Rules<T: Value>
where
TState: Copy + PartialEq,
TKernel: Copy,
{ {
pub kernel: [[TKernel; KERNEL_SIZE]; KERNEL_SIZE], pub kernel: Kernel3x3,
pub count_neighbor: Box<dyn Fn(TState, TKernel) -> i32>, pub count_neighbor: Box<dyn Fn(T, bool) -> i32>,
pub next_state: Box<dyn Fn(TState, i32) -> TState>, pub next_state: Box<dyn Fn(T, i32) -> T>,
} }
pub const MOORE_NEIGHBORHOOD: [[bool; 3]; 3] = type Kernel3x3 = [[bool; 3]; 3];
pub const MOORE_NEIGHBORHOOD: Kernel3x3 =
[[true, true, true], [true, false, true], [true, true, true]]; [[true, true, true], [true, false, true], [true, true, true]];
pub const NEUMANN_NEIGHBORHOOD: [[bool; 3]; 3] = [ pub const NEUMANN_NEIGHBORHOOD: Kernel3x3 = [
[false, true, false], [false, true, false],
[true, false, true], [true, false, true],
[false, true, false], [false, true, false],
]; ];
pub const DIAGONALS_NEIGHBORHOOD: [[bool; 3]; 3] = [ pub const DIAGONALS_NEIGHBORHOOD: Kernel3x3 = [
[true, false, true], [true, false, true],
[false, false, false], [false, false, false],
[true, false, true], [true, false, true],
@ -37,16 +36,11 @@ pub fn count_true_neighbor(neighbor_state: bool, kernel_value: bool) -> i32 {
} }
#[must_use] #[must_use]
pub fn generate_bb3() -> Rules<bool, bool, 3> { pub fn generate_bb3() -> Rules<bool> {
let mut rng = thread_rng(); let mut rng = thread_rng();
let is_moore = rng.gen_bool(1.0 / 2.0); let kernel = choose_neighborhood(&mut rng);
let kernel = if is_moore { let max_neighbors = count_max_neighbors(kernel);
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 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, &[]); let survive = generate_neighbor_counts(rng.gen_range(1..=max_neighbors), &mut rng, &[]);
@ -76,16 +70,10 @@ fn generate_neighbor_counts(count: u8, rng: &mut ThreadRng, exclude: &[i32]) ->
} }
#[must_use] #[must_use]
pub fn generate_u8b3() -> Rules<u8, bool, 3> { pub fn generate_u8b3() -> Rules<u8> {
let mut rng = thread_rng(); let mut rng = thread_rng();
let kernel = match rng.gen_range(0..3) { let kernel = choose_neighborhood(&mut rng);
0 => MOORE_NEIGHBORHOOD,
1 => NEUMANN_NEIGHBORHOOD,
2 => DIAGONALS_NEIGHBORHOOD,
_ => panic!(),
};
let alive_threshold = u8::max(1, rng.gen()); let alive_threshold = u8::max(1, rng.gen());
let birth = generate_neighbor_counts(rng.gen_range(1..=9), &mut rng, &[0]); let birth = generate_neighbor_counts(rng.gen_range(1..=9), &mut rng, &[0]);
@ -113,3 +101,24 @@ pub fn generate_u8b3() -> Rules<u8, bool, 3> {
}), }),
} }
} }
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<const SIZE: usize>(kernel: [[bool; SIZE]; SIZE]) -> u8 {
let mut result = 0;
for row in kernel {
for cell in row {
if cell {
result += 1;
}
}
}
result
}

View file

@ -11,10 +11,10 @@ use servicepoint::{
use std::num::Wrapping; use std::num::Wrapping;
pub(crate) struct Simulation { pub(crate) struct Simulation {
pub(crate) left_pixels: Game<bool, bool, 3>, pub(crate) left_pixels: Game<bool>,
pub(crate) right_pixels: Game<bool, bool, 3>, pub(crate) right_pixels: Game<bool>,
pub(crate) left_luma: Game<u8, bool, 3>, pub(crate) left_luma: Game<u8>,
pub(crate) right_luma: Game<u8, bool, 3>, pub(crate) right_luma: Game<u8>,
split_pixel: usize, split_pixel: usize,
pub(crate) split_speed: i32, pub(crate) split_speed: i32,
iteration: Wrapping<u8>, iteration: Wrapping<u8>,
@ -64,7 +64,7 @@ impl Simulation {
std::mem::swap(&mut self.left_pixels, &mut self.right_pixels); std::mem::swap(&mut self.left_pixels, &mut self.right_pixels);
} }
fn regenerate(pixels: &mut Game<bool, bool, 3>, luma: &mut Game<u8, bool, 3>) { fn regenerate(pixels: &mut Game<bool>, luma: &mut Game<u8>) {
randomize(&mut pixels.field); randomize(&mut pixels.field);
randomize(&mut luma.field); randomize(&mut luma.field);
pixels.rules = generate_bb3(); pixels.rules = generate_bb3();
@ -120,8 +120,8 @@ impl Simulation {
&self.right_luma.field &self.right_luma.field
}; };
for y in 0..luma.height() { for y in 0..luma.height() {
let set: u8 = left_or_right.get(x, y) / u8::MAX * u8::from(Brightness::MAX); let set = left_or_right.get(x, y) as f32 / u8::MAX as f32 * u8::from(Brightness::MAX) as f32;
let set = Brightness::try_from(set).unwrap(); let set = Brightness::try_from(set as u8).unwrap();
luma.set(x, y, set); luma.set(x, y, set);
} }
} }
@ -161,9 +161,9 @@ fn make_randomized<T: Value>(width: usize, height: usize) -> ValueGrid<T>
where where
Standard: Distribution<T>, Standard: Distribution<T>,
{ {
let mut pixels = ValueGrid::new(width, height); let mut grid = ValueGrid::new(width, height);
randomize(&mut pixels); randomize(&mut grid);
pixels grid
} }
fn randomize<T: Value>(field: &mut ValueGrid<T>) fn randomize<T: Value>(field: &mut ValueGrid<T>)