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