diff --git a/src/lib/ai.rs b/src/lib/ai.rs index 0aebb09..f13791e 100644 --- a/src/lib/ai.rs +++ b/src/lib/ai.rs @@ -1,75 +1,122 @@ -use crate::lib::entity::{Ant, Egg, Queen}; -use crate::lib::point::{astar, Point}; +use crate::lib::entity::{Ant, Egg, Food, FoodGenerator, Queen}; +use crate::lib::point::Point; use crate::lib::screen::{BoardCommand, Screen}; -use crate::lib::world::World; +use crate::lib::world::{Pheremone, World}; +use rand::Rng; 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 step(&mut self, b: &Screen, w: &mut World, step: u32) -> BoardCommand; + fn plan(&mut self, b: &Screen, w: &mut World) -> BoardCommand { + BoardCommand::Noop + } } #[derive(Clone, Debug)] pub enum AIGoal { - Reach(Point), - //Pickup(Point), - //Drop(), - Idle, + Seek, + 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 { - AIGoal::Reach(target) => { - if self.pos == *target { - self.plan.pop(); + 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) { + for p in &self.history { + w.drop_pheremone(&p, &self.goal); } + self.history.clear(); + self.about_face(); + self.goal = AIGoal::Return; } - AIGoal::Idle => {} + BoardCommand::Noop } - } + AIGoal::Return => { + if w.home.contains(&self.pos) { + for p in &self.history { + w.drop_pheremone(&p, &self.goal); + } + self.history.clear(); + self.about_face(); + self.goal = AIGoal::Seek; + return BoardCommand::SpawnAnt; + } else { + BoardCommand::Noop + } + } + } } - // 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, - }; + fn step(&mut self, b: &Screen, w: &mut World, t: u32) -> BoardCommand { + 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 choice = match goal { - AIGoal::Idle => { - let valid = w.get_valid_movements(&self.pos, b); - let mut rng = thread_rng(); - valid.choose(&mut rng).cloned() - } - AIGoal::Reach(target) => { - let mut movements = astar(&self.pos, &target); - movements.pop() - } + let ph: Vec = valid + .iter() + .map(|(_, pnt)| w.get_pheremone(pnt).clone()) + .collect(); + + let ph_fn = match self.goal { + AIGoal::Seek => |ph: &Pheremone| ph.food, + AIGoal::Return => |ph: &Pheremone| ph.home, }; - if !choice.is_none() { - let pos = choice.unwrap(); - if w.cleared.contains(&pos) { - let old_pos = self.pos; - self.pos = pos; - w.update_occupied(&old_pos, &pos); - } else { - w.clear(pos); + let r: f32 = rand::random(); + + let mut dir = &valid[0].0; + if r < 0.1 || ph.len() == 0 { + let mut rng = thread_rng(); + let choice = valid.choose(&mut rng).unwrap(); + dir = &choice.0; + } else { + 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(b); + } else if dir == &self.dir.cw() { + self.cw(); + } else if dir == &self.dir.ccw() { + self.ccw(); + } + BoardCommand::Noop + } +} + +impl AI for FoodGenerator { + fn step(&mut self, b: &Screen, w: &mut World, t: u32) -> BoardCommand { + // eventually might want to use poisson disk distrib instead + if t % 600 == 0 && self.counter < 10 { + // generate random coords, if valid, spawn + // if not, try again next step + let (min, max) = b.get_dimensions(); + + let mut rng = rand::thread_rng(); + let r_x = rng.gen_range(min.0..max.0 + 1); + let r_y = rng.gen_range(min.1..max.1 + 1); + + self.counter += 1; + return BoardCommand::SpawnFood(Point(r_x, r_y)); + } BoardCommand::Noop } } impl AI for Egg { - fn step(&mut self, b: &Screen, w: &mut World) -> BoardCommand { + fn step(&mut self, b: &Screen, w: &mut World, t: u32) -> BoardCommand { if self.counter > 0 { self.counter -= 1; return BoardCommand::Noop; @@ -80,26 +127,33 @@ impl AI for Egg { } impl AI for Queen { - fn step(&mut self, b: &Screen, w: &mut World) -> BoardCommand { - let valid: Vec = w.get_valid_movements(&self.pos, b); + fn step(&mut self, b: &Screen, w: &mut World, t: u32) -> BoardCommand { + let valid: Vec = 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) { - // choose between laying an egg and moving - if self.egg_count < 3 { - self.egg_count += 1; - return BoardCommand::LayEgg(pos, self.id); - } else { - let old_pos = self.pos; - self.pos = pos; - w.update_occupied(&old_pos, &pos); - } + // choose between laying an egg and moving + if self.egg_count < 3 { + self.egg_count += 1; + return BoardCommand::LayEgg(pos, self.id); + } else { + self.pos = pos; } } BoardCommand::Noop } } + +impl AI for Food { + fn step(&mut self, b: &Screen, w: &mut World, t: u32) -> BoardCommand { + for n in self.pos.get_neighbors() { + w.drop_pheremone(&n, &AIGoal::Return); + } + w.drop_pheremone(&self.pos, &AIGoal::Return); + + BoardCommand::Noop + } +} diff --git a/src/lib/entity.rs b/src/lib/entity.rs index e768176..76b81f1 100644 --- a/src/lib/entity.rs +++ b/src/lib/entity.rs @@ -1,10 +1,8 @@ use crate::lib::ai::{AIGoal, AI}; -use crate::lib::point::Point; -use crate::lib::screen::{BoardCommand, Screen}; -use crate::lib::world::World; -use rand::Rng; +use crate::lib::point::{Direction, Point}; +use crate::lib::screen::Screen; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use ncurses::*; @@ -16,6 +14,7 @@ pub trait Entity: AI + Downcast { fn set_id(&mut self, id: u32); } + macro_rules! impl_entity { ($t: ident) => { impl Entity for $t { @@ -40,7 +39,9 @@ impl_downcast!(Renderable); pub struct Ant { pub pos: Point, pub id: u32, - pub plan: Vec, + pub goal: AIGoal, + pub dir: Direction, + pub history: HashSet, } impl Ant { @@ -48,10 +49,37 @@ 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, s: &Screen) { + let target = self.dir.relative_point(&self.pos); + if s.is_in_bounds(&target) { + self.history.insert(self.pos); + self.pos = 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(); + } + + pub fn about_face(&mut self) { + self.cw(); + self.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, + }; } } @@ -173,7 +214,7 @@ impl Entities { #[derive(Clone)] pub struct Food { - pos: Point, + pub pos: Point, id: u32, } @@ -186,14 +227,6 @@ 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? - BoardCommand::Noop - } -} - impl Renderable for Food { fn representation(&self) -> &str { "f" @@ -203,32 +236,20 @@ impl Renderable for Food { // no position, does not get rendered yet acts per turn #[derive(Clone)] pub struct FoodGenerator { - counter: u32, + pub timer: u32, pos: Point, - id: u32 + id: u32, + pub counter: u32, } impl FoodGenerator { pub fn new() -> FoodGenerator { - FoodGenerator { counter: 0, id: 0, pos: Point(0,0) } - } -} - -impl AI for FoodGenerator { - fn step(&mut self, b: &Screen, w: &mut World) -> BoardCommand { - if self.counter % 600 == 0 { - // generate random coords, if valid, spawn - // if not, try again next step - let (min, max) = b.get_dimensions(); - - let mut rng = rand::thread_rng(); - let r_x = rng.gen_range(min.0..max.0 + 1); - let r_y = rng.gen_range(min.1..max.1 + 1); - - return BoardCommand::SpawnFood(Point(r_x, r_y)); + FoodGenerator { + timer: 0, + id: 0, + pos: Point(0, 0), + counter: 0, } - self.counter += 1; - BoardCommand::Noop } } diff --git a/src/lib/point.rs b/src/lib/point.rs index 084e3c7..d0383b3 100644 --- a/src/lib/point.rs +++ b/src/lib/point.rs @@ -1,89 +1,68 @@ -use std::collections::{HashMap, HashSet}; - #[derive(Hash, PartialEq, Eq, Clone, Debug, Copy)] pub struct Point(pub i32, pub i32); impl Point { pub fn get_neighbors(&self) -> Vec { - vec![ - Point(self.0, self.1 + 1), - Point(self.0, self.1 - 1), - Point(self.0 - 1, self.1), - Point(self.0 + 1, self.1), - ] + vec![self.left(), self.right(), self.up(), self.down()] } -} - -fn manhattan(c1: &Point, c2: &Point) -> i32 { - return (c2.0 - c1.0).abs() + (c2.1 - c1.1).abs(); -} - -pub fn astar(start: &Point, goal: &Point) -> Vec { - let mut open_set : HashSet = HashSet::new(); - open_set.insert(*start); - let mut came_from: HashMap = HashMap::new(); + pub fn left(&self) -> Point { + Point(self.0 - 1, self.1) + } - let mut gscore: HashMap = HashMap::new(); - gscore.insert(*start, 0); + pub fn right(&self) -> Point { + Point(self.0 + 1, self.1) + } - let mut fscore: HashMap = HashMap::new(); - fscore.insert(*start, manhattan(&start, &goal)); + // y values are reversed in ncurses + pub fn down(&self) -> Point { + Point(self.0, self.1 + 1) + } - let mut answer = Vec::new(); + pub fn up(&self) -> Point { + Point(self.0, self.1 - 1) + } +} - 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 }; - } +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum Direction { + Up = 0, + Right = 1, + Down = 2, + Left = 3, +} - 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()); - } - } - break; +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") } + } - open_set.remove(¤t); - - let current_gscore = gscore[¤t]; + pub fn cw(&self) -> Direction { + let val = *self as u8; + Direction::from_u8((val + 1) % 4) + } - let all_neighbors: Vec = current.get_neighbors(); - for neighbor in all_neighbors.iter() { - let neighbor_score = if gscore.contains_key(&neighbor) { - gscore[neighbor] - } else { - i32::MAX - }; - let score = current_gscore + 1; - 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 ccw(&self) -> Direction { + let val = *self as u8; + if val == 0 { + Direction::Left + } else { + Direction::from_u8(val - 1) } } - answer -} - -#[test] -fn test_astar() { - let start = Point(0, 0); - - let goal = Point(10, 10); - let answers: Vec = astar(&start, &goal); - - for a in answers.iter() { - println!("{:?}", &a); + 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(), + } } } diff --git a/src/lib/screen.rs b/src/lib/screen.rs index 8b07159..8a2c57d 100644 --- a/src/lib/screen.rs +++ b/src/lib/screen.rs @@ -1,16 +1,17 @@ use crate::lib::point::Point; +use ncurses::*; pub struct Screen { pub center: Point, - max_x: i32, - max_y: i32, + pub max_x: i32, + pub max_y: i32, } -use ncurses::*; - pub fn init_screen() -> Screen { initscr(); + start_color(); + init_pair(0, 0, 1); /* Invisible cursor. */ curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE); @@ -32,54 +33,22 @@ impl Screen { } pub fn is_in_bounds(&self, pos: &Point) -> bool { - (pos.0 > -self.max_x && pos.0 < self.max_x) && (pos.1 > -self.max_y && pos.1 < self.max_y) - } - - pub fn get_valid_movements(&self, pos: &Point) -> Vec { - let binding = pos.get_neighbors(); - binding - .iter() - .filter(|e| self.is_in_bounds(e)) - .map(|e| e.clone()) - .collect() + (pos.0 > 0 && pos.0 < self.max_x) && (pos.1 > 0 && pos.1 < self.max_y) } pub fn render(&self, p: &Point, char: &str) { - mvprintw(p.1 + self.center.1, p.0 + self.center.0, char); + mvprintw(p.1, p.0, char); } pub fn get_dimensions(&self) -> (Point, Point) { - (Point(-self.max_x, -self.max_y), Point(self.max_x, self.max_y)) + (Point(0,0), Point(self.max_x, self.max_y)) } } -#[test] -fn test_in_bounds() { - let x = 20; - let y = 20; - let s = Screen::new(x, y); - - assert!(&s.is_in_bounds(&Point(0, 0))); - assert!(!&s.is_in_bounds(&Point(21, 0))); -} - -#[test] -fn test_get_valid_movements_board() { - let x = 20; - let y = 20; - let s = Screen::new(x, y); - - let valid = s.get_valid_movements(&Point(0,0)); - assert_eq!(valid, Point(0,0).get_neighbors()); - - let border = s.get_valid_movements(&Point(19,19)); - assert!(border.len() == 2); -} - pub enum BoardCommand { - Dig(Point), LayEgg(Point, u32), SpawnFood(Point), Hatch(u32, u32), + SpawnAnt, Noop, } diff --git a/src/lib/world.rs b/src/lib/world.rs index b851498..2aba20b 100644 --- a/src/lib/world.rs +++ b/src/lib/world.rs @@ -1,50 +1,77 @@ -use crate::lib::screen::{Screen, BoardCommand}; +use crate::lib::ai::AIGoal; +use crate::lib::entity::{Ant, Egg, Entities, Food, Queen}; use crate::lib::point::Point; -use crate::lib::entity::{Entities, Queen, Egg, Ant, Food}; +use crate::lib::screen::{BoardCommand, Screen}; use std::collections::HashSet; +use ncurses::*; + #[derive(Clone)] pub struct World { - pub cleared: HashSet, - pub occupied: HashSet, - pub food: HashSet + pub pheremones: Vec, + pub food: HashSet, + pub home: HashSet, + width: usize, +} + +#[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 { + pub fn new(grid_size: &Point) -> World { + let x = grid_size.0; + let y = grid_size.1; + let ph: Vec = vec![Pheremone::new(); (x+1) as usize * (y+1) as usize]; + World { - cleared: HashSet::new(), - occupied: HashSet::new(), - food: HashSet::new() + pheremones: ph, + food: HashSet::new(), + home: HashSet::new(), // should be a property per colony + width: (y as usize), // width of map for Pheremone calculation } } - pub fn clear(&mut self, pos: Point) { - self.cleared.insert(pos); + pub fn get_mut_pheremone(&mut self, pos: &Point) -> &mut Pheremone { + &mut self.pheremones[((pos.0 as usize) * self.width) + (pos.1 as usize)] } - pub fn is_valid_movement(&self, pos: &Point, b: &Screen) -> bool { - b.is_in_bounds(pos) && !self.occupied.contains(pos) + pub fn get_pheremone(&self, pos: &Point) -> &Pheremone { + &self.pheremones[((pos.0 as usize) * self.width) + (pos.1 as usize)] } - pub fn get_valid_movements(&self, pos: &Point, b: &Screen) -> Vec { - let moves = b.get_valid_movements(pos); - moves - .iter() - .filter(|p| !self.occupied.contains(p)) - .map(|p| p.clone()) - .collect() - } + pub fn drop_pheremone(&mut self, pos: &Point, state: &AIGoal) { + let ph = self.get_mut_pheremone(pos); - pub fn update_occupied(&mut self, old: &Point, new: &Point) { - self.occupied.remove(old); - self.occupied.insert(*new); + match state { + AIGoal::Seek => ph.home += 1, + AIGoal::Return => ph.food += 1, + } } } pub fn render(e: &Entities, w: &World, b: &Screen) { - for c in w.cleared.iter() { - b.render(c, "x"); + erase(); + + for h in w.home.iter() { + b.render(h, "x"); } for a in e.data.values() { @@ -52,20 +79,17 @@ pub fn render(e: &Entities, w: &World, b: &Screen) { } } -pub fn simulate(e: &mut Entities, w: &mut World, b: &mut Screen) { - let cmds: Vec = 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 = e.data.values_mut().map(|a| a.plan(b, w)).collect(); + let mut cmds: Vec = e.data.values_mut().map(|a| a.step(b, w, step)).collect(); + + cmds.extend(plan_cmds); for cmd in cmds { match cmd { - BoardCommand::Dig(pos) => { - w.clear(pos); + BoardCommand::SpawnAnt => { + let ant = Ant::new(b.center.0, b.center.1); + e.add_entity(&ant); } BoardCommand::LayEgg(pos, id) => { let egg = Egg::new(pos.0, pos.1, id); @@ -83,9 +107,17 @@ pub fn simulate(e: &mut Entities, w: &mut World, b: &mut Screen) { } BoardCommand::SpawnFood(pos) => { let food = Food::new(pos.0, pos.1); - e.add_entity(&food); + e.add_entity(&food); + w.food.insert(pos); } BoardCommand::Noop => {} } } + + // decay all pheremones by some amount + if step % 600 == 0 { + for ph in w.pheremones.iter_mut() { + ph.decay(); + } + } } diff --git a/src/main.rs b/src/main.rs index 2bfef4a..f52aa97 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,38 +7,42 @@ 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, Queen}; +use lib::world::{render, simulate, World}; fn main() { - let mut board = init_screen(); - let mut world = World::new(); + let mut screen = init_screen(); + let mut world = World::new(&Point(screen.max_x, screen.max_y)); + world.home.insert(screen.center); let mut entities = Entities::new(); - let q = Queen::new(0,0); - entities.add_entity(&q); - for i in -3..3 { - for j in -3..3 { - world.clear(Point(i, j)); - } + let fg = FoodGenerator::new(); + entities.add_entity(&fg); + + for _ in 0..5 { + let mut a = Ant::new(screen.center.0, screen.center.1); + a.goal = lib::ai::AIGoal::Seek; + entities.add_entity(&a); } + 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); - render(&entities, &world, &board); + simulate(&mut entities, &mut world, &mut screen, t); + render(&entities, &world, &screen); sleep(time::Duration::from_millis(100)); refresh(); + t += 1; } endwin(); } diff --git a/tests/ai_test.rs b/tests/ai_test.rs index 5a97d06..900c2ed 100644 --- a/tests/ai_test.rs +++ b/tests/ai_test.rs @@ -1,8 +1,8 @@ -use antf::lib::screen::{Screen, init_screen}; -use antf::lib::point::Point; use antf::lib::ai::AIGoal; -use antf::lib::world::{World, simulate, render}; -use antf::lib::entity::{Entities, Ant, Food, FoodGenerator}; +use antf::lib::entity::{Ant, Entities, Food, FoodGenerator}; +use antf::lib::point::Point; +use antf::lib::screen::{init_screen, Screen}; +use antf::lib::world::{render, simulate, World}; use ncurses::*; use std::thread::sleep; @@ -12,25 +12,25 @@ use std::time; #[test] fn test_reach_astar() { - let mut board = init_screen();//Screen::new(40,40); - let mut world = World::new(); + let mut board = init_screen(); //Screen::new(40,40); + let mut world = World::new(&Point(board.max_x, board.max_y)); let mut entities = Entities::new(); - - let a = Ant::new(0,0); + + let a = Ant::new(0, 0); let id = entities.add_entity(&a); let a = entities.data.get_mut(&id).unwrap(); - let ant: &mut Ant = a.downcast_mut::().unwrap(); - ant.plan.push(AIGoal::Reach(Point(10,10))); - ant.plan.push(AIGoal::Reach(Point(-10,-10))); - ant.plan.push(AIGoal::Reach(Point(10,-10))); - ant.plan.push(AIGoal::Reach(Point(-10,10))); + let ant: &mut Ant = a.downcast_mut::().unwrap(); + /*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)));*/ // 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(); @@ -41,21 +41,21 @@ fn test_reach_astar() { /*#[test] fn test_drag() { - let mut board = init_screen(); + let mut board = init_screen(); let mut world = World::new(); let mut entities = Entities::new(); - + let f = Food::new(10,10); let a = Ant::new(0,0); let fid = entities.add_entity(&f); let id = entities.add_entity(&a); - + let a = entities.data.get_mut(&id).unwrap(); let ant: &mut Ant = a.downcast_mut::().unwrap(); - //ant.goal = - + //ant.goal = + for _ in 0..60 { // TODO: add way to break out of the loop by hitting a random key simulate(&mut entities, &mut world, &mut board); @@ -64,5 +64,5 @@ fn test_drag() { refresh(); } clear(); - endwin(); + endwin(); }*/ diff --git a/tests/entity_test.rs b/tests/entity_test.rs index 6053ed2..a78207d 100644 --- a/tests/entity_test.rs +++ b/tests/entity_test.rs @@ -1,6 +1,7 @@ use antf::lib::screen::init_screen; use antf::lib::world::{World, simulate, render}; use antf::lib::entity::{Entities, FoodGenerator}; +use antf::lib::point::Point; use ncurses::*; use std::thread::sleep; @@ -9,15 +10,15 @@ use std::time; #[test] fn test_foodgen() { let mut board = init_screen(); - let mut world = World::new(); + let mut world = World::new(&Point(board.max_x, board.max_y)); let mut entities = Entities::new(); 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();