use std::env::args; use std::io::stdout; use std::io::Write; use std::iter::repeat_with; use std::thread::sleep; use std::time::Duration; use rand::thread_rng; use rand::Rng; #[derive(Clone, Copy, Debug)] struct Color(u8, u8, u8); #[allow(unused)] impl Color { fn black() -> Color { Color(0, 0, 0) } fn white() -> Color { Color(255, 255, 255) } fn gray50() -> Color { Color(128, 128, 128) } fn red() -> Color { Color(255, 0, 0) } fn green() -> Color { Color(0, 255, 0) } fn blue() -> Color { Color(0, 0, 255) } fn yellow() -> Color { Color(255, 255, 0) } fn cyan() -> Color { Color(0, 255, 255) } fn magenta() -> Color { Color(255, 0, 255) } fn complement(&self) -> Color { Color(255 - self.0, 255 - self.1, 255 - self.2) } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum Square { Empty, Grass, Rabbit, Fox, } type Board = Vec>; fn send(w: &mut T, board: &Board) -> std::io::Result<()> { for line in board { for square in line { let Color(r, g, b) = match square { Square::Empty => Color::blue(), Square::Grass => Color::green(), Square::Rabbit => Color::yellow(), Square::Fox => Color::red(), }; let buf = &[r, g, b]; w.write_all(buf)?; } } w.flush() } const DEFAULT_ARG: usize = 10; fn grow(board: &Board, x: usize, y: usize, grow: Square, die: Square) -> Square { let x_size = board[0].len() as isize; let y_size = board.len() as isize; let x = x as isize; let y = y as isize; for (dx, dy) in vec![(1, 0), (0, 1), (-1, 0), (0, -1)] { let x1 = x + dx; let y1 = y + dy; if 0 <= x1 && x1 < x_size && 0 <= y1 && y1 < y_size { let neigh_sq = board[y1 as usize][x1 as usize]; if neigh_sq == grow { return grow; } } } die } fn main() -> std::io::Result<()> { let args = args().collect::>(); eprintln!("executing {}", args[0]); let x_size = args[1].parse::().unwrap(); let y_size = args[2].parse::().unwrap(); let arg = args[3].parse::().unwrap_or(DEFAULT_ARG); eprintln!("screen size {}x{}, arg {}", x_size, y_size, arg); let mut rng = thread_rng(); let p_empty = 0.25; let p_grass = 0.25; let p_rabbit = 0.25; // p_fox = 0.05 let mut board = repeat_with(|| { repeat_with(|| { let p = rng.gen::(); if p < p_empty { Square::Empty } else if p < p_empty + p_grass { Square::Grass } else if p < p_empty + p_grass + p_rabbit { Square::Rabbit } else { Square::Fox } }) .take(x_size) .collect::>() }) .take(y_size) .collect::>(); let t_frame = 0.040; // s let delay = Duration::new(0, (1_000_000_000.0 * t_frame) as u32); let x_mid = (x_size - 1) as f64 / 2.0; let y_mid = (y_size - 1) as f64 / 2.0; let r = (x_size.min(y_size) - 1) as f64 / 2.5; loop { for _ in 0..arg { let x = rng.gen_range(0, x_size); let y = rng.gen_range(0, y_size); let sq = board[y][x]; let dx = (x as f64 - x_mid as f64) / r as f64; let dy = (y as f64 - y_mid as f64) / r as f64; let p0 = (dx * dx + dy * dy).min(1.0); let p_survive = match sq { Square::Empty => 1.0, Square::Grass => p0, Square::Rabbit => p0, Square::Fox => 0.8 * p0, }; let new_sq = if rng.gen::() < p_survive { sq } else { Square::Empty }; board[y][x] = match sq { Square::Empty => grow(&board, x, y, Square::Grass, new_sq), Square::Grass => grow(&board, x, y, Square::Rabbit, new_sq), Square::Rabbit => grow(&board, x, y, Square::Fox, new_sq), Square::Fox => new_sq, }; } let mut buf = Vec::with_capacity(x_size * y_size * 3); send(&mut buf, &board)?; stdout().write_all(&buf)?; stdout().flush()?; sleep(delay); } }