From 7d9836ebef1950c550081b2ea9cd3f7718280a02 Mon Sep 17 00:00:00 2001 From: Malte Voos Date: Sat, 20 Aug 2022 13:27:09 +0200 Subject: init --- src/main.rs | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 src/main.rs (limited to 'src') diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a9af30d --- /dev/null +++ b/src/main.rs @@ -0,0 +1,233 @@ +use pixels::wgpu::Color; +use pixels::PixelsBuilder; +use pixels::SurfaceTexture; +use rand::distributions::Standard; +use rand::prelude::Distribution; +use winit::dpi::PhysicalSize; +use winit::event::Event; +use winit::event_loop::EventLoop; +use winit::window::WindowBuilder; +use winit_input_helper::WinitInputHelper; + +const RES_X: usize = 265; +const RES_Y: usize = 265; + +const WIN_WIDTH: u32 = 4 * RES_X as u32; +const WIN_HEIGHT: u32 = 4 * RES_Y as u32; + +fn main() -> anyhow::Result<()> { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new() + .with_inner_size(PhysicalSize { + width: WIN_WIDTH, + height: WIN_HEIGHT, + }) + .with_resizable(false) + .build(&event_loop)?; + let mut input = WinitInputHelper::new(); + + let window_size = window.inner_size(); + let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window); + + let mut pixels = PixelsBuilder::new(RES_X as u32, RES_Y as u32, surface_texture) + .clear_color(Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 1.0, + }) + .build()?; + let mut life = Life::new(); + + event_loop.run(move |event, _, control_flow| { + control_flow.set_poll(); + + if let Event::RedrawRequested(_) = event { + life.draw(pixels.get_frame()); + pixels.render().expect("failed to render"); + } + + if input.update(&event) { + if input.quit() { + control_flow.set_exit(); + return; + }; + + life.update(); + window.request_redraw(); + }; + }); +} + +#[derive(Clone, Copy, Debug)] +enum Cell { + Alive, + Dead { since: u8 }, +} + +#[derive(Debug)] +struct Grid { + pub cells: [[Cell; RES_X]; RES_Y], +} + +enum FrontGrid { + A, + B, +} + +struct Life { + grid_a: Grid, + grid_b: Grid, + front_grid: FrontGrid, +} + +impl Cell { + pub fn is_alive(&self) -> bool { + match self { + Cell::Alive => true, + Cell::Dead { .. } => false, + } + } +} + +impl Grid { + pub fn blank() -> Self { + Self { + cells: [[Cell::Dead { since: 0xff }; RES_X]; RES_Y], + } + } + + pub fn with_random_borders() -> Self { + let mut ret = Self::blank(); + ret.randomize_border(); + ret + } + + pub fn randomize_border(&mut self) { + self.cells[RES_X - 1] = rand::random(); + } + + pub fn draw(&self, frame: &mut [u8]) { + for row in 0..RES_X { + for col in 0..RES_Y { + let pixel_idx = 4 * (RES_X * row + col); + + match self.cells[row][col] { + Cell::Alive => { + frame[pixel_idx + 0] = 0xff; // R + frame[pixel_idx + 1] = 0xff; // G + frame[pixel_idx + 2] = 0xff; // B + frame[pixel_idx + 3] = 0xff; // A + } + Cell::Dead { since } => { + frame[pixel_idx + 0] = 0xff - since.saturating_mul(16); // R + frame[pixel_idx + 1] = if since < 0x8 { + 0 + } else { + (since - 0x8).saturating_mul(0x8) / 2 + }; + // G + frame[pixel_idx + 2] = 0xff; // B + frame[pixel_idx + 3] = 0xff - since; // A + } + }; + } + } + } + + pub fn num_alive_neighbors(&self, row: usize, col: usize) -> u8 { + let mut ret = 0; + + for i in row.saturating_sub(1)..=(row + 1).min(RES_X - 1) { + for j in col.saturating_sub(1)..=(col + 1).min(RES_Y - 1) { + if self.cells[i][j].is_alive() { + ret += 1; + } + } + } + if self.cells[row][col].is_alive() { + ret -= 1; + } + + ret + } + + pub fn new_state(&self, row: usize, col: usize) -> Cell { + match self.cells[row][col] { + Cell::Alive => { + if (2..=3).contains(&self.num_alive_neighbors(row, col)) { + Cell::Alive + } else { + Cell::Dead { since: 0 } + } + } + Cell::Dead { since } => { + if self.num_alive_neighbors(row, col) == 3 { + Cell::Alive + } else { + Cell::Dead { + since: since.saturating_add(1), + } + } + } + } + } +} + +impl Life { + pub fn new() -> Self { + Life { + grid_a: Grid::with_random_borders(), + grid_b: Grid::blank(), + front_grid: FrontGrid::A, + } + } + + pub fn draw(&self, frame: &mut [u8]) { + self.front_grid().draw(frame); + } + + pub fn update(&mut self) { + let (front, back) = self.grids(); + + for row in 0..RES_X { + for col in 0..RES_Y { + back.cells[row][col] = front.new_state(row, col); + } + } + back.randomize_border(); + + self.swap_grids(); + } + + fn grids(&mut self) -> (&Grid, &mut Grid) { + match self.front_grid { + FrontGrid::A => (&self.grid_a, &mut self.grid_b), + FrontGrid::B => (&self.grid_b, &mut self.grid_a), + } + } + + fn front_grid(&self) -> &Grid { + match self.front_grid { + FrontGrid::A => &self.grid_a, + FrontGrid::B => &self.grid_b, + } + } + + fn swap_grids(&mut self) { + self.front_grid = match self.front_grid { + FrontGrid::A => FrontGrid::B, + FrontGrid::B => FrontGrid::A, + } + } +} + +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> Cell { + if rng.gen() { + Cell::Alive + } else { + Cell::Dead { since: 0xff } + } + } +} -- cgit 1.4.1