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: 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/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/lib.rs b/src/lib.rs index 53a7904..6e4f78e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,67 @@ pub mod agent; 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; +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 { + 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) +} + +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 +} + +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 8cb1afe..777bc82 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,71 +1,24 @@ -use gntag::agent::{Agent, SimpleAgent}; -use gntag::view::{Backend, TerminalView}; -use gntag::world::ActualWorld; -use std::io; -use std::io::Read; -use std::process::exit; -use std::sync::{Arc, Mutex}; -use std::thread; +use gntag::{draw_world, 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() .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 { - 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; }; 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}; 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); } }