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 } } } }