4 changed files with 171 additions and 10 deletions
			
			
		- 
					2Cargo.toml
 - 
					1src/lib.rs
 - 
					46src/main.rs
 - 
					132src/view.rs
 
@ -1,2 +1,3 @@ | 
				
			|||
pub mod agent;
 | 
				
			|||
pub mod view;
 | 
				
			|||
pub mod world;
 | 
				
			|||
@ -1,16 +1,42 @@ | 
				
			|||
use gntag::agent::{Agent, Position, SimpleAgent};
 | 
				
			|||
use gntag::agent::{Agent, SimpleAgent};
 | 
				
			|||
use gntag::view::TerminalView;
 | 
				
			|||
use gntag::world::ActualWorld;
 | 
				
			|||
 | 
				
			|||
fn main() {
 | 
				
			|||
  let mut agents: Vec<(_, Box<dyn Agent>)> = vec![];
 | 
				
			|||
  for x in 0..5 {
 | 
				
			|||
    for y in 0..5 {
 | 
				
			|||
      agents.push(((x, y).into(), Box::new(SimpleAgent)));
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
  let mut world = ActualWorld::new((80, 40).into(), agents);
 | 
				
			|||
  let mut view = TerminalView::try_new().unwrap();
 | 
				
			|||
  loop {
 | 
				
			|||
    world.do_step();
 | 
				
			|||
    println!("{}", world.tagged);
 | 
				
			|||
    let (width, height) = view.content_size().unwrap();
 | 
				
			|||
    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;
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
@ -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)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
						Write
						Preview
					
					
					Loading…
					
					Cancel
						Save
					
		Reference in new issue