use std::{ io::{self, Write}, time::Duration, }; const WIDTH: usize = 64; const HEIGHT: usize = 36; pub struct Grid([[bool; HEIGHT + 2]; WIDTH + 2]); impl Default for Grid { fn default() -> Self { Self([[false; HEIGHT + 2]; WIDTH + 2]) } } impl Grid { #[inline] fn neighbors(&self, x: usize, y: usize) -> u8 { let x = x + 1; let y = y + 1; macro_rules! n { ($(($sx: tt $x: expr, $sy: tt $y: expr))+) => {{ let mut n = 0; $(if self.0[n!(@ $sx $x + x)][n!(@ $sy $y + y)] { n += 1; })+ n }}; (@ -$n:literal + $v: ident) => { $v - $n }; (@ +$n:literal + $v: ident) => { $v + $n }; } n![(-1, -1)(+0, -1)(+1, -1)(-1, +0)(+1, +0)(-1, +1)(+0, +1)(+1, +1)] } #[inline] fn kill(&mut self, x: usize, y: usize) { self.set(x, y, false); } #[inline] fn spawn(&mut self, x: usize, y: usize) { self.set(x, y, true); } #[inline] fn set(&mut self, x: usize, y: usize, to: bool) { self.0[x + 1][y + 1] = to } #[inline] fn cell(&self, x: usize, y: usize) -> bool { self.0[x + 1][y + 1] } fn iterate(&mut self) { let mut neighbors = [[0u8; HEIGHT + 2]; WIDTH + 2]; for x in 0..WIDTH { for y in 0..HEIGHT { neighbors[x + 1][y + 1] = self.neighbors(x, y); } } for x in 0..WIDTH { for y in 0..HEIGHT { let cell = self.cell(x, y); let n = neighbors[x + 1][y + 1]; match n { 2 | 3 if cell => continue, 3 if !cell => self.spawn(x, y), _ => self.kill(x, y), } } } } fn dead(&mut self) -> bool { for row in self.0 { for col in row { if col { return false; }; } } true } fn print(&self, mut w: impl Write) -> io::Result<()> { let tab = ["░░", "██"]; for y in 0..HEIGHT { for x in 0..WIDTH { w.write_all(tab[self.cell(x, y) as usize].as_bytes())?; } writeln!(w)?; } Ok(()) } } fn main() { let mut grid = Grid::default(); let img = image::open("src/seed.png").unwrap().to_luma8(); assert!(img.width() == WIDTH as u32); assert!(img.height() == HEIGHT as u32); for x in 0..WIDTH { for y in 0..HEIGHT { let on = img.get_pixel(x as u32, y as u32)[0] <= 128; grid.set(x, y, on); } } while !grid.dead() { print!(""); grid.print(std::io::stdout().lock()).unwrap(); grid.iterate(); std::thread::sleep(Duration::from_millis(100)); } } #[cfg(test)] mod tests { use super::*; #[test] fn neb() { let mut g = Grid::default(); assert_eq!(g.neighbors(0, 0), 0); assert_eq!(g.neighbors(5, 5), 0); g.spawn(5, 5); assert_eq!(g.neighbors(4, 4), 1); g.spawn(4, 5); assert_eq!(g.neighbors(4, 4), 2); } }