sideways #1
+74
-33
@@ -1,67 +1,108 @@
|
||||
use crate::lib::entity::{Ant, Egg, Queen};
|
||||
use crate::lib::point::{astar, Point};
|
||||
use crate::lib::point::{Direction, Point};
|
||||
use crate::lib::screen::{BoardCommand, Screen};
|
||||
use crate::lib::world::World;
|
||||
use crate::lib::world::{Pheremone, World};
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use std::iter::zip;
|
||||
|
||||
pub trait AI {
|
||||
fn step(&mut self, b: &Screen, w: &mut World) -> BoardCommand;
|
||||
fn plan(&mut self, w: &World) {}
|
||||
fn plan(&mut self, b: &Screen, w: &mut World) -> BoardCommand {
|
||||
BoardCommand::Noop
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum AIGoal {
|
||||
Seek,
|
||||
//Pickup(Point),
|
||||
//Drop(),
|
||||
Idle,
|
||||
Return,
|
||||
}
|
||||
|
||||
impl AI for Ant {
|
||||
fn plan(&mut self, w: &World) {
|
||||
// check last part of plan
|
||||
if let Some(goal) = self.plan.last() {
|
||||
match goal {
|
||||
fn plan(&mut self, b: &Screen, w: &mut World) -> BoardCommand {
|
||||
match self.goal {
|
||||
AIGoal::Seek => {
|
||||
// if we reach food, we change state
|
||||
if w.food.contains(&self.pos) {
|
||||
self.plan.pop();
|
||||
for p in &self.history {
|
||||
w.drop_pheremone(&p, &self.goal);
|
||||
}
|
||||
self.history.clear();
|
||||
self.cw();
|
||||
self.cw();
|
||||
self.goal = AIGoal::Return;
|
||||
}
|
||||
BoardCommand::Noop
|
||||
}
|
||||
AIGoal::Return => {
|
||||
if self.pos == b.center {
|
||||
for p in &self.history {
|
||||
w.drop_pheremone(&p, &self.goal);
|
||||
}
|
||||
self.history.clear();
|
||||
self.cw();
|
||||
self.cw();
|
||||
self.goal = AIGoal::Seek;
|
||||
return BoardCommand::SpawnAnt
|
||||
} else {
|
||||
BoardCommand::Noop
|
||||
}
|
||||
AIGoal::Idle => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return the next move for this ant
|
||||
fn step(&mut self, b: &Screen, w: &mut World) -> BoardCommand {
|
||||
let goal = match self.plan.last() {
|
||||
Some(g) => g,
|
||||
None => &AIGoal::Idle,
|
||||
|
||||
let valid = vec![
|
||||
(self.dir, self.dir.relative_point(&self.pos)),
|
||||
(self.dir.ccw(), self.dir.ccw().relative_point(&self.pos)),
|
||||
(self.dir.cw(), self.dir.cw().relative_point(&self.pos)),
|
||||
];
|
||||
|
||||
let ph: Vec<Pheremone> = valid
|
||||
.iter()
|
||||
.map(|(_, pnt)| {
|
||||
let op = w.cleared.get(pnt);
|
||||
match op {
|
||||
Some(ph) => ph.clone(),
|
||||
None => {
|
||||
w.cleared.insert(pnt.clone(), Pheremone::new());
|
||||
w.cleared.get(pnt).unwrap().clone()
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let ph_fn = match self.goal {
|
||||
AIGoal::Seek => |ph: &Pheremone| ph.food,
|
||||
AIGoal::Return => |ph: &Pheremone| ph.home,
|
||||
};
|
||||
|
||||
let valid = w.get_valid_movements(&self.pos, b, true);
|
||||
let r: f32 = rand::random();
|
||||
|
||||
let mut dir = &valid[0].0;
|
||||
if r < 0.2 || ph.len() == 0 {
|
||||
let mut rng = thread_rng();
|
||||
let choice = valid.choose(&mut rng).cloned();
|
||||
|
||||
if !choice.is_none() {
|
||||
let pos = choice.unwrap();
|
||||
if w.cleared.contains(&pos) {
|
||||
self.pos = pos;
|
||||
let choice = valid.choose(&mut rng).unwrap();
|
||||
dir = &choice.0;
|
||||
} else {
|
||||
|
||||
match goal {
|
||||
AIGoal::Seek => {
|
||||
if w.is_safe_to_dig(&pos, &b) {
|
||||
w.clear(pos);
|
||||
}
|
||||
},
|
||||
AIGoal::Idle => {}
|
||||
let mut greatest = &ph[0];
|
||||
for (tup, p) in zip(&valid, &ph) {
|
||||
if ph_fn(p) > ph_fn(greatest) {
|
||||
greatest = p;
|
||||
dir = &tup.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if dir == &self.dir {
|
||||
self.forward(w,b);
|
||||
} else if dir == &self.dir.cw() {
|
||||
self.cw();
|
||||
} else if dir == &self.dir.ccw() {
|
||||
self.ccw();
|
||||
}
|
||||
BoardCommand::Noop
|
||||
}
|
||||
}
|
||||
@@ -79,14 +120,14 @@ impl AI for Egg {
|
||||
|
||||
impl AI for Queen {
|
||||
fn step(&mut self, b: &Screen, w: &mut World) -> BoardCommand {
|
||||
let valid: Vec<Point> = w.get_valid_movements(&self.pos, b, false);
|
||||
let valid: Vec<Point> = self.pos.get_neighbors();
|
||||
let mut rng = thread_rng();
|
||||
|
||||
let choice = valid.choose(&mut rng).cloned();
|
||||
|
||||
if !choice.is_none() {
|
||||
let pos = choice.unwrap();
|
||||
if w.cleared.contains(&pos) {
|
||||
if w.is_cleared(&pos) {
|
||||
// choose between laying an egg and moving
|
||||
if self.egg_count < 3 {
|
||||
self.egg_count += 1;
|
||||
|
||||
+58
-9
@@ -1,10 +1,10 @@
|
||||
use crate::lib::ai::{AIGoal, AI};
|
||||
use crate::lib::point::Point;
|
||||
use crate::lib::point::{Direction, Point};
|
||||
use crate::lib::screen::{BoardCommand, Screen};
|
||||
use crate::lib::world::World;
|
||||
use rand::Rng;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use ncurses::*;
|
||||
|
||||
@@ -40,7 +40,9 @@ impl_downcast!(Renderable);
|
||||
pub struct Ant {
|
||||
pub pos: Point,
|
||||
pub id: u32,
|
||||
pub plan: Vec<AIGoal>,
|
||||
pub goal: AIGoal,
|
||||
pub dir: Direction,
|
||||
pub history: HashSet<Point>,
|
||||
}
|
||||
|
||||
impl Ant {
|
||||
@@ -48,10 +50,36 @@ impl Ant {
|
||||
Ant {
|
||||
pos: Point(x, y),
|
||||
id: 0,
|
||||
plan: vec![],
|
||||
goal: AIGoal::Seek,
|
||||
dir: Direction::Up,
|
||||
history: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn forward(&mut self, w: &mut World, b: &Screen) {
|
||||
let target = self.dir.relative_point(&self.pos);
|
||||
if b.is_in_bounds(&target) {
|
||||
if w.is_cleared(&self.pos) {
|
||||
self.history.insert(self.pos);
|
||||
self.pos = target;
|
||||
} else {
|
||||
w.clear(target);
|
||||
}
|
||||
} else {
|
||||
self.cw();
|
||||
self.cw();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ccw(&mut self) {
|
||||
self.dir = self.dir.ccw();
|
||||
}
|
||||
|
||||
pub fn cw(&mut self) {
|
||||
self.dir = self.dir.cw();
|
||||
}
|
||||
}
|
||||
|
||||
impl_entity!(Ant);
|
||||
impl_entity!(Food);
|
||||
impl_entity!(FoodGenerator);
|
||||
@@ -60,14 +88,27 @@ impl_entity!(Queen);
|
||||
|
||||
impl Renderable for Ant {
|
||||
fn representation(&self) -> &str {
|
||||
"o"
|
||||
match self.dir {
|
||||
Direction::Up => "^",
|
||||
Direction::Down => "v",
|
||||
Direction::Right => ">",
|
||||
Direction::Left => "<",
|
||||
}
|
||||
}
|
||||
fn before_render(&self) {
|
||||
attron(A_BOLD());
|
||||
match self.goal {
|
||||
AIGoal::Return => attron(A_UNDERLINE()),
|
||||
AIGoal::Seek => 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn after_render(&self) {
|
||||
attroff(A_BOLD());
|
||||
match self.goal {
|
||||
AIGoal::Return => attroff(A_UNDERLINE()),
|
||||
AIGoal::Seek => 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,8 +229,11 @@ impl Food {
|
||||
|
||||
impl AI for Food {
|
||||
fn step(&mut self, b: &Screen, w: &mut World) -> BoardCommand {
|
||||
// perhaps check if we're in target?
|
||||
// implement drag logic?
|
||||
for n in self.pos.get_neighbors() {
|
||||
w.drop_pheremone(&n, &AIGoal::Seek);
|
||||
}
|
||||
w.drop_pheremone(&self.pos, &AIGoal::Seek);
|
||||
|
||||
BoardCommand::Noop
|
||||
}
|
||||
}
|
||||
@@ -206,12 +250,17 @@ pub struct FoodGenerator {
|
||||
timer: u32,
|
||||
pos: Point,
|
||||
id: u32,
|
||||
counter: u32
|
||||
counter: u32,
|
||||
}
|
||||
|
||||
impl FoodGenerator {
|
||||
pub fn new() -> FoodGenerator {
|
||||
FoodGenerator { timer: 0, id: 0, pos: Point(0,0), counter: 0 }
|
||||
FoodGenerator {
|
||||
timer: 0,
|
||||
id: 0,
|
||||
pos: Point(0, 0),
|
||||
counter: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+33
-118
@@ -14,29 +14,10 @@ impl Point {
|
||||
Point(self.0 - 1, self.1)
|
||||
}
|
||||
|
||||
pub fn is_below(&self, other: Point) -> bool {
|
||||
other.down() == *self
|
||||
}
|
||||
|
||||
pub fn right(&self) -> Point {
|
||||
Point(self.0 + 1, self.1)
|
||||
}
|
||||
|
||||
pub fn uldiag(&self) -> Point {
|
||||
Point(self.0 - 1, self.1 - 1)
|
||||
}
|
||||
|
||||
pub fn bldiag(&self) -> Point {
|
||||
Point(self.0 + 1, self.1 - 1)
|
||||
}
|
||||
|
||||
pub fn brdiag(&self) -> Point {
|
||||
Point(self.0 + 1, self.1 + 1)
|
||||
}
|
||||
|
||||
pub fn urdiag(&self) -> Point {
|
||||
Point(self.0 - 1, self.1 + 1)
|
||||
}
|
||||
// y values are reversed in ncurses
|
||||
pub fn down(&self) -> Point {
|
||||
Point(self.0, self.1 + 1)
|
||||
@@ -47,111 +28,45 @@ impl Point {
|
||||
}
|
||||
}
|
||||
|
||||
fn manhattan(c1: &Point, c2: &Point) -> i32 {
|
||||
return (c2.0 - c1.0).abs() + (c2.1 - c1.1).abs();
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum Direction {
|
||||
Up = 0,
|
||||
Right = 1,
|
||||
Down = 2,
|
||||
Left = 3,
|
||||
}
|
||||
|
||||
fn weigh_point(w: &World, p: &Point) -> i32 {
|
||||
if w.is_cleared(p) {
|
||||
3
|
||||
impl Direction {
|
||||
pub fn from_u8(v: u8) -> Direction {
|
||||
match v {
|
||||
0 => Direction::Up,
|
||||
1 => Direction::Right,
|
||||
2 => Direction::Down,
|
||||
3 => Direction::Left,
|
||||
_ => panic!("Shouldn't happen")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cw(&self) -> Direction {
|
||||
let val = *self as u8;
|
||||
Direction::from_u8((val + 1) % 4)
|
||||
}
|
||||
|
||||
pub fn ccw(&self) -> Direction {
|
||||
let val = *self as u8;
|
||||
if val == 0 {
|
||||
Direction::Left
|
||||
} else {
|
||||
15
|
||||
Direction::from_u8(val - 1)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PathNotFoundError;
|
||||
|
||||
pub fn astar(start: &Point, goal: &Point, w: Option<&World>, s: Option<&Screen>) -> Result<Vec<Point>, PathNotFoundError> {
|
||||
let mut open_set: HashSet<Point> = HashSet::new();
|
||||
open_set.insert(*start);
|
||||
|
||||
let mut came_from: HashMap<Point, Point> = HashMap::new();
|
||||
|
||||
let mut gscore: HashMap<Point, i32> = HashMap::new();
|
||||
gscore.insert(*start, 0);
|
||||
|
||||
let mut fscore: HashMap<Point, i32> = HashMap::new();
|
||||
fscore.insert(*start, manhattan(&start, &goal));
|
||||
|
||||
let mut answer = Vec::new();
|
||||
|
||||
let mut current: Point = *start;
|
||||
while !open_set.is_empty() {
|
||||
let mut min_score = i32::MAX;
|
||||
for c in open_set.iter() {
|
||||
let val = fscore[c];
|
||||
current = if val < min_score { c.clone() } else { current };
|
||||
min_score = if val < min_score { val } else { min_score };
|
||||
}
|
||||
|
||||
if current == *goal {
|
||||
answer.push(current.clone());
|
||||
while came_from.contains_key(¤t) {
|
||||
current = came_from[¤t].clone();
|
||||
if current != *start {
|
||||
answer.push(current.clone());
|
||||
}
|
||||
}
|
||||
return Ok(answer);
|
||||
}
|
||||
|
||||
open_set.remove(¤t);
|
||||
|
||||
let current_gscore = gscore[¤t];
|
||||
|
||||
let all_neighbors: Vec<Point> = match w {
|
||||
Some(w) => current
|
||||
.get_neighbors()
|
||||
.iter()
|
||||
.filter(|p| w.is_valid_movement(¤t, p, s.unwrap()))
|
||||
.map(|e| e.clone())
|
||||
.collect(),
|
||||
None => current.get_neighbors(),
|
||||
};
|
||||
|
||||
for neighbor in all_neighbors.iter() {
|
||||
let neighbor_score = if gscore.contains_key(&neighbor) {
|
||||
gscore[&neighbor]
|
||||
} else {
|
||||
i32::MAX
|
||||
};
|
||||
|
||||
let weight = match w {
|
||||
Some(w) => weigh_point(&w, neighbor),
|
||||
None => 1,
|
||||
};
|
||||
|
||||
let score = current_gscore + weight;
|
||||
|
||||
if score < neighbor_score {
|
||||
gscore.insert(neighbor.clone(), score);
|
||||
fscore.insert(neighbor.clone(), score + manhattan(&neighbor, &goal));
|
||||
came_from.insert(neighbor.clone(), current.clone());
|
||||
open_set.insert(neighbor.clone());
|
||||
pub fn relative_point(&self, p: &Point) -> Point {
|
||||
match self {
|
||||
Direction::Up => p.up(),
|
||||
Direction::Left => p.left(),
|
||||
Direction::Right => p.right(),
|
||||
Direction::Down => p.down(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(PathNotFoundError)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_astar() {
|
||||
let start = Point(0, 0);
|
||||
|
||||
let goal = Point(10, 10);
|
||||
let answers = astar(&start, &goal, None, None);
|
||||
|
||||
match answers {
|
||||
Ok(ans) => {
|
||||
for a in ans.iter() {
|
||||
println!("{:?}", &a);
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
panic!("Path not found. That shouldn't happen!")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ use ncurses::*;
|
||||
pub fn init_screen() -> Screen {
|
||||
initscr();
|
||||
|
||||
start_color();
|
||||
init_pair(0, 0, 1);
|
||||
/* Invisible cursor. */
|
||||
curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE);
|
||||
|
||||
@@ -81,5 +83,6 @@ pub enum BoardCommand {
|
||||
LayEgg(Point, u32),
|
||||
SpawnFood(Point),
|
||||
Hatch(u32, u32),
|
||||
SpawnAnt,
|
||||
Noop,
|
||||
}
|
||||
|
||||
+63
-65
@@ -1,24 +1,46 @@
|
||||
use crate::lib::ai::AIGoal;
|
||||
use crate::lib::entity::{Ant, Egg, Entities, Food, Queen};
|
||||
use crate::lib::point::{astar, Point};
|
||||
use crate::lib::point::Point;
|
||||
use crate::lib::screen::{BoardCommand, Screen};
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct World {
|
||||
pub cleared: HashSet<Point>,
|
||||
pub cleared: HashMap<Point, Pheremone>,
|
||||
pub food: HashSet<Point>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, PartialOrd, Debug)]
|
||||
pub struct Pheremone {
|
||||
pub home: u32,
|
||||
pub food: u32,
|
||||
}
|
||||
|
||||
impl Pheremone {
|
||||
pub fn new() -> Pheremone {
|
||||
Pheremone { home: 0, food: 0 }
|
||||
}
|
||||
|
||||
pub fn decay(&mut self) {
|
||||
if self.home > 0 {
|
||||
self.home -= 1;
|
||||
}
|
||||
if self.food > 0 {
|
||||
self.food -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl World {
|
||||
pub fn new() -> World {
|
||||
World {
|
||||
cleared: HashSet::new(),
|
||||
cleared: HashMap::new(),
|
||||
food: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self, pos: Point) {
|
||||
self.cleared.insert(pos);
|
||||
self.cleared.insert(pos, Pheremone::new());
|
||||
}
|
||||
|
||||
pub fn create_chamber(&mut self, center: Point, radius: i32) {
|
||||
@@ -32,67 +54,30 @@ impl World {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_valid_movement(&self, current: &Point, target: &Point, b: &Screen) -> bool {
|
||||
// should allow down movements always
|
||||
|
||||
let safe = target.is_below(*current)
|
||||
|| (!self.cleared.contains(&target.down())
|
||||
|| !self.cleared.contains(&target.left())
|
||||
|| !self.cleared.contains(&target.right())
|
||||
|| !self.cleared.contains(&target.bldiag())
|
||||
|| !self.cleared.contains(&target.uldiag())
|
||||
|| !self.cleared.contains(&target.brdiag())
|
||||
|| !self.cleared.contains(&target.urdiag()));
|
||||
|
||||
// of course, its only a valid move if you can walk there!
|
||||
b.is_in_bounds(target) && self.cleared.contains(target) && safe
|
||||
}
|
||||
|
||||
pub fn is_cleared(&self, pos: &Point) -> bool {
|
||||
self.cleared.contains(pos)
|
||||
self.cleared.contains_key(pos)
|
||||
}
|
||||
|
||||
pub fn is_safe_to_dig(&self, target: &Point, b: &Screen) -> bool {
|
||||
let mut hypothetical_world = self.clone();
|
||||
hypothetical_world.clear(*target);
|
||||
let result = astar(target, &b.center, Some(&hypothetical_world), Some(b));
|
||||
b.is_in_bounds(target) && result.is_ok()
|
||||
pub fn drop_pheremone(&mut self, pos: &Point, state: &AIGoal) {
|
||||
let op = self.cleared.get_mut(&pos);
|
||||
|
||||
let ph = match op {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
self.cleared.insert(*pos, Pheremone::new());
|
||||
self.cleared.get_mut(pos).unwrap()
|
||||
}
|
||||
};
|
||||
|
||||
pub fn get_diggable(&self, b: &Screen, pos: &Point) -> Vec<Point> {
|
||||
let moves = b.get_valid_movements(pos);
|
||||
moves
|
||||
.iter()
|
||||
.filter(|p| !self.is_cleared(p))
|
||||
.map(|p| p.clone())
|
||||
.collect()
|
||||
match state {
|
||||
AIGoal::Seek => ph.home += 1,
|
||||
AIGoal::Return => ph.food += 1,
|
||||
}
|
||||
|
||||
pub fn get_valid_movements(
|
||||
&self,
|
||||
pos: &Point,
|
||||
b: &Screen,
|
||||
return_diggables: bool,
|
||||
) -> Vec<Point> {
|
||||
let moves = b.get_valid_movements(pos);
|
||||
let mut ans: Vec<Point> = moves
|
||||
.iter()
|
||||
.filter(|p| self.is_valid_movement(pos, p, b))
|
||||
.map(|p| p.clone())
|
||||
.collect();
|
||||
|
||||
if return_diggables {
|
||||
let digs = self.get_diggable(b, pos);
|
||||
ans.extend(digs);
|
||||
}
|
||||
|
||||
ans
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(e: &Entities, w: &World, b: &Screen) {
|
||||
for c in w.cleared.iter() {
|
||||
for c in w.cleared.keys() {
|
||||
b.render(c, "x");
|
||||
}
|
||||
|
||||
@@ -101,18 +86,19 @@ pub fn render(e: &Entities, w: &World, b: &Screen) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn simulate(e: &mut Entities, w: &mut World, b: &mut Screen) {
|
||||
let cmds: Vec<BoardCommand> = e
|
||||
.data
|
||||
.values_mut()
|
||||
.map(|a| {
|
||||
a.plan(w);
|
||||
a.step(b, w)
|
||||
})
|
||||
.collect();
|
||||
pub fn simulate(e: &mut Entities, w: &mut World, b: &mut Screen, step: u32) {
|
||||
let plan_cmds: Vec<BoardCommand> = e.data.values_mut().map(|a| a.plan(b, w)).collect();
|
||||
|
||||
let mut cmds: Vec<BoardCommand> = e.data.values_mut().map(|a| a.step(b, w)).collect();
|
||||
|
||||
cmds.extend(plan_cmds);
|
||||
|
||||
for cmd in cmds {
|
||||
match cmd {
|
||||
BoardCommand::SpawnAnt => {
|
||||
let ant = Ant::new(b.center.0, b.center.1);
|
||||
e.add_entity(&ant);
|
||||
}
|
||||
BoardCommand::Dig(pos) => {
|
||||
w.clear(pos);
|
||||
}
|
||||
@@ -138,4 +124,16 @@ pub fn simulate(e: &mut Entities, w: &mut World, b: &mut Screen) {
|
||||
BoardCommand::Noop => {}
|
||||
}
|
||||
}
|
||||
|
||||
for n in b.center.get_neighbors() {
|
||||
w.drop_pheremone(&n, &AIGoal::Return);
|
||||
}
|
||||
w.drop_pheremone(&b.center, &AIGoal::Return);
|
||||
|
||||
// decay all pheremones by some amount
|
||||
if step % 60 == 0 {
|
||||
for ph in w.cleared.values_mut() {
|
||||
ph.decay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-6
@@ -7,17 +7,17 @@ use std::thread::sleep;
|
||||
use std::time;
|
||||
|
||||
mod lib {
|
||||
pub mod ai;
|
||||
pub mod entity;
|
||||
pub mod point;
|
||||
pub mod screen;
|
||||
pub mod world;
|
||||
pub mod entity;
|
||||
pub mod ai;
|
||||
}
|
||||
|
||||
use lib::entity::{Ant, Entities, FoodGenerator};
|
||||
use lib::point::Point;
|
||||
use lib::screen::init_screen;
|
||||
use lib::world::{World, simulate, render};
|
||||
use lib::entity::{Entities, Ant, FoodGenerator};
|
||||
use lib::world::{render, simulate, World};
|
||||
|
||||
fn main() {
|
||||
let mut board = init_screen();
|
||||
@@ -31,18 +31,22 @@ fn main() {
|
||||
let fg = FoodGenerator::new();
|
||||
entities.add_entity(&fg);
|
||||
|
||||
for _ in 0..5 {
|
||||
let mut a = Ant::new(board.center.0, board.center.1);
|
||||
a.plan.push(lib::ai::AIGoal::Seek);
|
||||
a.goal = lib::ai::AIGoal::Seek;
|
||||
entities.add_entity(&a);
|
||||
}
|
||||
|
||||
world.create_chamber(Point(board.center.0, board.center.1), 3);
|
||||
|
||||
let mut t = 0;
|
||||
loop {
|
||||
// TODO: add way to break out of the loop by hitting a random key
|
||||
simulate(&mut entities, &mut world, &mut board);
|
||||
simulate(&mut entities, &mut world, &mut board, t);
|
||||
render(&entities, &world, &board);
|
||||
sleep(time::Duration::from_millis(100));
|
||||
refresh();
|
||||
t += 1;
|
||||
}
|
||||
endwin();
|
||||
}
|
||||
|
||||
+4
-4
@@ -23,15 +23,15 @@ fn test_reach_astar() {
|
||||
|
||||
let a = entities.data.get_mut(&id).unwrap();
|
||||
let ant: &mut Ant = a.downcast_mut::<Ant>().unwrap();
|
||||
ant.plan.push(AIGoal::Reach(Point(0, 0)));
|
||||
/*ant.plan.push(AIGoal::Reach(Point(0, 0)));
|
||||
ant.plan.push(AIGoal::Reach(Point(20, 20)));
|
||||
ant.plan.push(AIGoal::Reach(Point(0, 0)));
|
||||
ant.plan.push(AIGoal::Reach(Point(20, 20)));
|
||||
ant.plan.push(AIGoal::Reach(Point(20, 20)));*/
|
||||
|
||||
// craps out... need to make sure unwrap() is safe
|
||||
for _ in 0..420 {
|
||||
for t in 0..420 {
|
||||
// TODO: add way to break out of the loop by hitting a random key
|
||||
simulate(&mut entities, &mut world, &mut board);
|
||||
simulate(&mut entities, &mut world, &mut board, t);
|
||||
render(&entities, &world, &board);
|
||||
sleep(time::Duration::from_millis(100));
|
||||
refresh();
|
||||
|
||||
@@ -15,9 +15,9 @@ fn test_foodgen() {
|
||||
let fg = FoodGenerator::new();
|
||||
entities.add_entity(&fg);
|
||||
|
||||
for _ in 0..60 {
|
||||
for t in 0..60 {
|
||||
// TODO: add way to break out of the loop by hitting a random key
|
||||
simulate(&mut entities, &mut world, &mut board);
|
||||
simulate(&mut entities, &mut world, &mut board, t);
|
||||
render(&entities, &world, &board);
|
||||
sleep(time::Duration::from_millis(100));
|
||||
refresh();
|
||||
|
||||
Reference in New Issue
Block a user