From 1c1ad2c4fdafbac0b1f0d152c3a9cf452b82ef8d Mon Sep 17 00:00:00 2001 From: JOLIMAITRE Matthieu Date: Sat, 15 Oct 2022 04:36:53 +0200 Subject: [PATCH] added script to generate random patterns --- Cargo.toml | 4 +- patterns/gen.ts | 6 +- rustfmt.toml | 1 + src/main.rs | 50 ++++---- src/view.rs | 311 +++++++++++++++++++++++++++--------------------- 5 files changed, 208 insertions(+), 164 deletions(-) create mode 100644 rustfmt.toml diff --git a/Cargo.toml b/Cargo.toml index 04865f0..fa1e667 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "golrs" -version = "0.1.0" +version = "0.2.0" edition = "2021" description = "a TUI for vizualising a rust implementation of the game of life." authors = ["Matthieu JOLIMAITRE "] @@ -9,4 +9,4 @@ repository = "https://github.com/MajorBarnulf/golrs" [dependencies] termion = "1.5" -metrohash = "1.0" \ No newline at end of file +metrohash = "1.0" diff --git a/patterns/gen.ts b/patterns/gen.ts index e96a460..b61cee4 100755 --- a/patterns/gen.ts +++ b/patterns/gen.ts @@ -2,6 +2,10 @@ const { args } = Deno; +if (args[0] == "--help") { + console.log("usage: [bin] "); +} + const size = parseInt(args[0] ?? "5"); const frequency = parseFloat(args[1] ?? "0.5"); @@ -18,4 +22,4 @@ for (const _y of range(0, size)) { result += '\n'; } -console.log(result); \ No newline at end of file +console.log(result); diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..218e203 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +hard_tabs = true diff --git a/src/main.rs b/src/main.rs index 38c147c..3c1877a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,34 +13,34 @@ pub use view::View; mod view; fn deserialize(str: &str) -> Vec { - let mut result = vec![]; - let mut pos = pos!(0, 0); - for c in str.chars() { - match c { - ' ' => { - pos.x += 1; - } - '\n' => pos = pos!(0, pos.y + 1), - _ => { - result.push(pos); - pos.x += 1 - } - } - } - result + let mut result = vec![]; + let mut pos = pos!(0, 0); + for c in str.chars() { + match c { + ' ' => { + pos.x += 1; + } + '\n' => pos = pos!(0, pos.y + 1), + _ => { + result.push(pos); + pos.x += 1 + } + } + } + result } pub fn main() { - let path = args().nth(1).unwrap_or_else(|| { - eprintln!("[error] must provide a path argument"); - exit(1); - }); + let path = args().nth(1).unwrap_or_else(|| { + eprintln!("[error] must provide a path argument"); + exit(1); + }); - let content = fs::read_to_string(path).unwrap(); - let actives = deserialize(&content); - let simulation = Sim::spawn(actives); - let view = View::spawn::(simulation.handle()); + let content = fs::read_to_string(path).unwrap(); + let actives = deserialize(&content); + let simulation = Sim::::spawn(actives); + let view = View::spawn(simulation.handle()); - simulation.join(); - view.join(); + simulation.join(); + view.join(); } diff --git a/src/view.rs b/src/view.rs index 17e0624..7a6a86f 100644 --- a/src/view.rs +++ b/src/view.rs @@ -1,9 +1,9 @@ use std::{ - io::{stdin, stdout}, - process::exit, - sync::mpsc, - thread::{self, JoinHandle}, - time::Duration, + io::{stdin, stdout}, + process::exit, + sync::mpsc, + thread::{self, JoinHandle}, + time::Duration, }; pub use canvas::Canvas; @@ -13,189 +13,228 @@ use termion::{event::Key, input::TermRead, raw::IntoRawMode}; use crate::{pos, Pos, SimHandle, World}; -pub struct View { - thread: JoinHandle<()>, +pub struct View +where + W: World, +{ + thread: JoinHandle<()>, + sender: mpsc::Sender>, } -impl View { - pub fn spawn(handle: SimHandle) -> Self - where - W: World, - { - let thread = thread::spawn(|| view_loop(handle)); - Self { thread } - } +impl View +where + W: World, +{ + pub fn spawn(handle: SimHandle) -> Self + where + W: World, + { + let (sender, receiver) = mpsc::channel(); + let thread = thread::spawn(|| view_loop(receiver, handle)); + Self { thread, sender } + } - pub fn join(self) { - self.thread.join().unwrap(); - } + pub fn join(self) { + self.thread.join().unwrap(); + } +} + +pub struct ViewRemote +where + W: World, +{ + sender: mpsc::Sender>, +} + +pub enum ViewCmd +where + W: World, +{ + Refresh, + UpdateWorld(W), +} + +impl ViewRemote +where + W: World, +{ + fn new(sender: mpsc::Sender>) -> Self { + Self { sender } + } + pub fn refresh(&self) { + self.sender.send(ViewCmd::Refresh).unwrap(); + } + + pub fn set_world(&self, world: W) { + self.sender.send(ViewCmd::UpdateWorld(world)).unwrap(); + } } #[derive(Debug)] pub enum Dir { - Up, - Down, - Left, - Right, + Up, + Down, + Left, + Right, } #[derive(Debug)] pub enum InputCmd { - Exit, - ToggleDebug, - Move(Dir), - Accelerate, - Decelerate, + Exit, + ToggleDebug, + Move(Dir), + Accelerate, + Decelerate, } fn input_loop(sender: mpsc::Sender) { - let stdout = stdout().into_raw_mode().unwrap(); - for c in stdin().keys() { - let command = match c.unwrap() { - Key::Char('q') => InputCmd::Exit, - Key::Char('d') => InputCmd::ToggleDebug, - Key::Up => InputCmd::Move(Dir::Up), - Key::Down => InputCmd::Move(Dir::Down), - Key::Left => InputCmd::Move(Dir::Left), - Key::Right => InputCmd::Move(Dir::Right), - Key::Char('+') => InputCmd::Accelerate, - Key::Char('-') => InputCmd::Decelerate, - _ => continue, - }; + let stdout = stdout().into_raw_mode().unwrap(); + for c in stdin().keys() { + let command = match c.unwrap() { + Key::Char('q') => InputCmd::Exit, + Key::Char('d') => InputCmd::ToggleDebug, + Key::Up => InputCmd::Move(Dir::Up), + Key::Down => InputCmd::Move(Dir::Down), + Key::Left => InputCmd::Move(Dir::Left), + Key::Right => InputCmd::Move(Dir::Right), + Key::Char('+') => InputCmd::Accelerate, + Key::Char('-') => InputCmd::Decelerate, + _ => continue, + }; - sender.send(command).unwrap(); - } - drop(stdout); + sender.send(command).unwrap(); + } + drop(stdout); } const VIEW_REFRESH_INTERVAL: Duration = Duration::from_millis(100); #[allow(unreachable_code)] -fn view_loop(handle: SimHandle) +fn view_loop(receiver: mpsc::Receiver>, handle: SimHandle) where - W: World, + W: World, { - let (sender, receiver) = mpsc::channel(); - let _input_handle = thread::spawn(|| input_loop(sender)); + let (sender, receiver) = mpsc::channel(); + let _input_handle = thread::spawn(|| input_loop(sender)); - let (x, y) = termion::terminal_size().unwrap(); - let mut tick_delay = 200u64; - let mut view_origin = pos!(-(x as i32) / 2, -(y as i32)); - let mut debug = false; - loop { - handle_inputs(&receiver, &mut view_origin, &mut debug, &mut tick_delay); - handle.set_delay(tick_delay); - let world = handle.snapshot(); + let (x, y) = termion::terminal_size().unwrap(); + let mut tick_delay = 200; + let mut view_origin = pos!(-(x as i32) / 2, -(y as i32)); + let mut debug = false; + loop { + handle_inputs(&receiver, &mut view_origin, &mut debug, &mut tick_delay); + handle.set_delay(tick_delay); + let world = handle.snapshot(); - let mut canvas = Canvas::from_screen(); - if debug { - dbg_layer(&mut canvas, &world, view_origin); - } - grid_layer(&mut canvas, view_origin); - world_layer(&mut canvas, &world, view_origin); - title_layer(&mut canvas, handle.delay()); - canvas.display(); - } - drop(handle); + let mut canvas = Canvas::from_screen(); + if debug { + dbg_layer(&mut canvas, &world, view_origin); + } + grid_layer(&mut canvas, view_origin); + world_layer(&mut canvas, &world, view_origin); + title_layer(&mut canvas, handle.delay()); + canvas.display(); + } + drop(handle); } const MOVEMENT_STEP: i32 = 3; fn handle_inputs( - receiver: &mpsc::Receiver, - view_origin: &mut Pos, - debug: &mut bool, - delay: &mut u64, + receiver: &mpsc::Receiver, + view_origin: &mut Pos, + debug: &mut bool, + delay: &mut u64, ) { - if let Ok(cmd) = receiver.recv_timeout(VIEW_REFRESH_INTERVAL) { - match cmd { - InputCmd::Exit => { - println!("{}{}", termion::clear::All, termion::cursor::Goto(1, 1)); - exit(0); - } - InputCmd::ToggleDebug => *debug = !*debug, - InputCmd::Move(direction) => { - *view_origin = *view_origin - + match direction { - Dir::Up => pos!(0, -MOVEMENT_STEP), - Dir::Down => pos!(0, MOVEMENT_STEP), - Dir::Left => pos!(-2 * MOVEMENT_STEP, 0), - Dir::Right => pos!(2 * MOVEMENT_STEP, 0), - } - } - InputCmd::Accelerate => { - if *delay > 0 { - *delay -= 100 - } - } - InputCmd::Decelerate => *delay += 100, - } - } + if let Ok(cmd) = receiver.recv_timeout(VIEW_REFRESH_INTERVAL) { + match cmd { + InputCmd::Exit => { + println!("{}{}", termion::clear::All, termion::cursor::Goto(1, 1)); + exit(0); + } + InputCmd::ToggleDebug => *debug = !*debug, + InputCmd::Move(direction) => { + *view_origin = *view_origin + + match direction { + Dir::Up => pos!(0, -MOVEMENT_STEP), + Dir::Down => pos!(0, MOVEMENT_STEP), + Dir::Left => pos!(-2 * MOVEMENT_STEP, 0), + Dir::Right => pos!(2 * MOVEMENT_STEP, 0), + } + } + InputCmd::Accelerate => { + if *delay > 0 { + *delay -= 100 + } + } + InputCmd::Decelerate => *delay += 100, + } + } } fn grid_layer(canvas: &mut Canvas, view_origin: Pos) { - canvas.layer(|local_pos| { - let Pos { x, y } = screen_pos_to_world(local_pos, view_origin); - match (x % 16 == 0, dmod(y, 16) <= 1) { - (true, true) => Some('┼'), - (true, _) => Some('│'), - (_, true) => Some('─'), - _ => None, - } - }) + canvas.layer(|local_pos| { + let Pos { x, y } = screen_pos_to_world(local_pos, view_origin); + match (x % 16 == 0, dmod(y, 16) <= 1) { + (true, true) => Some('┼'), + (true, _) => Some('│'), + (_, true) => Some('─'), + _ => None, + } + }) } fn world_layer(canvas: &mut Canvas, world: &W, view_origin: Pos) where - W: World, + W: World, { - canvas.layer(|mut local_pos| { - local_pos.y *= 2; - let pos_top = local_pos + view_origin; - let top = world.get(pos_top).is_active(); - let pos_bottom = pos_top + pos!(0, 1); - let bottom = world.get(pos_bottom).is_active(); + canvas.layer(|mut local_pos| { + local_pos.y *= 2; + let pos_top = local_pos + view_origin; + let top = world.get(pos_top).is_active(); + let pos_bottom = pos_top + pos!(0, 1); + let bottom = world.get(pos_bottom).is_active(); - match (top, bottom) { - (true, true) => Some('█'), - (true, _) => Some('▀'), - (_, true) => Some('▄'), - _ => None, - } - }); + match (top, bottom) { + (true, true) => Some('█'), + (true, _) => Some('▀'), + (_, true) => Some('▄'), + _ => None, + } + }); } #[allow(clippy::useless_format)] fn title_layer(canvas: &mut Canvas, de: usize) { - let table = [ - format!("│ | [+]: speed up │"), - format!("│ | [-]: slow down │"), - format!("│ tick delay: | [d]: debug │"), - format!("│ {de:>7} ms | [q]: quit │"), - format!("└──────────────────────────────┘"), - ]; - canvas.layer(|Pos { x, y }| { - table - .get(y as usize) - .and_then(|line| line.chars().nth(x as usize)) - }) + let table = [ + format!("│ | [+]: speed up │"), + format!("│ | [-]: slow down │"), + format!("│ tick delay: | [d]: debug │"), + format!("│ {de:>7} ms | [q]: quit │"), + format!("└──────────────────────────────┘"), + ]; + canvas.layer(|Pos { x, y }| { + table + .get(y as usize) + .and_then(|line| line.chars().nth(x as usize)) + }) } fn dbg_layer(canvas: &mut Canvas, world: &W, view_origin: Pos) where - W: World, + W: World, { - canvas.layer(|local_pos| { - let pos = screen_pos_to_world(local_pos, view_origin); - world.dbg_is_loaded(pos).then_some('.') - }) + canvas.layer(|local_pos| { + let pos = screen_pos_to_world(local_pos, view_origin); + world.dbg_is_loaded(pos).then_some('.') + }) } fn screen_pos_to_world(mut pos: Pos, view_origin: Pos) -> Pos { - pos.y *= 2; - pos + view_origin + pos.y *= 2; + pos + view_origin } /// double module to avoid irregularities between negatives and positives fn dmod(a: i32, module: i32) -> i32 { - ((a % module) + module) % module + ((a % module) + module) % module }