cleaned up some api impl, added food + foodgen
This commit is contained in:
+6
-18
@@ -7,20 +7,16 @@ use rand::thread_rng;
|
||||
|
||||
pub trait AI {
|
||||
fn step(&mut self, b: &Screen, w: &mut World) -> BoardCommand;
|
||||
fn get_position(&self) -> Point;
|
||||
fn plan(&mut self, w: &World) {}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum AIGoal {
|
||||
Reach(Point),
|
||||
Idle,
|
||||
}
|
||||
|
||||
impl AI for Ant {
|
||||
fn get_position(&self) -> Point {
|
||||
self.pos.clone()
|
||||
}
|
||||
|
||||
fn plan(&mut self, w: &World) {
|
||||
if self.plan.len() == 0 {
|
||||
self.plan = match self.goal {
|
||||
@@ -31,7 +27,7 @@ impl AI for Ant {
|
||||
} else {
|
||||
astar(&self.pos, &target)
|
||||
}
|
||||
},
|
||||
}
|
||||
AIGoal::Idle => vec![],
|
||||
}
|
||||
}
|
||||
@@ -41,16 +37,15 @@ impl AI for Ant {
|
||||
fn step(&mut self, b: &Screen, w: &mut World) -> BoardCommand {
|
||||
let choice = match self.goal {
|
||||
AIGoal::Idle => {
|
||||
let valid = w.get_valid_movements(&self.pos, b);
|
||||
let mut rng = thread_rng();
|
||||
valid.choose(&mut rng).cloned()
|
||||
},
|
||||
let valid = w.get_valid_movements(&self.pos, b);
|
||||
let mut rng = thread_rng();
|
||||
valid.choose(&mut rng).cloned()
|
||||
}
|
||||
AIGoal::Reach(_) => {
|
||||
let movement = self.plan.pop();
|
||||
movement
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if !choice.is_none() {
|
||||
let pos = choice.unwrap();
|
||||
@@ -83,16 +78,9 @@ impl AI for Egg {
|
||||
BoardCommand::Hatch(self.id, self.queen_id)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_position(&self) -> Point {
|
||||
self.pos.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl AI for Queen {
|
||||
fn get_position(&self) -> Point {
|
||||
self.pos.clone()
|
||||
}
|
||||
fn step(&mut self, b: &Screen, w: &mut World) -> BoardCommand {
|
||||
let valid: Vec<Point> = w.get_valid_movements(&self.pos, b);
|
||||
let mut rng = thread_rng();
|
||||
|
||||
+122
-43
@@ -1,6 +1,8 @@
|
||||
use crate::lib::ai::{AI, AIGoal};
|
||||
use crate::lib::ai::{AIGoal, AI};
|
||||
use crate::lib::point::Point;
|
||||
use crate::lib::screen::Screen;
|
||||
use crate::lib::screen::{BoardCommand, Screen};
|
||||
use crate::lib::world::World;
|
||||
use rand::Rng;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -8,28 +10,55 @@ use ncurses::*;
|
||||
|
||||
use downcast_rs::{impl_downcast, Downcast};
|
||||
|
||||
pub trait Entity: AI + Renderable + Downcast {}
|
||||
impl_downcast!(Entity);
|
||||
pub trait Entity: AI + Downcast {
|
||||
fn get_position(&self) -> Point;
|
||||
fn get_id(&self) -> u32;
|
||||
fn set_id(&mut self, id: u32);
|
||||
}
|
||||
|
||||
macro_rules! impl_entity {
|
||||
($t: ident) => {
|
||||
impl Entity for $t {
|
||||
fn get_position(&self) -> Point {
|
||||
self.pos.clone()
|
||||
}
|
||||
|
||||
fn get_id(&self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn set_id(&mut self, id: u32) {
|
||||
self.id = id;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_downcast!(Renderable);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Ant {
|
||||
pub pos: Point,
|
||||
pub id: u32,
|
||||
pub goal: AIGoal,
|
||||
pub plan: Vec<Point>
|
||||
pub plan: Vec<Point>,
|
||||
}
|
||||
|
||||
impl Ant {
|
||||
pub fn new(x: i32, y: i32, id: u32) -> Ant {
|
||||
pub fn new(x: i32, y: i32) -> Ant {
|
||||
Ant {
|
||||
pos: Point(x, y),
|
||||
id,
|
||||
plan: vec![],
|
||||
goal: AIGoal::Idle
|
||||
id: 0,
|
||||
plan: vec![],
|
||||
goal: AIGoal::Idle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for Ant {}
|
||||
impl_entity!(Ant);
|
||||
impl_entity!(Food);
|
||||
impl_entity!(FoodGenerator);
|
||||
impl_entity!(Egg);
|
||||
impl_entity!(Queen);
|
||||
|
||||
impl Renderable for Ant {
|
||||
fn representation(&self) -> &str {
|
||||
@@ -44,6 +73,7 @@ impl Renderable for Ant {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Queen {
|
||||
pub pos: Point,
|
||||
pub egg_count: u8,
|
||||
@@ -64,8 +94,10 @@ impl Renderable for Queen {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Renderable: AI {
|
||||
fn representation(&self) -> &str;
|
||||
pub trait Renderable: Entity {
|
||||
fn representation(&self) -> &str {
|
||||
"X"
|
||||
}
|
||||
fn before_render(&self) {}
|
||||
fn after_render(&self) {}
|
||||
fn render(&self, b: &Screen) {
|
||||
@@ -76,16 +108,16 @@ pub trait Renderable: AI {
|
||||
}
|
||||
|
||||
impl Queen {
|
||||
pub fn new(x: i32, y: i32, id: u32) -> Queen {
|
||||
pub fn new(x: i32, y: i32) -> Queen {
|
||||
Queen {
|
||||
pos: Point(x, y),
|
||||
egg_count: 0,
|
||||
id,
|
||||
id: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Egg {
|
||||
pub pos: Point,
|
||||
pub counter: u8,
|
||||
@@ -108,21 +140,18 @@ impl Renderable for Egg {
|
||||
}
|
||||
|
||||
impl Egg {
|
||||
pub fn new(x: i32, y: i32, id: u32, queen_id: u32) -> Egg {
|
||||
pub fn new(x: i32, y: i32, queen_id: u32) -> Egg {
|
||||
Egg {
|
||||
pos: Point(x, y),
|
||||
counter: 10,
|
||||
id,
|
||||
id: 0,
|
||||
queen_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for Queen {}
|
||||
impl Entity for Egg {}
|
||||
|
||||
pub struct Entities {
|
||||
pub data: HashMap<u32, Box<dyn Entity>>,
|
||||
pub data: HashMap<u32, Box<dyn Renderable>>,
|
||||
id_counter: u32,
|
||||
}
|
||||
|
||||
@@ -134,27 +163,77 @@ impl Entities {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_ant(&mut self, x: i32, y: i32) -> u32 {
|
||||
let a = Ant::new(x, y, self.id_counter);
|
||||
let id = a.id;
|
||||
self.data.insert(a.id, Box::new(a));
|
||||
pub fn add_entity<T: Renderable + Clone>(&mut self, e: &T) -> u32 {
|
||||
let mut clone = e.clone();
|
||||
clone.set_id(self.id_counter);
|
||||
let id = clone.get_id();
|
||||
self.data.insert(id, Box::new(clone));
|
||||
self.id_counter += 1;
|
||||
id
|
||||
}
|
||||
|
||||
pub fn add_queen(&mut self, x: i32, y: i32) -> u32 {
|
||||
let q = Queen::new(x, y, self.id_counter);
|
||||
let id = q.id;
|
||||
self.data.insert(q.id, Box::new(q));
|
||||
self.id_counter += 1;
|
||||
id
|
||||
}
|
||||
|
||||
pub fn add_egg(&mut self, x: i32, y: i32, queen_id: u32) -> u32 {
|
||||
let e = Egg::new(x, y, self.id_counter, queen_id);
|
||||
let id = e.id;
|
||||
self.data.insert(e.id, Box::new(e));
|
||||
self.id_counter += 1;
|
||||
id
|
||||
e.get_id()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Food {
|
||||
pos: Point,
|
||||
id: u32,
|
||||
}
|
||||
|
||||
impl Food {
|
||||
pub fn new(x: i32, y: i32) -> Food {
|
||||
Food {
|
||||
pos: Point(x, y),
|
||||
id: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
// no position, does not get rendered yet acts per turn
|
||||
#[derive(Clone)]
|
||||
pub struct FoodGenerator {
|
||||
counter: u32,
|
||||
pos: Point,
|
||||
id: 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));
|
||||
}
|
||||
self.counter += 1;
|
||||
BoardCommand::Noop
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderable for FoodGenerator {
|
||||
fn render(&self, b: &Screen) {}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,10 @@ impl Screen {
|
||||
pub fn render(&self, p: &Point, char: &str) {
|
||||
mvprintw(p.1 + self.center.1, p.0 + self.center.0, char);
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (Point, Point) {
|
||||
(Point(-self.max_x, -self.max_y), Point(self.max_x, self.max_y))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -75,6 +79,7 @@ fn test_get_valid_movements_board() {
|
||||
pub enum BoardCommand {
|
||||
Dig(Point),
|
||||
LayEgg(Point, u32),
|
||||
SpawnFood(Point),
|
||||
Hatch(u32, u32),
|
||||
Noop,
|
||||
}
|
||||
|
||||
+11
-3
@@ -1,12 +1,13 @@
|
||||
use crate::lib::screen::{Screen, BoardCommand};
|
||||
use crate::lib::point::Point;
|
||||
use crate::lib::entity::{Entities, Queen};
|
||||
use crate::lib::entity::{Entities, Queen, Egg, Ant, Food};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct World {
|
||||
pub cleared: HashSet<Point>,
|
||||
pub occupied: HashSet<Point>,
|
||||
pub food: HashSet<u32>
|
||||
}
|
||||
|
||||
impl World {
|
||||
@@ -14,6 +15,7 @@ impl World {
|
||||
World {
|
||||
cleared: HashSet::new(),
|
||||
occupied: HashSet::new(),
|
||||
food: HashSet::new()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +68,8 @@ pub fn simulate(e: &mut Entities, w: &mut World, b: &mut Screen) {
|
||||
w.clear(pos);
|
||||
}
|
||||
BoardCommand::LayEgg(pos, id) => {
|
||||
e.add_egg(pos.0, pos.1, id);
|
||||
let egg = Egg::new(pos.0, pos.1, id);
|
||||
e.add_entity(&egg);
|
||||
}
|
||||
BoardCommand::Hatch(egg_id, queen_id) => {
|
||||
let egg = e.data.remove(&egg_id);
|
||||
@@ -75,7 +78,12 @@ pub fn simulate(e: &mut Entities, w: &mut World, b: &mut Screen) {
|
||||
let q = e.data.get_mut(&queen_id).unwrap();
|
||||
let queen: &mut Queen = q.downcast_mut::<Queen>().unwrap();
|
||||
queen.egg_count -= 1;
|
||||
e.add_ant(pos.0, pos.1);
|
||||
let ant = Ant::new(pos.0, pos.1);
|
||||
e.add_entity(&ant);
|
||||
}
|
||||
BoardCommand::SpawnFood(pos) => {
|
||||
let food = Food::new(pos.0, pos.1);
|
||||
e.add_entity(&food);
|
||||
}
|
||||
BoardCommand::Noop => {}
|
||||
}
|
||||
|
||||
+3
-2
@@ -17,14 +17,15 @@ mod lib {
|
||||
use lib::point::Point;
|
||||
use lib::screen::init_screen;
|
||||
use lib::world::{World, simulate, render};
|
||||
use lib::entity::Entities;
|
||||
use lib::entity::{Entities, Queen};
|
||||
|
||||
fn main() {
|
||||
let mut board = init_screen();
|
||||
let mut world = World::new();
|
||||
|
||||
let mut entities = Entities::new();
|
||||
entities.add_queen(0,0);
|
||||
let q = Queen::new(0,0);
|
||||
entities.add_entity(&q);
|
||||
|
||||
for i in -3..3 {
|
||||
for j in -3..3 {
|
||||
|
||||
+27
-3
@@ -2,19 +2,23 @@ use antf::lib::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};
|
||||
use antf::lib::entity::{Entities, Ant, FoodGenerator};
|
||||
|
||||
use ncurses::*;
|
||||
use std::thread::sleep;
|
||||
use std::time;
|
||||
|
||||
// make sure to run with --test-threads 1! otherwise output will be bugged
|
||||
|
||||
#[test]
|
||||
fn test_astar() {
|
||||
fn test_reach_astar() {
|
||||
let mut board = init_screen();
|
||||
let mut world = World::new();
|
||||
|
||||
let mut entities = Entities::new();
|
||||
let id = entities.add_ant(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::<Ant>().unwrap();
|
||||
@@ -27,6 +31,26 @@ fn test_astar() {
|
||||
sleep(time::Duration::from_millis(100));
|
||||
refresh();
|
||||
}
|
||||
clear();
|
||||
endwin();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_foodgen() {
|
||||
let mut board = init_screen();
|
||||
let mut world = World::new();
|
||||
|
||||
let mut entities = Entities::new();
|
||||
let fg = FoodGenerator::new();
|
||||
entities.add_entity(&fg);
|
||||
|
||||
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);
|
||||
render(&entities, &world, &board);
|
||||
sleep(time::Duration::from_millis(100));
|
||||
refresh();
|
||||
}
|
||||
clear();
|
||||
endwin();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user