commit
1b6dd4b9ad
@ -0,0 +1 @@ |
|||||||
|
/target |
@ -0,0 +1,102 @@ |
|||||||
|
# This file is automatically @generated by Cargo. |
||||||
|
# It is not intended for manual editing. |
||||||
|
version = 3 |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "antf" |
||||||
|
version = "0.1.0" |
||||||
|
dependencies = [ |
||||||
|
"ncurses", |
||||||
|
"rand", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "cc" |
||||||
|
version = "1.0.83" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" |
||||||
|
dependencies = [ |
||||||
|
"libc", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "cfg-if" |
||||||
|
version = "1.0.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "getrandom" |
||||||
|
version = "0.2.11" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" |
||||||
|
dependencies = [ |
||||||
|
"cfg-if", |
||||||
|
"libc", |
||||||
|
"wasi", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "libc" |
||||||
|
version = "0.2.151" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "ncurses" |
||||||
|
version = "5.101.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "5e2c5d34d72657dc4b638a1c25d40aae81e4f1c699062f72f467237920752032" |
||||||
|
dependencies = [ |
||||||
|
"cc", |
||||||
|
"libc", |
||||||
|
"pkg-config", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "pkg-config" |
||||||
|
version = "0.3.28" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "ppv-lite86" |
||||||
|
version = "0.2.17" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand" |
||||||
|
version = "0.8.5" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" |
||||||
|
dependencies = [ |
||||||
|
"libc", |
||||||
|
"rand_chacha", |
||||||
|
"rand_core", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand_chacha" |
||||||
|
version = "0.3.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" |
||||||
|
dependencies = [ |
||||||
|
"ppv-lite86", |
||||||
|
"rand_core", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand_core" |
||||||
|
version = "0.6.4" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" |
||||||
|
dependencies = [ |
||||||
|
"getrandom", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "wasi" |
||||||
|
version = "0.11.0+wasi-snapshot-preview1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" |
@ -0,0 +1,10 @@ |
|||||||
|
[package] |
||||||
|
name = "antf" |
||||||
|
version = "0.1.0" |
||||||
|
edition = "2021" |
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
ncurses = "5.101.0" |
||||||
|
rand = "0.8.5" |
@ -0,0 +1,300 @@ |
|||||||
|
extern crate ncurses; |
||||||
|
|
||||||
|
use ncurses::*; |
||||||
|
use rand::seq::SliceRandom; |
||||||
|
use rand::thread_rng; |
||||||
|
use std::collections::HashSet; |
||||||
|
use std::thread::sleep; |
||||||
|
use std::time; |
||||||
|
|
||||||
|
#[derive(Hash, PartialEq, Eq, Clone, Debug, Copy)] |
||||||
|
struct Point(i32, i32); |
||||||
|
|
||||||
|
struct Board { |
||||||
|
center: Point, |
||||||
|
max_x: i32, |
||||||
|
max_y: i32, |
||||||
|
} |
||||||
|
|
||||||
|
fn get_neighbors(pos: &Point) -> Vec<Point> { |
||||||
|
vec![ |
||||||
|
Point(pos.0, pos.1 + 1), |
||||||
|
Point(pos.0, pos.1 - 1), |
||||||
|
Point(pos.0 - 1, pos.1), |
||||||
|
Point(pos.0 + 1, pos.1), |
||||||
|
] |
||||||
|
} |
||||||
|
|
||||||
|
impl Board { |
||||||
|
fn new(max_x: i32, max_y: i32) -> Board { |
||||||
|
Board { |
||||||
|
center: Point(max_x / 2, max_y / 2), |
||||||
|
max_x, |
||||||
|
max_y, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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) |
||||||
|
} |
||||||
|
|
||||||
|
fn get_valid_movements(&self, pos: &Point) -> Vec<Point> { |
||||||
|
let binding = get_neighbors(pos); |
||||||
|
binding |
||||||
|
.iter() |
||||||
|
.filter(|e| self.is_in_bounds(e)) |
||||||
|
.map(|e| e.clone()) |
||||||
|
.collect() |
||||||
|
} |
||||||
|
} |
||||||
|
#[derive(Clone)] |
||||||
|
struct World { |
||||||
|
cleared: HashSet<Point>, |
||||||
|
} |
||||||
|
|
||||||
|
impl World { |
||||||
|
fn new() -> World { |
||||||
|
World { |
||||||
|
cleared: HashSet::new(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn clear(&mut self, pos: Point) { |
||||||
|
self.cleared.insert(pos); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
struct Colony { |
||||||
|
ants: Vec<Box<dyn AI>>, |
||||||
|
} |
||||||
|
|
||||||
|
impl Colony { |
||||||
|
fn new() -> Colony { |
||||||
|
Colony { ants: vec![] } |
||||||
|
} |
||||||
|
|
||||||
|
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) { |
||||||
|
self.ants.push(Box::new(e)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
trait AI { |
||||||
|
fn step(&mut self, b: &Board, w: &World) -> BoardCommand; |
||||||
|
fn get_position(&self) -> Point; |
||||||
|
fn set_position(&mut self, p: Point); |
||||||
|
} |
||||||
|
|
||||||
|
struct Ant { |
||||||
|
pos: Point, |
||||||
|
} |
||||||
|
|
||||||
|
impl Ant { |
||||||
|
fn new(x: i32, y: i32) -> Ant { |
||||||
|
Ant { pos: Point(x, y) } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
struct Queen { |
||||||
|
pos: Point, |
||||||
|
egg_count: u8, |
||||||
|
} |
||||||
|
|
||||||
|
impl Queen { |
||||||
|
fn new(x: i32, y: i32) -> Queen { |
||||||
|
Queen { |
||||||
|
pos: Point(x, y), |
||||||
|
egg_count: 0, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
struct Egg { |
||||||
|
pos: Point, |
||||||
|
counter: u8, |
||||||
|
} |
||||||
|
|
||||||
|
impl Egg { |
||||||
|
fn new(x: i32, y: i32) -> Egg { |
||||||
|
Egg { |
||||||
|
pos: Point(x, y), |
||||||
|
counter: 10, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl AI for Egg { |
||||||
|
fn step(&mut self, b: &Board, w: &World) -> BoardCommand { |
||||||
|
if self.counter > 0 { |
||||||
|
self.counter -= 1; |
||||||
|
return BoardCommand::Noop; |
||||||
|
} else { |
||||||
|
BoardCommand::Hatch |
||||||
|
} |
||||||
|
} |
||||||
|
fn get_position(&self) -> Point { |
||||||
|
self.pos.clone() |
||||||
|
} |
||||||
|
fn set_position(&mut self, p: Point) { |
||||||
|
self.pos = p; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
enum BoardCommand<'a> { |
||||||
|
Move(Box<&'a mut dyn AI>, Point), |
||||||
|
Dig(Point), |
||||||
|
LayEgg(Point), |
||||||
|
Hatch, |
||||||
|
Noop, |
||||||
|
} |
||||||
|
|
||||||
|
impl AI for Ant { |
||||||
|
// return the next move for this ant
|
||||||
|
fn step(&mut self, b: &Board, w: &World) -> BoardCommand { |
||||||
|
let valid = b.get_valid_movements(&self.pos); |
||||||
|
let mut rng = thread_rng(); |
||||||
|
|
||||||
|
let choice = valid.choose(&mut rng).cloned(); |
||||||
|
|
||||||
|
if choice.is_none() { |
||||||
|
return BoardCommand::Noop; |
||||||
|
} else { |
||||||
|
let pos = choice.unwrap(); |
||||||
|
if w.cleared.contains(&pos) { |
||||||
|
return BoardCommand::Move(Box::new(self), pos); |
||||||
|
} else { |
||||||
|
return BoardCommand::Dig(pos); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn get_position(&self) -> Point { |
||||||
|
self.pos.clone() |
||||||
|
} |
||||||
|
|
||||||
|
fn set_position(&mut self, p: Point) { |
||||||
|
self.pos = p |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl AI for Queen { |
||||||
|
fn step(&mut self, b: &Board, w: &World) -> BoardCommand { |
||||||
|
let valid = b.get_valid_movements(&self.pos); |
||||||
|
let mut rng = thread_rng(); |
||||||
|
let choice = valid.choose(&mut rng).cloned(); |
||||||
|
|
||||||
|
if choice.is_none() { |
||||||
|
return BoardCommand::Noop; |
||||||
|
} else { |
||||||
|
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); |
||||||
|
} else { |
||||||
|
return BoardCommand::Move(Box::new(self), pos); |
||||||
|
} |
||||||
|
} else { |
||||||
|
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) { |
||||||
|
for c in w.cleared.iter() { |
||||||
|
mvprintw(c.1 + b.center.1, c.0 + b.center.0, "x"); |
||||||
|
} |
||||||
|
|
||||||
|
for a in c.ants.iter() { |
||||||
|
let pos = a.get_position(); |
||||||
|
mvprintw(pos.1 + b.center.1, pos.0 + b.center.0, "o"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests { |
||||||
|
// TODO: tests, cleanup code in general
|
||||||
|
use super::*; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn board() { |
||||||
|
let max_x = 20; |
||||||
|
let max_y = 20; |
||||||
|
|
||||||
|
let mut board = Board::new(max_x, max_y); |
||||||
|
let mut world = World::new(); |
||||||
|
|
||||||
|
dbg!(board.is_in_bounds(&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) { |
||||||
|
let world = w.clone(); |
||||||
|
let cmds: Vec<BoardCommand> = c.ants.iter_mut().map(|a| a.step(b, &world)).collect(); |
||||||
|
|
||||||
|
for cmd in cmds { |
||||||
|
match cmd { |
||||||
|
BoardCommand::Move(a, pos) => { |
||||||
|
a.set_position(pos); |
||||||
|
} |
||||||
|
BoardCommand::Dig(pos) => { |
||||||
|
w.clear(pos); |
||||||
|
} |
||||||
|
BoardCommand::LayEgg(pos) => { |
||||||
|
//c.add_entity(Egg::new(pos.0, pos.1));
|
||||||
|
} |
||||||
|
BoardCommand::Hatch => {} |
||||||
|
BoardCommand::Noop => {} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn main() { |
||||||
|
initscr(); |
||||||
|
|
||||||
|
/* Invisible cursor. */ |
||||||
|
curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE); |
||||||
|
|
||||||
|
let mut max_x = 0; |
||||||
|
let mut max_y = 0; |
||||||
|
|
||||||
|
getmaxyx(stdscr(), &mut max_y, &mut max_x); |
||||||
|
|
||||||
|
// might move all of contents of board into world
|
||||||
|
|
||||||
|
let mut board = Board::new(max_x, max_y); |
||||||
|
let mut world = World::new(); |
||||||
|
let mut colony = Colony::new(); |
||||||
|
|
||||||
|
colony.add_ant(0,0); |
||||||
|
|
||||||
|
|
||||||
|
loop { |
||||||
|
// TODO: add way to break out of the loop by hitting a random key
|
||||||
|
simulate(&mut colony, &mut world, &mut board); |
||||||
|
render(&colony, &world, &board); |
||||||
|
sleep(time::Duration::from_millis(100)); |
||||||
|
refresh(); |
||||||
|
} |
||||||
|
endwin(); |
||||||
|
} |
Loading…
Reference in new issue