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