Initial view
Another two hours.
This commit is contained in:
parent
bfe6f615a0
commit
62c46ce8f9
4 changed files with 171 additions and 10 deletions
|
|
@ -5,3 +5,5 @@ edition = "2018"
|
||||||
authors = ["Adrian Heine <mail@adrianheine.de>"]
|
authors = ["Adrian Heine <mail@adrianheine.de>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
tui = "0.15"
|
||||||
|
termion = "1.5"
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod agent;
|
pub mod agent;
|
||||||
|
pub mod view;
|
||||||
pub mod world;
|
pub mod world;
|
||||||
|
|
|
||||||
46
src/main.rs
46
src/main.rs
|
|
@ -1,16 +1,42 @@
|
||||||
use gntag::agent::{Agent, Position, SimpleAgent};
|
use gntag::agent::{Agent, SimpleAgent};
|
||||||
|
use gntag::view::TerminalView;
|
||||||
use gntag::world::ActualWorld;
|
use gntag::world::ActualWorld;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut agents: Vec<(_, Box<dyn Agent>)> = vec![];
|
let mut view = TerminalView::try_new().unwrap();
|
||||||
for x in 0..5 {
|
loop {
|
||||||
for y in 0..5 {
|
let (width, height) = view.content_size().unwrap();
|
||||||
agents.push(((x, y).into(), Box::new(SimpleAgent)));
|
let mut agents: Vec<(_, Box<dyn Agent>)> = 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);
|
||||||
|
|
||||||
|
let mut gen = 0;
|
||||||
|
loop {
|
||||||
|
let resized = view
|
||||||
|
.draw(
|
||||||
|
gen,
|
||||||
|
world
|
||||||
|
.agent_positions
|
||||||
|
.get(&world.tagged)
|
||||||
|
.map(|pos| (pos.x, pos.y))
|
||||||
|
.unwrap(),
|
||||||
|
world
|
||||||
|
.agent_positions
|
||||||
|
.iter()
|
||||||
|
.map(|(_id, pos)| (pos.x, pos.y))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.as_ref(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
if resized {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
world.do_step();
|
||||||
|
gen += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut world = ActualWorld::new((80, 40).into(), agents);
|
|
||||||
loop {
|
|
||||||
world.do_step();
|
|
||||||
println!("{}", world.tagged);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
132
src/view.rs
Normal file
132
src/view.rs
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io::{stdout, Stdout, Write};
|
||||||
|
use termion::clear;
|
||||||
|
use termion::raw::{IntoRawMode, RawTerminal};
|
||||||
|
use tui::backend::{Backend, TermionBackend};
|
||||||
|
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
|
||||||
|
use tui::style::{Color, Modifier, Style};
|
||||||
|
use tui::text::{Span, Spans};
|
||||||
|
use tui::widgets::canvas::{Canvas, Points};
|
||||||
|
use tui::widgets::{Block, Borders, Paragraph, Wrap};
|
||||||
|
use tui::Terminal;
|
||||||
|
|
||||||
|
pub struct TerminalView<B: Backend> {
|
||||||
|
terminal: Terminal<B>,
|
||||||
|
previous_coords: Vec<(f64, f64)>,
|
||||||
|
inner_size: Rect,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_layout(area: Rect) -> Vec<Rect> {
|
||||||
|
Layout::default()
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.margin(1)
|
||||||
|
.constraints([Constraint::Percentage(80), Constraint::Percentage(20)].as_ref())
|
||||||
|
.split(area)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_block() -> Block<'static> {
|
||||||
|
Block::default().borders(Borders::ALL).title("gntag")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_inner_size(terminal: &mut Terminal<impl Backend>) -> Result<Rect, Box<dyn Error>> {
|
||||||
|
let mut res = None;
|
||||||
|
terminal.draw(|f| {
|
||||||
|
let chunks = get_layout(f.size());
|
||||||
|
res = Some(get_block().inner(chunks[0]));
|
||||||
|
})?;
|
||||||
|
res.ok_or_else(|| "Error getting canvas content size".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TerminalView<TermionBackend<RawTerminal<Stdout>>> {
|
||||||
|
pub fn try_new() -> Result<Self, Box<dyn Error>> {
|
||||||
|
let mut stdout = stdout().into_raw_mode()?;
|
||||||
|
write!(stdout, "{}", clear::All)?;
|
||||||
|
let backend = TermionBackend::new(stdout);
|
||||||
|
let mut terminal = Terminal::new(backend)?;
|
||||||
|
let inner_size = get_inner_size(&mut terminal)?;
|
||||||
|
Ok(Self {
|
||||||
|
terminal,
|
||||||
|
previous_coords: vec![],
|
||||||
|
inner_size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FACTOR: isize = 3;
|
||||||
|
impl<B: Backend> TerminalView<B> {
|
||||||
|
pub fn content_size(&self) -> Result<(isize, isize), Box<dyn Error>> {
|
||||||
|
let size = self.inner_size;
|
||||||
|
let width: isize = size.width.try_into()?;
|
||||||
|
let height: isize = size.height.try_into()?;
|
||||||
|
Ok((width * FACTOR, height * FACTOR))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(
|
||||||
|
&mut self,
|
||||||
|
gen: usize,
|
||||||
|
tagged: (isize, isize),
|
||||||
|
positions: &[(isize, isize)],
|
||||||
|
) -> Result<bool, Box<dyn Error>> {
|
||||||
|
let mut coords = vec![];
|
||||||
|
for (x, y) in positions {
|
||||||
|
coords.push((*x as f64, *y as f64));
|
||||||
|
}
|
||||||
|
let prev_points = Points {
|
||||||
|
coords: &self.previous_coords,
|
||||||
|
color: Color::Black,
|
||||||
|
};
|
||||||
|
let cur_points = Points {
|
||||||
|
coords: &coords,
|
||||||
|
color: Color::White,
|
||||||
|
};
|
||||||
|
let content_size = self.content_size()?;
|
||||||
|
let mut inner_size = self.inner_size;
|
||||||
|
self.terminal.draw(|f| {
|
||||||
|
let chunks = get_layout(f.size());
|
||||||
|
let block = get_block();
|
||||||
|
|
||||||
|
inner_size = block.inner(chunks[0]);
|
||||||
|
let canvas = Canvas::default()
|
||||||
|
.block(block)
|
||||||
|
.paint(|ctx| {
|
||||||
|
ctx.draw(&prev_points);
|
||||||
|
ctx.layer();
|
||||||
|
ctx.draw(&cur_points);
|
||||||
|
ctx.layer();
|
||||||
|
ctx.draw(&Points {
|
||||||
|
coords: &[(tagged.0 as f64, tagged.1 as f64)],
|
||||||
|
color: Color::Yellow,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.background_color(Color::Black)
|
||||||
|
.x_bounds([0.0, content_size.0 as f64])
|
||||||
|
.y_bounds([0.0, content_size.1 as f64]);
|
||||||
|
f.render_widget(canvas, chunks[0]);
|
||||||
|
|
||||||
|
let text = vec![
|
||||||
|
Spans::from(vec![
|
||||||
|
Span::styled(
|
||||||
|
"Generation: ",
|
||||||
|
Style::default().add_modifier(Modifier::BOLD),
|
||||||
|
),
|
||||||
|
Span::raw(gen.to_string()),
|
||||||
|
]),
|
||||||
|
Spans::from(vec![
|
||||||
|
Span::styled("Agents: ", Style::default().add_modifier(Modifier::BOLD)),
|
||||||
|
Span::raw(coords.len().to_string()),
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
let side = Paragraph::new(text)
|
||||||
|
.block(Block::default().title("Info").borders(Borders::ALL))
|
||||||
|
.style(Style::default().fg(Color::White).bg(Color::Black))
|
||||||
|
.alignment(Alignment::Center)
|
||||||
|
.wrap(Wrap { trim: true });
|
||||||
|
f.render_widget(side, chunks[1]);
|
||||||
|
})?;
|
||||||
|
self.previous_coords = coords;
|
||||||
|
let old_inner_size = self.inner_size;
|
||||||
|
self.inner_size = inner_size;
|
||||||
|
Ok(inner_size != old_inner_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue