sideways #1
+13
-4
@@ -35,7 +35,6 @@ impl AI for Ant {
|
|||||||
|
|
||||||
// return the next move for this ant
|
// return the next move for this ant
|
||||||
fn step(&mut self, b: &Screen, w: &mut World) -> BoardCommand {
|
fn step(&mut self, b: &Screen, w: &mut World) -> BoardCommand {
|
||||||
|
|
||||||
let goal = match self.plan.last() {
|
let goal = match self.plan.last() {
|
||||||
Some(g) => g,
|
Some(g) => g,
|
||||||
None => &AIGoal::Idle,
|
None => &AIGoal::Idle,
|
||||||
@@ -43,12 +42,18 @@ impl AI for Ant {
|
|||||||
|
|
||||||
let choice = match goal {
|
let choice = match goal {
|
||||||
AIGoal::Idle => {
|
AIGoal::Idle => {
|
||||||
let valid = w.get_valid_movements(&self.pos, b);
|
// valid_movements does not return any diggables!
|
||||||
|
let valid = w.get_valid_movements(&self.pos, b, true);
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
valid.choose(&mut rng).cloned()
|
valid.choose(&mut rng).cloned()
|
||||||
}
|
}
|
||||||
AIGoal::Reach(target) => {
|
AIGoal::Reach(target) => {
|
||||||
let mut movements = astar(&self.pos, &target);
|
// here astar only produces a path between the target & pos without digging
|
||||||
|
let result = astar(&self.pos, &target, Some(w), Some(b));
|
||||||
|
let mut movements = match result {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(_) => vec![]
|
||||||
|
};
|
||||||
movements.pop()
|
movements.pop()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -57,6 +62,10 @@ impl AI for Ant {
|
|||||||
let pos = choice.unwrap();
|
let pos = choice.unwrap();
|
||||||
if w.cleared.contains(&pos) {
|
if w.cleared.contains(&pos) {
|
||||||
self.pos = pos;
|
self.pos = pos;
|
||||||
|
} else {
|
||||||
|
if w.is_safe_to_dig(&self.pos, &pos, &b) {
|
||||||
|
w.clear(pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +86,7 @@ impl AI for Egg {
|
|||||||
|
|
||||||
impl AI for Queen {
|
impl AI for Queen {
|
||||||
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, false);
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
let choice = valid.choose(&mut rng).cloned();
|
let choice = valid.choose(&mut rng).cloned();
|
||||||
|
|||||||
+63
-36
@@ -1,3 +1,5 @@
|
|||||||
|
use crate::lib::screen::Screen;
|
||||||
|
use crate::lib::world::World;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
#[derive(Hash, PartialEq, Eq, Clone, Debug, Copy)]
|
#[derive(Hash, PartialEq, Eq, Clone, Debug, Copy)]
|
||||||
@@ -5,32 +7,7 @@ pub struct Point(pub i32, pub i32);
|
|||||||
|
|
||||||
impl Point {
|
impl Point {
|
||||||
pub fn get_neighbors(&self) -> Vec<Point> {
|
pub fn get_neighbors(&self) -> Vec<Point> {
|
||||||
vec![
|
vec![self.left(), self.right(), self.up(), self.down()]
|
||||||
self.left(),
|
|
||||||
self.right(),
|
|
||||||
self.up(),
|
|
||||||
self.down()
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_above(&self, other: &Point) -> bool {
|
|
||||||
other.up() == *self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_below(&self, other: &Point) -> bool {
|
|
||||||
other.down() == *self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_left(&self, other: &Point) -> bool {
|
|
||||||
other.left() == *self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_right(&self, other:&Point) -> bool {
|
|
||||||
other.right() == *self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_adjacent(&self, other: &Point) -> bool {
|
|
||||||
other.is_left(self) || other.is_right(self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn left(&self) -> Point {
|
pub fn left(&self) -> Point {
|
||||||
@@ -41,6 +18,21 @@ impl Point {
|
|||||||
Point(self.0 + 1, self.1)
|
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
|
// y values are reversed in ncurses
|
||||||
pub fn down(&self) -> Point {
|
pub fn down(&self) -> Point {
|
||||||
Point(self.0, self.1 + 1)
|
Point(self.0, self.1 + 1)
|
||||||
@@ -55,8 +47,18 @@ fn manhattan(c1: &Point, c2: &Point) -> i32 {
|
|||||||
return (c2.0 - c1.0).abs() + (c2.1 - c1.1).abs();
|
return (c2.0 - c1.0).abs() + (c2.1 - c1.1).abs();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn astar(start: &Point, goal: &Point) -> Vec<Point> {
|
fn weigh_point(w: &World, p: &Point) -> i32 {
|
||||||
let mut open_set : HashSet<Point> = HashSet::new();
|
if w.is_cleared(p) {
|
||||||
|
3
|
||||||
|
} else {
|
||||||
|
15
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
open_set.insert(*start);
|
||||||
|
|
||||||
let mut came_from: HashMap<Point, Point> = HashMap::new();
|
let mut came_from: HashMap<Point, Point> = HashMap::new();
|
||||||
@@ -86,21 +88,37 @@ pub fn astar(start: &Point, goal: &Point) -> Vec<Point> {
|
|||||||
answer.push(current.clone());
|
answer.push(current.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
return Ok(answer);
|
||||||
}
|
}
|
||||||
|
|
||||||
open_set.remove(¤t);
|
open_set.remove(¤t);
|
||||||
|
|
||||||
let current_gscore = gscore[¤t];
|
let current_gscore = gscore[¤t];
|
||||||
|
|
||||||
let all_neighbors: Vec<Point> = current.get_neighbors();
|
let all_neighbors: Vec<Point> = match w {
|
||||||
|
Some(w) => current
|
||||||
|
.get_neighbors()
|
||||||
|
.iter()
|
||||||
|
.filter(|p| w.is_valid_movement(p, s.unwrap()))
|
||||||
|
.map(|e| e.clone())
|
||||||
|
.collect(),
|
||||||
|
None => current.get_neighbors(),
|
||||||
|
};
|
||||||
|
|
||||||
for neighbor in all_neighbors.iter() {
|
for neighbor in all_neighbors.iter() {
|
||||||
let neighbor_score = if gscore.contains_key(&neighbor) {
|
let neighbor_score = if gscore.contains_key(&neighbor) {
|
||||||
gscore[neighbor]
|
gscore[&neighbor]
|
||||||
} else {
|
} else {
|
||||||
i32::MAX
|
i32::MAX
|
||||||
};
|
};
|
||||||
let score = current_gscore + 1;
|
|
||||||
|
let weight = match w {
|
||||||
|
Some(w) => weigh_point(&w, neighbor),
|
||||||
|
None => 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let score = current_gscore + weight;
|
||||||
|
|
||||||
if score < neighbor_score {
|
if score < neighbor_score {
|
||||||
gscore.insert(neighbor.clone(), score);
|
gscore.insert(neighbor.clone(), score);
|
||||||
fscore.insert(neighbor.clone(), score + manhattan(&neighbor, &goal));
|
fscore.insert(neighbor.clone(), score + manhattan(&neighbor, &goal));
|
||||||
@@ -110,7 +128,7 @@ pub fn astar(start: &Point, goal: &Point) -> Vec<Point> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
answer
|
Err(PathNotFoundError)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -118,9 +136,18 @@ fn test_astar() {
|
|||||||
let start = Point(0, 0);
|
let start = Point(0, 0);
|
||||||
|
|
||||||
let goal = Point(10, 10);
|
let goal = Point(10, 10);
|
||||||
let answers: Vec<Point> = astar(&start, &goal);
|
let answers = astar(&start, &goal, None, None);
|
||||||
|
|
||||||
for a in answers.iter() {
|
match answers {
|
||||||
println!("{:?}", &a);
|
Ok(ans) => {
|
||||||
|
for a in ans.iter() {
|
||||||
|
println!("{:?}", &a);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
panic!("Path not found. That shouldn't happen!")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -53,7 +53,7 @@ impl Screen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
/*#[test]
|
||||||
fn test_in_bounds() {
|
fn test_in_bounds() {
|
||||||
let x = 20;
|
let x = 20;
|
||||||
let y = 20;
|
let y = 20;
|
||||||
@@ -74,7 +74,7 @@ fn test_get_valid_movements_board() {
|
|||||||
|
|
||||||
let border = s.get_valid_movements(&Point(19,19));
|
let border = s.get_valid_movements(&Point(19,19));
|
||||||
assert!(border.len() == 2);
|
assert!(border.len() == 2);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
pub enum BoardCommand {
|
pub enum BoardCommand {
|
||||||
Dig(Point),
|
Dig(Point),
|
||||||
|
|||||||
+76
-7
@@ -1,5 +1,5 @@
|
|||||||
use crate::lib::entity::{Ant, Egg, Entities, Food, Queen};
|
use crate::lib::entity::{Ant, Egg, Entities, Food, Queen};
|
||||||
use crate::lib::point::Point;
|
use crate::lib::point::{astar, Point};
|
||||||
use crate::lib::screen::{BoardCommand, Screen};
|
use crate::lib::screen::{BoardCommand, Screen};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
@@ -21,12 +21,29 @@ impl World {
|
|||||||
self.cleared.insert(pos);
|
self.cleared.insert(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid_movement(&self, current_pos: &Point, target: &Point, b: &Screen) -> bool {
|
// make sure that this is as simple & robust as possible
|
||||||
let mut safe = true;
|
// then, use this set of rules when digging
|
||||||
|
// call astar from the suggested dig site to origin (or whatever "home" coordinate")
|
||||||
|
// if route does not exist, do not dig
|
||||||
|
pub fn is_valid_movement(&self, target: &Point, b: &Screen) -> bool {
|
||||||
|
let safe =
|
||||||
|
!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());
|
||||||
|
|
||||||
|
// can only go to target if
|
||||||
|
// its above or left or right of a not cleared space
|
||||||
|
|
||||||
|
/*
|
||||||
if target.is_above(current_pos) {
|
if target.is_above(current_pos) {
|
||||||
safe =
|
safe =
|
||||||
!self.cleared.contains(&target.left()) || !self.cleared.contains(&target.right());
|
(!self.cleared.contains(&target.left()) || !self.cleared.contains(&target.right()))
|
||||||
|
|| (!self.cleared.contains(¤t_pos.right()) && self.cleared.contains(&target.right()))
|
||||||
|
|| (!self.cleared.contains(¤t_pos.left()) && self.cleared.contains(&target.left()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if target.is_adjacent(current_pos) {
|
if target.is_adjacent(current_pos) {
|
||||||
@@ -38,17 +55,69 @@ impl World {
|
|||||||
safe = !self.cleared.contains(&target.down());
|
safe = !self.cleared.contains(&target.down());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b.is_in_bounds(target) && safe
|
|
||||||
|
if target.is_below(current_pos) {
|
||||||
|
safe = true;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// of course, its only a valid move if you can walk there!
|
||||||
|
b.is_in_bounds(target) && self.cleared.contains(target) && safe
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_valid_movements(&self, pos: &Point, b: &Screen) -> Vec<Point> {
|
pub fn is_cleared(&self, pos: &Point) -> bool {
|
||||||
|
self.cleared.contains(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_safe_to_dig(&self, current_pos: &Point, target: &Point, b: &Screen) -> bool {
|
||||||
|
// create a scenario in which this has been dug out
|
||||||
|
let mut hypothetical_world = self.clone();
|
||||||
|
hypothetical_world.clear(*target);
|
||||||
|
// test if we can return from the target to the beginning
|
||||||
|
// if yes, dig
|
||||||
|
let result = astar(target, &Point(0, 0), Some(&hypothetical_world), Some(b));
|
||||||
|
//let to_current_pos = astar(target, current_pos, Some(&hypothetical_world), Some(b));
|
||||||
|
/*if target.is_above(current_pos) {
|
||||||
|
safe = !self.cleared.contains(&target.up());
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.is_adjacent(current_pos) {
|
||||||
|
safe = !self.cleared.contains(&target.up()) && !self.cleared.contains(&target.down());
|
||||||
|
}
|
||||||
|
|
||||||
|
if target.is_below(current_pos) {
|
||||||
|
safe = !self.cleared.contains(&target.down()) && !self.cleared.contains(&target.left()) && !self.cleared.contains(&target.right());
|
||||||
|
}*/
|
||||||
|
b.is_in_bounds(target) && result.is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_diggable(&self, b: &Screen, pos: &Point) -> Vec<Point> {
|
||||||
let moves = b.get_valid_movements(pos);
|
let moves = b.get_valid_movements(pos);
|
||||||
moves
|
moves
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|p| self.is_valid_movement(pos, p, b))
|
.filter(|p| !self.is_cleared(p))
|
||||||
.map(|p| p.clone())
|
.map(|p| p.clone())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(p, b))
|
||||||
|
.map(|p| p.clone())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if return_diggables {
|
||||||
|
ans.extend(self.get_diggable(b, pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
ans
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(e: &Entities, w: &World, b: &Screen) {
|
pub fn render(e: &Entities, w: &World, b: &Screen) {
|
||||||
|
|||||||
+3
-8
@@ -28,13 +28,8 @@ fn main() {
|
|||||||
entities.add_entity(&q);
|
entities.add_entity(&q);
|
||||||
|
|
||||||
for i in 0..6 {
|
for i in 0..6 {
|
||||||
for j in 0..6 {
|
for j in 0..1 {
|
||||||
if j != 1 {
|
world.clear(Point(i, j));
|
||||||
world.clear(Point(i, j));
|
|
||||||
} else {
|
|
||||||
world.clear(Point(0, j));
|
|
||||||
world.clear(Point(5, j));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +37,7 @@ fn main() {
|
|||||||
// 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
|
||||||
simulate(&mut entities, &mut world, &mut board);
|
simulate(&mut entities, &mut world, &mut board);
|
||||||
render(&entities, &world, &board);
|
render(&entities, &world, &board);
|
||||||
sleep(time::Duration::from_millis(1000));
|
sleep(time::Duration::from_millis(100));
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
endwin();
|
endwin();
|
||||||
|
|||||||
+11
-10
@@ -1,8 +1,8 @@
|
|||||||
use antf::lib::screen::{Screen, init_screen};
|
|
||||||
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::entity::{Ant, Entities, Food, FoodGenerator};
|
||||||
use antf::lib::entity::{Entities, Ant, Food, FoodGenerator};
|
use antf::lib::point::Point;
|
||||||
|
use antf::lib::screen::{init_screen, Screen};
|
||||||
|
use antf::lib::world::{render, simulate, World};
|
||||||
|
|
||||||
use ncurses::*;
|
use ncurses::*;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
@@ -12,20 +12,21 @@ use std::time;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_reach_astar() {
|
fn test_reach_astar() {
|
||||||
let mut board = init_screen();//Screen::new(40,40);
|
let mut board = init_screen(); //Screen::new(40,40);
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
|
||||||
|
world.clear(Point(0, 0));
|
||||||
let mut entities = Entities::new();
|
let mut entities = Entities::new();
|
||||||
|
|
||||||
let a = Ant::new(0,0);
|
let a = Ant::new(0, 0);
|
||||||
let id = entities.add_entity(&a);
|
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();
|
||||||
ant.plan.push(AIGoal::Reach(Point(10,10)));
|
ant.plan.push(AIGoal::Reach(Point(0, 0)));
|
||||||
ant.plan.push(AIGoal::Reach(Point(-10,-10)));
|
ant.plan.push(AIGoal::Reach(Point(20, 20)));
|
||||||
ant.plan.push(AIGoal::Reach(Point(10,-10)));
|
ant.plan.push(AIGoal::Reach(Point(0, 0)));
|
||||||
ant.plan.push(AIGoal::Reach(Point(-10,10)));
|
ant.plan.push(AIGoal::Reach(Point(20, 20)));
|
||||||
|
|
||||||
// craps out... need to make sure unwrap() is safe
|
// craps out... need to make sure unwrap() is safe
|
||||||
for _ in 0..420 {
|
for _ in 0..420 {
|
||||||
|
|||||||
Reference in New Issue
Block a user