added script to generate random patterns

This commit is contained in:
JOLIMAITRE Matthieu 2022-10-15 04:36:53 +02:00
parent dc2a80bf4f
commit 1c1ad2c4fd
5 changed files with 208 additions and 164 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "golrs" name = "golrs"
version = "0.1.0" version = "0.2.0"
edition = "2021" edition = "2021"
description = "a TUI for vizualising a rust implementation of the game of life." description = "a TUI for vizualising a rust implementation of the game of life."
authors = ["Matthieu JOLIMAITRE <matthieu@imagevo.fr>"] authors = ["Matthieu JOLIMAITRE <matthieu@imagevo.fr>"]
@ -9,4 +9,4 @@ repository = "https://github.com/MajorBarnulf/golrs"
[dependencies] [dependencies]
termion = "1.5" termion = "1.5"
metrohash = "1.0" metrohash = "1.0"

View file

@ -2,6 +2,10 @@
const { args } = Deno; const { args } = Deno;
if (args[0] == "--help") {
console.log("usage: [bin] <size> <frequency>");
}
const size = parseInt(args[0] ?? "5"); const size = parseInt(args[0] ?? "5");
const frequency = parseFloat(args[1] ?? "0.5"); const frequency = parseFloat(args[1] ?? "0.5");
@ -18,4 +22,4 @@ for (const _y of range(0, size)) {
result += '\n'; result += '\n';
} }
console.log(result); console.log(result);

1
rustfmt.toml Normal file
View file

@ -0,0 +1 @@
hard_tabs = true

View file

@ -13,34 +13,34 @@ pub use view::View;
mod view; mod view;
fn deserialize(str: &str) -> Vec<Pos> { fn deserialize(str: &str) -> Vec<Pos> {
let mut result = vec![]; let mut result = vec![];
let mut pos = pos!(0, 0); let mut pos = pos!(0, 0);
for c in str.chars() { for c in str.chars() {
match c { match c {
' ' => { ' ' => {
pos.x += 1; pos.x += 1;
} }
'\n' => pos = pos!(0, pos.y + 1), '\n' => pos = pos!(0, pos.y + 1),
_ => { _ => {
result.push(pos); result.push(pos);
pos.x += 1 pos.x += 1
} }
} }
} }
result result
} }
pub fn main() { pub fn main() {
let path = args().nth(1).unwrap_or_else(|| { let path = args().nth(1).unwrap_or_else(|| {
eprintln!("[error] must provide a path argument"); eprintln!("[error] must provide a path argument");
exit(1); exit(1);
}); });
let content = fs::read_to_string(path).unwrap(); let content = fs::read_to_string(path).unwrap();
let actives = deserialize(&content); let actives = deserialize(&content);
let simulation = Sim::spawn(actives); let simulation = Sim::<HashedWorld>::spawn(actives);
let view = View::spawn::<HashedWorld>(simulation.handle()); let view = View::spawn(simulation.handle());
simulation.join(); simulation.join();
view.join(); view.join();
} }

View file

@ -1,9 +1,9 @@
use std::{ use std::{
io::{stdin, stdout}, io::{stdin, stdout},
process::exit, process::exit,
sync::mpsc, sync::mpsc,
thread::{self, JoinHandle}, thread::{self, JoinHandle},
time::Duration, time::Duration,
}; };
pub use canvas::Canvas; pub use canvas::Canvas;
@ -13,189 +13,228 @@ use termion::{event::Key, input::TermRead, raw::IntoRawMode};
use crate::{pos, Pos, SimHandle, World}; use crate::{pos, Pos, SimHandle, World};
pub struct View { pub struct View<W>
thread: JoinHandle<()>, where
W: World,
{
thread: JoinHandle<()>,
sender: mpsc::Sender<ViewCmd<W>>,
} }
impl View { impl<W> View<W>
pub fn spawn<W>(handle: SimHandle<W>) -> Self where
where W: World,
W: World, {
{ pub fn spawn(handle: SimHandle<W>) -> Self
let thread = thread::spawn(|| view_loop(handle)); where
Self { thread } W: World,
} {
let (sender, receiver) = mpsc::channel();
let thread = thread::spawn(|| view_loop(receiver, handle));
Self { thread, sender }
}
pub fn join(self) { pub fn join(self) {
self.thread.join().unwrap(); self.thread.join().unwrap();
} }
}
pub struct ViewRemote<W>
where
W: World,
{
sender: mpsc::Sender<ViewCmd<W>>,
}
pub enum ViewCmd<W>
where
W: World,
{
Refresh,
UpdateWorld(W),
}
impl<W> ViewRemote<W>
where
W: World,
{
fn new(sender: mpsc::Sender<ViewCmd<W>>) -> 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)] #[derive(Debug)]
pub enum Dir { pub enum Dir {
Up, Up,
Down, Down,
Left, Left,
Right, Right,
} }
#[derive(Debug)] #[derive(Debug)]
pub enum InputCmd { pub enum InputCmd {
Exit, Exit,
ToggleDebug, ToggleDebug,
Move(Dir), Move(Dir),
Accelerate, Accelerate,
Decelerate, Decelerate,
} }
fn input_loop(sender: mpsc::Sender<InputCmd>) { fn input_loop(sender: mpsc::Sender<InputCmd>) {
let stdout = stdout().into_raw_mode().unwrap(); let stdout = stdout().into_raw_mode().unwrap();
for c in stdin().keys() { for c in stdin().keys() {
let command = match c.unwrap() { let command = match c.unwrap() {
Key::Char('q') => InputCmd::Exit, Key::Char('q') => InputCmd::Exit,
Key::Char('d') => InputCmd::ToggleDebug, Key::Char('d') => InputCmd::ToggleDebug,
Key::Up => InputCmd::Move(Dir::Up), Key::Up => InputCmd::Move(Dir::Up),
Key::Down => InputCmd::Move(Dir::Down), Key::Down => InputCmd::Move(Dir::Down),
Key::Left => InputCmd::Move(Dir::Left), Key::Left => InputCmd::Move(Dir::Left),
Key::Right => InputCmd::Move(Dir::Right), Key::Right => InputCmd::Move(Dir::Right),
Key::Char('+') => InputCmd::Accelerate, Key::Char('+') => InputCmd::Accelerate,
Key::Char('-') => InputCmd::Decelerate, Key::Char('-') => InputCmd::Decelerate,
_ => continue, _ => continue,
}; };
sender.send(command).unwrap(); sender.send(command).unwrap();
} }
drop(stdout); drop(stdout);
} }
const VIEW_REFRESH_INTERVAL: Duration = Duration::from_millis(100); const VIEW_REFRESH_INTERVAL: Duration = Duration::from_millis(100);
#[allow(unreachable_code)] #[allow(unreachable_code)]
fn view_loop<W>(handle: SimHandle<W>) fn view_loop<W>(receiver: mpsc::Receiver<ViewCmd<W>>, handle: SimHandle<W>)
where where
W: World, W: World,
{ {
let (sender, receiver) = mpsc::channel(); let (sender, receiver) = mpsc::channel();
let _input_handle = thread::spawn(|| input_loop(sender)); let _input_handle = thread::spawn(|| input_loop(sender));
let (x, y) = termion::terminal_size().unwrap(); let (x, y) = termion::terminal_size().unwrap();
let mut tick_delay = 200u64; let mut tick_delay = 200;
let mut view_origin = pos!(-(x as i32) / 2, -(y as i32)); let mut view_origin = pos!(-(x as i32) / 2, -(y as i32));
let mut debug = false; let mut debug = false;
loop { loop {
handle_inputs(&receiver, &mut view_origin, &mut debug, &mut tick_delay); handle_inputs(&receiver, &mut view_origin, &mut debug, &mut tick_delay);
handle.set_delay(tick_delay); handle.set_delay(tick_delay);
let world = handle.snapshot(); let world = handle.snapshot();
let mut canvas = Canvas::from_screen(); let mut canvas = Canvas::from_screen();
if debug { if debug {
dbg_layer(&mut canvas, &world, view_origin); dbg_layer(&mut canvas, &world, view_origin);
} }
grid_layer(&mut canvas, view_origin); grid_layer(&mut canvas, view_origin);
world_layer(&mut canvas, &world, view_origin); world_layer(&mut canvas, &world, view_origin);
title_layer(&mut canvas, handle.delay()); title_layer(&mut canvas, handle.delay());
canvas.display(); canvas.display();
} }
drop(handle); drop(handle);
} }
const MOVEMENT_STEP: i32 = 3; const MOVEMENT_STEP: i32 = 3;
fn handle_inputs( fn handle_inputs(
receiver: &mpsc::Receiver<InputCmd>, receiver: &mpsc::Receiver<InputCmd>,
view_origin: &mut Pos, view_origin: &mut Pos,
debug: &mut bool, debug: &mut bool,
delay: &mut u64, delay: &mut u64,
) { ) {
if let Ok(cmd) = receiver.recv_timeout(VIEW_REFRESH_INTERVAL) { if let Ok(cmd) = receiver.recv_timeout(VIEW_REFRESH_INTERVAL) {
match cmd { match cmd {
InputCmd::Exit => { InputCmd::Exit => {
println!("{}{}", termion::clear::All, termion::cursor::Goto(1, 1)); println!("{}{}", termion::clear::All, termion::cursor::Goto(1, 1));
exit(0); exit(0);
} }
InputCmd::ToggleDebug => *debug = !*debug, InputCmd::ToggleDebug => *debug = !*debug,
InputCmd::Move(direction) => { InputCmd::Move(direction) => {
*view_origin = *view_origin *view_origin = *view_origin
+ match direction { + match direction {
Dir::Up => pos!(0, -MOVEMENT_STEP), Dir::Up => pos!(0, -MOVEMENT_STEP),
Dir::Down => pos!(0, MOVEMENT_STEP), Dir::Down => pos!(0, MOVEMENT_STEP),
Dir::Left => pos!(-2 * MOVEMENT_STEP, 0), Dir::Left => pos!(-2 * MOVEMENT_STEP, 0),
Dir::Right => pos!(2 * MOVEMENT_STEP, 0), Dir::Right => pos!(2 * MOVEMENT_STEP, 0),
} }
} }
InputCmd::Accelerate => { InputCmd::Accelerate => {
if *delay > 0 { if *delay > 0 {
*delay -= 100 *delay -= 100
} }
} }
InputCmd::Decelerate => *delay += 100, InputCmd::Decelerate => *delay += 100,
} }
} }
} }
fn grid_layer(canvas: &mut Canvas, view_origin: Pos) { fn grid_layer(canvas: &mut Canvas, view_origin: Pos) {
canvas.layer(|local_pos| { canvas.layer(|local_pos| {
let Pos { x, y } = screen_pos_to_world(local_pos, view_origin); let Pos { x, y } = screen_pos_to_world(local_pos, view_origin);
match (x % 16 == 0, dmod(y, 16) <= 1) { match (x % 16 == 0, dmod(y, 16) <= 1) {
(true, true) => Some('┼'), (true, true) => Some('┼'),
(true, _) => Some('│'), (true, _) => Some('│'),
(_, true) => Some('─'), (_, true) => Some('─'),
_ => None, _ => None,
} }
}) })
} }
fn world_layer<W>(canvas: &mut Canvas, world: &W, view_origin: Pos) fn world_layer<W>(canvas: &mut Canvas, world: &W, view_origin: Pos)
where where
W: World, W: World,
{ {
canvas.layer(|mut local_pos| { canvas.layer(|mut local_pos| {
local_pos.y *= 2; local_pos.y *= 2;
let pos_top = local_pos + view_origin; let pos_top = local_pos + view_origin;
let top = world.get(pos_top).is_active(); let top = world.get(pos_top).is_active();
let pos_bottom = pos_top + pos!(0, 1); let pos_bottom = pos_top + pos!(0, 1);
let bottom = world.get(pos_bottom).is_active(); let bottom = world.get(pos_bottom).is_active();
match (top, bottom) { match (top, bottom) {
(true, true) => Some('█'), (true, true) => Some('█'),
(true, _) => Some('▀'), (true, _) => Some('▀'),
(_, true) => Some('▄'), (_, true) => Some('▄'),
_ => None, _ => None,
} }
}); });
} }
#[allow(clippy::useless_format)] #[allow(clippy::useless_format)]
fn title_layer(canvas: &mut Canvas, de: usize) { fn title_layer(canvas: &mut Canvas, de: usize) {
let table = [ let table = [
format!("│ <golrs> | [+]: speed up │"), format!("│ <golrs> | [+]: speed up │"),
format!("│ | [-]: slow down │"), format!("│ | [-]: slow down │"),
format!("│ tick delay: | [d]: debug │"), format!("│ tick delay: | [d]: debug │"),
format!("{de:>7} ms | [q]: quit │"), format!("{de:>7} ms | [q]: quit │"),
format!("└──────────────────────────────┘"), format!("└──────────────────────────────┘"),
]; ];
canvas.layer(|Pos { x, y }| { canvas.layer(|Pos { x, y }| {
table table
.get(y as usize) .get(y as usize)
.and_then(|line| line.chars().nth(x as usize)) .and_then(|line| line.chars().nth(x as usize))
}) })
} }
fn dbg_layer<W>(canvas: &mut Canvas, world: &W, view_origin: Pos) fn dbg_layer<W>(canvas: &mut Canvas, world: &W, view_origin: Pos)
where where
W: World, W: World,
{ {
canvas.layer(|local_pos| { canvas.layer(|local_pos| {
let pos = screen_pos_to_world(local_pos, view_origin); let pos = screen_pos_to_world(local_pos, view_origin);
world.dbg_is_loaded(pos).then_some('.') world.dbg_is_loaded(pos).then_some('.')
}) })
} }
fn screen_pos_to_world(mut pos: Pos, view_origin: Pos) -> Pos { fn screen_pos_to_world(mut pos: Pos, view_origin: Pos) -> Pos {
pos.y *= 2; pos.y *= 2;
pos + view_origin pos + view_origin
} }
/// double module to avoid irregularities between negatives and positives /// double module to avoid irregularities between negatives and positives
fn dmod(a: i32, module: i32) -> i32 { fn dmod(a: i32, module: i32) -> i32 {
((a % module) + module) % module ((a % module) + module) % module
} }