From 759c9c4f8a2e1f57745ca9250860621109fbd025 Mon Sep 17 00:00:00 2001 From: Juergen Stuber Date: Wed, 28 Nov 2018 16:02:33 +0100 Subject: [PATCH] Initial commit. --- .gitignore | 3 + Cargo.toml | 8 ++ README.txt | 12 +++ src/bin/bimood/main.rs | 141 ++++++++++++++++++++++++ src/bin/maze/main.rs | 225 +++++++++++++++++++++++++++++++++++++++ src/bin/predprey/main.rs | 166 +++++++++++++++++++++++++++++ src/lib.rs | 7 ++ 7 files changed, 562 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.txt create mode 100644 src/bin/bimood/main.rs create mode 100644 src/bin/maze/main.rs create mode 100644 src/bin/predprey/main.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f665f2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +*~ diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..66d07b9 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "pixelfoo" +version = "0.1.0" +authors = ["juergen "] +edition = "2018" + +[dependencies] +rand = "0.6.0" diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..14825bd --- /dev/null +++ b/README.txt @@ -0,0 +1,12 @@ +bimood - color cycling mood lamp with two complementary colors + parameter is the seconds for one cycle through the colors + +predprey - grass-prey-predator simulation + parameter is number of actions per 40ms frame + +maze - draws a maze + parameter is number of actions per 40ms frame + +Compile with "cargo build --release". + +The binary executables are in the "target/release" subdirectory. diff --git a/src/bin/bimood/main.rs b/src/bin/bimood/main.rs new file mode 100644 index 0000000..ba73a45 --- /dev/null +++ b/src/bin/bimood/main.rs @@ -0,0 +1,141 @@ +use std::env::args; +use std::io::stdout; +use std::io::Write; +use std::iter::repeat; +use std::thread::sleep; +use std::time::Duration; + +#[derive(Clone, Copy, Debug)] +struct Color(u8, u8, u8); + +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) + } +} + +fn interpolate_u8(b0: u8, b1: u8, a: f64) -> u8 { + let b0 = b0 as f64; + let b1 = b1 as f64; + ((1.0 - a) * b0 + a * b1) as u8 +} + +fn interpolate(c0: Color, c1: Color, a: f64) -> Color { + Color( + interpolate_u8(c0.0, c1.0, a), + interpolate_u8(c0.1, c1.1, a), + interpolate_u8(c0.2, c1.2, a), + ) +} + +type Frame = Vec>; + +fn send(w: &mut T, f: &Frame) -> std::io::Result<()> { + for l in f { + for c in l { + let Color(r, g, b) = c; + let buf = &[*r, *g, *b]; + w.write_all(buf)?; + } + } + w.flush() +} + +const DEFAULT_LOOP_TIME: usize = 120; + +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 loop_time = args[3].parse::().unwrap_or(DEFAULT_LOOP_TIME); + eprintln!( + "screen size {}x{}, loop time {:?}s", + x_size, y_size, loop_time + ); + + let mut border = 0; + while (x_size - 2 * border) * (y_size - 2 * border) > x_size * y_size / 2 { + border += 1; + } + + let t_frame = 0.040; // s + let delay = Duration::new(0, (1_000_000_000.0 * t_frame) as u32); + + let mut previous_color = Color::blue(); + let colors = vec![ + Color::red(), + Color::yellow(), + Color::green(), + Color::cyan(), + Color::blue(), + Color::magenta(), + ]; + let mut color_index = 0; + let mut next_color = colors[color_index]; + + // time to interpolate from one color to the next + let t_interpolate = (loop_time as f64) / (colors.len() as f64); // s + let delta_a = t_frame / t_interpolate; + let mut a = 0.0; + + eprint!("delta_a {}", delta_a); + loop { + let c0 = interpolate(previous_color, next_color, a); + let c1 = c0.complement(); + let mut frame = repeat(repeat(c0).take(x_size).collect::>()) + .take(y_size) + .collect::>(); + for x in border..(x_size - border) { + for y in border..(y_size - border) { + frame[y][x] = c1 + } + } + + a += delta_a; + if a >= 1.0 { + a = 0.0; + + color_index += 1; + if color_index >= colors.len() { + color_index = 0; + } + + previous_color = next_color; + next_color = colors[color_index]; + } + let mut buf = Vec::with_capacity(x_size * y_size * 3); + send(&mut buf, &frame)?; + stdout().write_all(&buf)?; + stdout().flush()?; + sleep(delay); + } +} diff --git a/src/bin/maze/main.rs b/src/bin/maze/main.rs new file mode 100644 index 0000000..01047b7 --- /dev/null +++ b/src/bin/maze/main.rs @@ -0,0 +1,225 @@ +use std::env::args; +use std::io::stdout; +use std::io::Write; +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 { + Unknown, + Corridor, + Wall, + Start, + Finish, +} + +struct Board(Vec>); + +fn send(w: &mut T, board: &Board) -> std::io::Result<()> { + for line in &board.0 { + for square in line { + let Color(r, g, b) = match square { + Square::Unknown => Color::black(), + Square::Corridor => Color::yellow(), + Square::Wall => Color::blue(), + Square::Start => Color::red(), + Square::Finish => Color::green(), + }; + let buf = &[r, g, b]; + w.write_all(buf)?; + } + } + w.flush() +} + +impl Board { + fn new(x_size: i32, y_size: i32, x_maze_size: i32, y_maze_size: i32) -> Board { + Board( + (0..y_size) + .map(move |y| { + (0..x_size) + .map(|x| { + if x < x_maze_size && y < y_maze_size { + if x == 0 + || x == x_maze_size - 1 + || y == 0 + || y == y_maze_size - 1 + || (x % 2 == 0 && y % 2 == 0) + { + Square::Wall + } else { + Square::Unknown + } + } else { + Square::Unknown + } + }) + .collect::>() + }) + .collect::>(), + ) + } + fn get(&self, pos: (i32, i32)) -> Square { + let (x, y) = pos; + self.0[y as usize][x as usize] + } + fn set(&mut self, pos: (i32, i32), sq: Square) { + let (x, y) = pos; + self.0[y as usize][x as usize] = sq; + } +} + +struct Move { + from: (i32, i32), + dir: (i32, i32), + prio: i32, +} + +fn add_move(open: &mut Vec, arg: isize, from: (i32, i32), dir: (i32, i32)) { + open.push(Move { from, dir, prio: 0 }); + open.sort_unstable_by(|Move { prio: pa, .. }, Move { prio: pb, .. }| pa.cmp(pb)); +} + +const DEFAULT_ARG: isize = 16; + +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 t_frame = 0.040; // s + let delay = Duration::new(0, (1_000_000_000.0 * t_frame) as u32); + let mut t_wait = 0.0; // s + + let x_size = x_size as i32; + let y_size = y_size as i32; + let x_maze_size = (x_size - 1) / 2 * 2 + 1; + let y_maze_size = (y_size - 1) / 2 * 2 + 1; + let x_maze_mid = (x_maze_size - 1) / 4 * 2 + 1; + let y_maze_mid = (y_maze_size - 1) / 4 * 2 + 1; + + let mut board = Board(Vec::new()); + let mut open = Vec::new(); + loop { + if open.is_empty() { + if t_wait > 0.0 { + t_wait -= t_frame; + } else { + t_wait = 60.0; // s + board = Board::new(x_size, y_size, x_maze_size, y_maze_size); + + // start in the middle + let mid = (x_maze_mid, y_maze_mid); + board.set(mid, Square::Corridor); + add_move(&mut open, arg, mid, (1, 0)); + add_move(&mut open, arg, mid, (0, 1)); + add_move(&mut open, arg, mid, (-1, 0)); + add_move(&mut open, arg, mid, (0, -1)); + } + } else { + let work = arg.max(1); + let mut count = 0; + while !open.is_empty() && count < work { + let mut start = 0; + let mut limit = 0; + let mut prio = open[limit].prio; + loop { + limit += 1; + if limit >= open.len() { + break; + } + if open[limit].prio > prio { + if rng.gen::() >= 0.4 { + break; + } + start = limit; + prio = open[limit].prio; + } + } + let r = rng.gen_range(start, limit); + let Move { + from: (x0, y0), + dir: (dx, dy), + .. + } = open.remove(r); + + let (x1, y1) = (x0 + dx, y0 + dy); + let (x2, y2) = (x1 + dx, y1 + dy); + if board.get((x1, y1)) == Square::Unknown { + board.set((x1, y1), Square::Corridor); + board.set((x2, y2), Square::Corridor); + + for (dx, dy) in vec![(1, 0), (0, 1), (-1, 0), (0, -1)] { + let (x3, y3) = (x2 + dx, y2 + dy); + let (x4, y4) = (x3 + dx, y3 + dy); + if board.get((x3, y3)) == Square::Unknown { + if board.get((x4, y4)) == Square::Unknown { + add_move(&mut open, arg, (x2, y2), (dx, dy)); + } else { + board.set((x3, y3), Square::Wall); + } + } + } + + count += 1; + } + } + if open.is_empty() { + board.set((1, 1), Square::Start); + board.set((x_maze_size - 2, y_maze_size - 2), Square::Finish); + } + } + + let mut buf = Vec::with_capacity((x_size * y_size * 3) as usize); + send(&mut buf, &board)?; + stdout().write_all(&buf)?; + stdout().flush()?; + sleep(delay); + } +} diff --git a/src/bin/predprey/main.rs b/src/bin/predprey/main.rs new file mode 100644 index 0000000..8e36d3c --- /dev/null +++ b/src/bin/predprey/main.rs @@ -0,0 +1,166 @@ +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); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..31e1bb2 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +}