|
|
@ -3,7 +3,7 @@ extern crate ncurses; |
|
|
|
use ncurses::*; |
|
|
|
use ncurses::*; |
|
|
|
use rand::seq::SliceRandom; |
|
|
|
use rand::seq::SliceRandom; |
|
|
|
use rand::thread_rng; |
|
|
|
use rand::thread_rng; |
|
|
|
use std::collections::HashSet; |
|
|
|
use std::collections::{HashMap, HashSet}; |
|
|
|
use std::thread::sleep; |
|
|
|
use std::thread::sleep; |
|
|
|
use std::time; |
|
|
|
use std::time; |
|
|
|
|
|
|
|
|
|
|
@ -50,112 +50,121 @@ impl Board { |
|
|
|
#[derive(Clone)] |
|
|
|
#[derive(Clone)] |
|
|
|
struct World { |
|
|
|
struct World { |
|
|
|
cleared: HashSet<Point>, |
|
|
|
cleared: HashSet<Point>, |
|
|
|
|
|
|
|
occupied: HashSet<Point>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl World { |
|
|
|
impl World { |
|
|
|
fn new() -> World { |
|
|
|
fn new() -> World { |
|
|
|
World { |
|
|
|
World { |
|
|
|
cleared: HashSet::new(), |
|
|
|
cleared: HashSet::new(), |
|
|
|
|
|
|
|
occupied: HashSet::new(), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn clear(&mut self, pos: Point) { |
|
|
|
fn clear(&mut self, pos: Point) { |
|
|
|
self.cleared.insert(pos); |
|
|
|
self.cleared.insert(pos); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn get_valid_movements(&self, pos: &Point, b: &Board) -> Vec<Point> { |
|
|
|
|
|
|
|
let moves = b.get_valid_movements(pos); |
|
|
|
|
|
|
|
moves.iter().filter(|p| !self.occupied.contains(p)).map(|p| p.clone()).collect() |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
trait Entity: AI + Renderable {} |
|
|
|
|
|
|
|
|
|
|
|
struct Colony { |
|
|
|
struct Colony { |
|
|
|
ants: Vec<Box<dyn AI>>, |
|
|
|
ants: HashMap<Point, Box<dyn Entity>>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl Colony { |
|
|
|
impl Colony { |
|
|
|
fn new() -> Colony { |
|
|
|
fn new() -> Colony { |
|
|
|
Colony { ants: vec![] } |
|
|
|
Colony { |
|
|
|
|
|
|
|
ants: HashMap::new(), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn add_ant(&mut self, x: i32, y: i32) { |
|
|
|
|
|
|
|
self.ants.push(Box::new(Ant::new(x, y))); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn add_entity<T: AI + 'static>(&mut self, e: T) { |
|
|
|
fn add_entity<T: Entity + 'static>(&mut self, pos: &Point, e: T) { |
|
|
|
self.ants.push(Box::new(e)); |
|
|
|
self.ants.insert(*pos, Box::new(e)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
trait AI { |
|
|
|
trait AI { |
|
|
|
fn step(&mut self, b: &Board, w: &World) -> BoardCommand; |
|
|
|
fn step(&mut self, pos: &Point, b: &Board, w: &World) -> BoardCommand; |
|
|
|
fn get_position(&self) -> Point; |
|
|
|
|
|
|
|
fn set_position(&mut self, p: Point); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
struct Ant { |
|
|
|
struct Ant {} |
|
|
|
pos: Point, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Ant { |
|
|
|
impl Entity for Ant {} |
|
|
|
fn new(x: i32, y: i32) -> Ant { |
|
|
|
|
|
|
|
Ant { pos: Point(x, y) } |
|
|
|
impl Renderable for Ant { |
|
|
|
|
|
|
|
fn render(&self) -> &str { |
|
|
|
|
|
|
|
"o" |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
struct Queen { |
|
|
|
struct Queen { |
|
|
|
pos: Point, |
|
|
|
|
|
|
|
egg_count: u8, |
|
|
|
egg_count: u8, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl Queen { |
|
|
|
impl Renderable for Queen { |
|
|
|
fn new(x: i32, y: i32) -> Queen { |
|
|
|
fn render(&self) -> &str { |
|
|
|
Queen { |
|
|
|
"q" |
|
|
|
pos: Point(x, y), |
|
|
|
|
|
|
|
egg_count: 0, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
trait Renderable { |
|
|
|
|
|
|
|
fn render(&self) -> &str { |
|
|
|
|
|
|
|
"z" |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Queen { |
|
|
|
|
|
|
|
fn new() -> Queen { |
|
|
|
|
|
|
|
Queen { egg_count: 0 } |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
#[derive(Debug)] |
|
|
|
struct Egg { |
|
|
|
struct Egg { |
|
|
|
pos: Point, |
|
|
|
|
|
|
|
counter: u8, |
|
|
|
counter: u8, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl Egg { |
|
|
|
impl Renderable for Egg { |
|
|
|
fn new(x: i32, y: i32) -> Egg { |
|
|
|
fn render(&self) -> &str { |
|
|
|
Egg { |
|
|
|
"e" |
|
|
|
pos: Point(x, y), |
|
|
|
|
|
|
|
counter: 10, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Egg { |
|
|
|
|
|
|
|
fn new() -> Egg { |
|
|
|
|
|
|
|
Egg { counter: 10 } |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl AI for Egg { |
|
|
|
impl AI for Egg { |
|
|
|
fn step(&mut self, b: &Board, w: &World) -> BoardCommand { |
|
|
|
fn step(&mut self, p: &Point, b: &Board, w: &World) -> BoardCommand { |
|
|
|
if self.counter > 0 { |
|
|
|
if self.counter > 0 { |
|
|
|
self.counter -= 1; |
|
|
|
self.counter -= 1; |
|
|
|
return BoardCommand::Noop; |
|
|
|
return BoardCommand::Noop; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
BoardCommand::Hatch |
|
|
|
BoardCommand::Hatch(*p) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
fn get_position(&self) -> Point { |
|
|
|
|
|
|
|
self.pos.clone() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
fn set_position(&mut self, p: Point) { |
|
|
|
|
|
|
|
self.pos = p; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
enum BoardCommand<'a> { |
|
|
|
enum BoardCommand { |
|
|
|
Move(Box<&'a mut dyn AI>, Point), |
|
|
|
Move(Point, Point), |
|
|
|
Dig(Point), |
|
|
|
Dig(Point), |
|
|
|
LayEgg(Point), |
|
|
|
LayEgg(Point), |
|
|
|
Hatch, |
|
|
|
Hatch(Point), |
|
|
|
Noop, |
|
|
|
Noop, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl AI for Ant { |
|
|
|
impl AI for Ant { |
|
|
|
// return the next move for this ant
|
|
|
|
// return the next move for this ant
|
|
|
|
fn step(&mut self, b: &Board, w: &World) -> BoardCommand { |
|
|
|
fn step(&mut self, p: &Point, b: &Board, w: &World) -> BoardCommand { |
|
|
|
let valid = b.get_valid_movements(&self.pos); |
|
|
|
let valid = w.get_valid_movements(p, b); |
|
|
|
let mut rng = thread_rng(); |
|
|
|
let mut rng = thread_rng(); |
|
|
|
|
|
|
|
|
|
|
|
let choice = valid.choose(&mut rng).cloned(); |
|
|
|
let choice = valid.choose(&mut rng).cloned(); |
|
|
@ -165,25 +174,20 @@ impl AI for Ant { |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
let pos = choice.unwrap(); |
|
|
|
let pos = choice.unwrap(); |
|
|
|
if w.cleared.contains(&pos) { |
|
|
|
if w.cleared.contains(&pos) { |
|
|
|
return BoardCommand::Move(Box::new(self), pos); |
|
|
|
return BoardCommand::Move(*p, pos); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
return BoardCommand::Dig(pos); |
|
|
|
return BoardCommand::Dig(pos); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn get_position(&self) -> Point { |
|
|
|
|
|
|
|
self.pos.clone() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn set_position(&mut self, p: Point) { |
|
|
|
|
|
|
|
self.pos = p |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Entity for Queen {} |
|
|
|
|
|
|
|
impl Entity for Egg {} |
|
|
|
|
|
|
|
|
|
|
|
impl AI for Queen { |
|
|
|
impl AI for Queen { |
|
|
|
fn step(&mut self, b: &Board, w: &World) -> BoardCommand { |
|
|
|
fn step(&mut self, p: &Point, b: &Board, w: &World) -> BoardCommand { |
|
|
|
let valid = b.get_valid_movements(&self.pos); |
|
|
|
let valid: Vec<Point> = w.get_valid_movements(p, b); |
|
|
|
let mut rng = thread_rng(); |
|
|
|
let mut rng = thread_rng(); |
|
|
|
let choice = valid.choose(&mut rng).cloned(); |
|
|
|
let choice = valid.choose(&mut rng).cloned(); |
|
|
|
|
|
|
|
|
|
|
@ -194,24 +198,16 @@ impl AI for Queen { |
|
|
|
if w.cleared.contains(&pos) { |
|
|
|
if w.cleared.contains(&pos) { |
|
|
|
// choose between laying an egg and moving
|
|
|
|
// choose between laying an egg and moving
|
|
|
|
if self.egg_count < 3 { |
|
|
|
if self.egg_count < 3 { |
|
|
|
self.egg_count -= 1; |
|
|
|
self.egg_count += 1; |
|
|
|
return BoardCommand::LayEgg(pos); |
|
|
|
return BoardCommand::LayEgg(pos); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
return BoardCommand::Move(Box::new(self), pos); |
|
|
|
return BoardCommand::Move(*p, pos); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
return BoardCommand::Noop; |
|
|
|
return BoardCommand::Noop; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn get_position(&self) -> Point { |
|
|
|
|
|
|
|
self.pos.clone() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn set_position(&mut self, p: Point) { |
|
|
|
|
|
|
|
self.pos = p |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn render(c: &Colony, w: &World, b: &Board) { |
|
|
|
fn render(c: &Colony, w: &World, b: &Board) { |
|
|
@ -219,9 +215,8 @@ fn render(c: &Colony, w: &World, b: &Board) { |
|
|
|
mvprintw(c.1 + b.center.1, c.0 + b.center.0, "x"); |
|
|
|
mvprintw(c.1 + b.center.1, c.0 + b.center.0, "x"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for a in c.ants.iter() { |
|
|
|
for (pos, a) in c.ants.iter() { |
|
|
|
let pos = a.get_position(); |
|
|
|
mvprintw(pos.1 + b.center.1, pos.0 + b.center.0, a.render()); |
|
|
|
mvprintw(pos.1 + b.center.1, pos.0 + b.center.0, "o"); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -240,33 +235,39 @@ mod tests { |
|
|
|
|
|
|
|
|
|
|
|
dbg!(board.is_in_bounds(&Point(0, 0))); |
|
|
|
dbg!(board.is_in_bounds(&Point(0, 0))); |
|
|
|
dbg!(board.get_valid_movements(&Point(0, 0))); |
|
|
|
dbg!(board.get_valid_movements(&Point(0, 0))); |
|
|
|
|
|
|
|
|
|
|
|
let mut ant = Ant { pos: Point(0, 0) }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
use std::iter::zip; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn simulate(c: &mut Colony, w: &mut World, b: &mut Board) { |
|
|
|
fn simulate(c: &mut Colony, w: &mut World, b: &mut Board) { |
|
|
|
let world = w.clone(); |
|
|
|
let cmds: Vec<BoardCommand> = c |
|
|
|
let cmds: Vec<BoardCommand> = c.ants.iter_mut().map(|a| a.step(b, &world)).collect(); |
|
|
|
.ants |
|
|
|
|
|
|
|
.iter_mut() |
|
|
|
|
|
|
|
.map(|(p, a)| {
|
|
|
|
|
|
|
|
a.step(&p, b, &w) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.collect(); |
|
|
|
|
|
|
|
|
|
|
|
for cmd in cmds { |
|
|
|
for cmd in cmds { |
|
|
|
match cmd { |
|
|
|
match cmd { |
|
|
|
BoardCommand::Move(a, pos) => { |
|
|
|
BoardCommand::Move(old_pos, pos) => { |
|
|
|
a.set_position(pos); |
|
|
|
let a = c.ants.remove(&old_pos).unwrap(); |
|
|
|
|
|
|
|
c.ants.insert(pos, a); |
|
|
|
} |
|
|
|
} |
|
|
|
BoardCommand::Dig(pos) => { |
|
|
|
BoardCommand::Dig(pos) => { |
|
|
|
w.clear(pos); |
|
|
|
w.clear(pos); |
|
|
|
} |
|
|
|
} |
|
|
|
BoardCommand::LayEgg(pos) => { |
|
|
|
BoardCommand::LayEgg(pos) => { |
|
|
|
//c.add_entity(Egg::new(pos.0, pos.1));
|
|
|
|
c.add_entity(&pos, Egg::new()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
BoardCommand::Hatch(pos) => { |
|
|
|
|
|
|
|
c.ants.remove(&pos); |
|
|
|
|
|
|
|
c.add_entity(&pos, Ant {}); |
|
|
|
} |
|
|
|
} |
|
|
|
BoardCommand::Hatch => {} |
|
|
|
|
|
|
|
BoardCommand::Noop => {} |
|
|
|
BoardCommand::Noop => {} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
w.occupied.clear(); |
|
|
|
|
|
|
|
let _ = c.ants.keys().map(|p| w.occupied.insert(*p)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn main() { |
|
|
|
fn main() { |
|
|
@ -286,8 +287,13 @@ fn main() { |
|
|
|
let mut world = World::new(); |
|
|
|
let mut world = World::new(); |
|
|
|
let mut colony = Colony::new(); |
|
|
|
let mut colony = Colony::new(); |
|
|
|
|
|
|
|
|
|
|
|
colony.add_ant(0,0); |
|
|
|
for i in -3..3 { |
|
|
|
|
|
|
|
for j in -3..3 { |
|
|
|
|
|
|
|
world.clear(Point(i, j)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
colony.add_entity(&Point(0, 0), Queen::new()); |
|
|
|
|
|
|
|
|
|
|
|
loop { |
|
|
|
loop { |
|
|
|
// TODO: add way to break out of the loop by hitting a random key
|
|
|
|
// TODO: add way to break out of the loop by hitting a random key
|
|
|
|