Compare commits
2 commits
558964bfb1
...
fa8118bb78
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fa8118bb78 | ||
![]() |
3b5872c8ea |
|
@ -45,7 +45,7 @@ impl App {
|
||||||
connection,
|
connection,
|
||||||
sim: Simulation::new(),
|
sim: Simulation::new(),
|
||||||
terminated: false,
|
terminated: false,
|
||||||
target_duration: FRAME_PACING,
|
target_duration: FRAME_PACING * 4,
|
||||||
pixels: Bitmap::max_sized(),
|
pixels: Bitmap::max_sized(),
|
||||||
luma: BrightnessGrid::new(TILE_WIDTH, TILE_HEIGHT),
|
luma: BrightnessGrid::new(TILE_WIDTH, TILE_HEIGHT),
|
||||||
}
|
}
|
||||||
|
|
20
src/game.rs
20
src/game.rs
|
@ -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;
|
||||||
|
|
||||||
|
|
72
src/rules.rs
72
src/rules.rs
|
@ -1,28 +1,30 @@
|
||||||
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];
|
||||||
[[true, true, true], [true, false, true], [true, true, true]];
|
|
||||||
|
|
||||||
pub const NEUMANN_NEIGHBORHOOD: [[bool; 3]; 3] = [
|
pub const MOORE_NEIGHBORHOOD: Kernel3x3 = [
|
||||||
|
[true, true, true],
|
||||||
|
[true, false, true],
|
||||||
|
[true, true, true]
|
||||||
|
];
|
||||||
|
|
||||||
|
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 +39,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 +73,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]);
|
||||||
|
@ -95,8 +86,8 @@ pub fn generate_u8b3() -> Rules<u8, bool, 3> {
|
||||||
&[],
|
&[],
|
||||||
);
|
);
|
||||||
|
|
||||||
let add = rng.gen_range(5..40);
|
let add = rng.gen_range(1..15);
|
||||||
let sub = rng.gen_range(5..40);
|
let sub = rng.gen_range(1..15);
|
||||||
|
|
||||||
println_info(format!("generated u8b3: Birth {birth:?} Survival {survive:?}, kernel: {kernel:?}, alive_thresh: {alive_threshold}, delta: {add}/{sub}"));
|
println_info(format!("generated u8b3: Birth {birth:?} Survival {survive:?}, kernel: {kernel:?}, alive_thresh: {alive_threshold}, delta: {add}/{sub}"));
|
||||||
|
|
||||||
|
@ -113,3 +104,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
|
||||||
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
@ -75,10 +75,8 @@ impl Simulation {
|
||||||
self.left_pixels.step();
|
self.left_pixels.step();
|
||||||
self.right_pixels.step();
|
self.right_pixels.step();
|
||||||
|
|
||||||
if self.iteration % Wrapping(10) == Wrapping(0) {
|
|
||||||
self.left_luma.step();
|
self.left_luma.step();
|
||||||
self.right_luma.step();
|
self.right_luma.step();
|
||||||
}
|
|
||||||
|
|
||||||
self.iteration += Wrapping(1u8);
|
self.iteration += Wrapping(1u8);
|
||||||
|
|
||||||
|
@ -120,7 +118,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 = (set as u8).max(1);
|
||||||
let set = Brightness::try_from(set).unwrap();
|
let set = Brightness::try_from(set).unwrap();
|
||||||
luma.set(x, y, set);
|
luma.set(x, y, set);
|
||||||
}
|
}
|
||||||
|
@ -161,9 +160,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>)
|
||||||
|
|
Loading…
Reference in a new issue