From 9b92497c3b29655880d2c30484cf3f2ebbad59a4 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Fri, 23 Jul 2021 15:10:14 +0200 Subject: [PATCH 1/5] Fix off-by-ones at world's end --- src/agent.rs | 2 +- src/world.rs | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/agent.rs b/src/agent.rs index 260a922..7cd68f7 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -110,7 +110,7 @@ fn random_move_within( loop { let mv = random_move(); if let Move::TryMove(Direction { x, y }) = mv { - if top + y > 0 && bottom - y > 0 && left + x > 0 && right - x > 0 { + if top + y >= 0 && bottom - y >= 0 && left + x >= 0 && right - x >= 0 { return mv; } } diff --git a/src/world.rs b/src/world.rs index 581d006..6633052 100644 --- a/src/world.rs +++ b/src/world.rs @@ -57,7 +57,12 @@ impl ActualWorld { self_id: *id, tagged_by: self.state.tagged_by, tagged: self.state.tagged, - bounds_distance: (pos.y, self.size.x - pos.x, self.size.y - pos.y, pos.x), + bounds_distance: ( + pos.y, + self.size.x - pos.x - 1, + self.size.y - pos.y - 1, + pos.x, + ), other_agents: &mut self .state .agent_positions @@ -123,9 +128,9 @@ impl ActualWorld { assert!(dir.y.abs() <= 1); let pos = self.state.agent_positions.get(&id).unwrap(); let size = &self.size; - assert!(pos.x + dir.x > 0); + assert!(pos.x + dir.x >= 0); assert!(pos.x + dir.x < size.x); - assert!(pos.y + dir.y > 0); + assert!(pos.y + dir.y >= 0); assert!(pos.y + dir.y < size.y); } } From 1892f7e9f5b4d90f1b46be9bee16f8e924b08715 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Fri, 23 Jul 2021 15:13:41 +0200 Subject: [PATCH 2/5] Share get_world --- benches/benchmark.rs | 17 +++-------------- src/lib.rs | 13 +++++++++++++ src/main.rs | 11 ++--------- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/benches/benchmark.rs b/benches/benchmark.rs index ce51364..78d990e 100644 --- a/benches/benchmark.rs +++ b/benches/benchmark.rs @@ -1,16 +1,5 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; -use gntag::agent::{Agent, SimpleAgent}; -use gntag::world::ActualWorld; - -fn get_world(width: isize, spacing: usize, validate: bool) -> ActualWorld { - let mut agents = vec![]; - for x in (0..width).step_by(spacing) { - for y in (0..width).step_by(spacing) { - agents.push(((x, y).into(), Box::new(SimpleAgent) as Box)); - } - } - ActualWorld::new((width, width).into(), agents, validate) -} +use gntag::get_world; fn world(c: &mut Criterion) { let mut group = c.benchmark_group("world"); @@ -21,7 +10,7 @@ fn world(c: &mut Criterion) { BenchmarkId::new("validating", spacing), &spacing, |b, &spacing| { - let mut world = get_world(width, spacing as usize, true); + let mut world = get_world(width, width, spacing as usize, true); b.iter(|| world.do_step()); }, ); @@ -29,7 +18,7 @@ fn world(c: &mut Criterion) { BenchmarkId::new("non-validating", spacing), &spacing, |b, &spacing| { - let mut world = get_world(width, spacing as usize, false); + let mut world = get_world(width, width, spacing as usize, false); b.iter(|| world.do_step()); }, ); diff --git a/src/lib.rs b/src/lib.rs index 53a7904..486b5a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,16 @@ pub mod agent; pub mod view; pub mod world; + +use agent::{Agent, SimpleAgent}; +use world::ActualWorld; + +pub fn get_world(width: isize, height: isize, spacing: usize, validating: bool) -> ActualWorld { + let mut agents: Vec<(_, Box)> = vec![]; + for x in (0..width).step_by(spacing) { + for y in (0..height).step_by(spacing) { + agents.push(((x, y).into(), Box::new(SimpleAgent))); + } + } + ActualWorld::new((width, height).into(), agents, validating) +} diff --git a/src/main.rs b/src/main.rs index 8cb1afe..5f38ece 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,5 @@ -use gntag::agent::{Agent, SimpleAgent}; +use gntag::get_world; use gntag::view::{Backend, TerminalView}; -use gntag::world::ActualWorld; use std::io; use std::io::Read; use std::process::exit; @@ -36,13 +35,7 @@ fn run_simulation(view: &Arc>>>) { .unwrap() .content_size() .unwrap(); - let mut agents: Vec<(_, Box)> = vec![]; - for x in (0..width).step_by(10) { - for y in (0..height).step_by(10) { - agents.push(((x, y).into(), Box::new(SimpleAgent))); - } - } - let mut world = ActualWorld::new((width, height).into(), agents, true); + let mut world = get_world(width, height, 10, true); let mut gen = 0; loop { From b0ebb1bb08f3ae122c1809ee6ba92f1490b37f23 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Fri, 23 Jul 2021 15:24:44 +0200 Subject: [PATCH 3/5] Move get_view from main to lib --- src/lib.rs | 27 +++++++++++++++++++++++++++ src/main.rs | 27 +++------------------------ src/view.rs | 7 ++++--- 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 486b5a1..c9f7445 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,12 @@ pub mod view; pub mod world; use agent::{Agent, SimpleAgent}; +use std::io; +use std::io::Read; +use std::process::exit; +use std::sync::{Arc, Mutex}; +use std::thread; +use view::{RawTerminal, TerminalView, TermionBackend}; use world::ActualWorld; pub fn get_world(width: isize, height: isize, spacing: usize, validating: bool) -> ActualWorld { @@ -14,3 +20,24 @@ pub fn get_world(width: isize, height: isize, spacing: usize, validating: bool) } ActualWorld::new((width, height).into(), agents, validating) } + +pub type DefaultView = Arc>>>>>; +pub fn get_view() -> DefaultView { + let view = Arc::new(Mutex::new(Some(TerminalView::try_new().unwrap()))); + + // Exit on q, ESC and Ctrl-C and reset terminal + let view2 = view.clone(); + thread::spawn(move || { + let stdin = io::stdin(); + for byte in stdin.bytes().flatten() { + if byte == b'q' || byte == 0x1b || byte == 0x03 { + if let Ok(mut view) = view2.lock() { + *view = None; // drop view + println!("\n"); + } + exit(0); + } + } + }); + view +} diff --git a/src/main.rs b/src/main.rs index 5f38ece..84c9d82 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,35 +1,14 @@ -use gntag::get_world; -use gntag::view::{Backend, TerminalView}; -use std::io; -use std::io::Read; -use std::process::exit; -use std::sync::{Arc, Mutex}; -use std::thread; +use gntag::{get_view, get_world, DefaultView}; fn main() { - let view = Arc::new(Mutex::new(Some(TerminalView::try_new().unwrap()))); - - // Exit on q, ESC and Ctrl-C and reset terminal - let view2 = view.clone(); - thread::spawn(move || { - let stdin = io::stdin(); - for byte in stdin.bytes().flatten() { - if byte == b'q' || byte == 0x1b || byte == 0x03 { - if let Ok(mut view) = view2.lock() { - *view = None; // drop view - println!("\n"); - } - exit(0); - } - } - }); + let view = get_view(); loop { run_simulation(&view); } } -fn run_simulation(view: &Arc>>>) { +fn run_simulation(view: &DefaultView) { let (width, height) = (*view.lock().unwrap()) .as_mut() .unwrap() diff --git a/src/view.rs b/src/view.rs index 5fb7290..8aa8a21 100644 --- a/src/view.rs +++ b/src/view.rs @@ -2,9 +2,10 @@ use std::convert::TryInto; use std::error::Error; use std::io::{stdout, Stdout, Write}; use termion::clear; -use termion::raw::{IntoRawMode, RawTerminal}; -pub use tui::backend::Backend; -use tui::backend::TermionBackend; +use termion::raw::IntoRawMode; +pub use termion::raw::RawTerminal; +use tui::backend::Backend; +pub use tui::backend::TermionBackend; use tui::layout::{Alignment, Constraint, Direction, Layout, Rect}; use tui::style::{Color, Modifier, Style}; use tui::text::{Span, Spans}; From 2ec11dfd6cefc6d34141bd379bfe9e231ec9d73a Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Fri, 23 Jul 2021 15:30:25 +0200 Subject: [PATCH 4/5] Extract draw_world --- src/lib.rs | 24 ++++++++++++++++++++++++ src/main.rs | 23 ++--------------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c9f7445..6e4f78e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ pub mod view; pub mod world; use agent::{Agent, SimpleAgent}; +use std::error::Error; use std::io; use std::io::Read; use std::process::exit; @@ -41,3 +42,26 @@ pub fn get_view() -> DefaultView { }); view } + +pub fn draw_world( + world: &ActualWorld, + gen: usize, + view: &DefaultView, +) -> Result> { + (*view.lock().unwrap()).as_mut().unwrap().draw( + gen, + world + .state + .agent_positions + .get(&world.state.tagged) + .map(|pos| (pos.x, pos.y)) + .unwrap(), + world + .state + .agent_positions + .iter() + .map(|(_id, pos)| (pos.x, pos.y)) + .collect::>() + .as_ref(), + ) +} diff --git a/src/main.rs b/src/main.rs index 84c9d82..777bc82 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use gntag::{get_view, get_world, DefaultView}; +use gntag::{draw_world, get_view, get_world, DefaultView}; fn main() { let view = get_view(); @@ -18,26 +18,7 @@ fn run_simulation(view: &DefaultView) { let mut gen = 0; loop { - let resized = (*view.lock().unwrap()) - .as_mut() - .unwrap() - .draw( - gen, - world - .state - .agent_positions - .get(&world.state.tagged) - .map(|pos| (pos.x, pos.y)) - .unwrap(), - world - .state - .agent_positions - .iter() - .map(|(_id, pos)| (pos.x, pos.y)) - .collect::>() - .as_ref(), - ) - .unwrap(); + let resized = draw_world(&world, gen, view).unwrap(); if resized { return; }; From 4f2f0431d1db1399e0edd04b1544194772bbf21a Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Fri, 23 Jul 2021 15:33:28 +0200 Subject: [PATCH 5/5] Document usage as a library --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 8d3cd2f..747cf78 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ cargo run To exit the simulation, press q, ESC or Ctrl+c. +## Using as a library + +`src/main.rs` is a pretty minimal example of the currently exposed high-level functionality. `gntag::actor` includes simpler actor iplementations and all the types, `gntag::world` contains `ActualWorld` and `WorldState`. + ## Tests A few tests can be run with: