use super::*;
use colored::*;
use fnv::FnvHashMap;
use rand::seq::SliceRandom;
use std::cell::{RefCell, RefMut};
pub struct World {
pub verbose: u8,
width: i32,
height: i32,
rng: RefCell<Box<dyn RngCore>>,
actors: FnvHashMap<Point, Vec<ComponentId>>,
pending: Vec<(Point, ComponentId)>,
dummy: Vec<ComponentId>,
ticks: i32, }
impl World {
pub fn new(width: i32, height: i32, rng: Box<dyn RngCore>, verbose: u8) -> World {
World {
width,
height,
verbose,
rng: RefCell::new(rng),
actors: FnvHashMap::default(),
pending: Vec::new(),
dummy: Vec::new(),
ticks: 0,
}
}
pub fn rng(&self) -> RefMut<Box<dyn RngCore>> {
self.rng.borrow_mut()
}
pub fn cell(&self, loc: Point) -> &Vec<ComponentId> {
let loc = self.wrap(loc);
&self.actors.get(&loc).unwrap_or(&self.dummy)
}
pub fn add_back(&mut self, store: &Store, loc: Point, component: Component) {
assert!(has_trait!(component, Action)); assert!(has_trait!(component, Render));
let loc = self.wrap(loc);
let actors = self.actors.entry(loc).or_default();
actors.push(component.id);
store.add(component)
}
pub fn add_front(&mut self, store: &Store, loc: Point, component: Component) {
assert!(has_trait!(component, Action)); assert!(has_trait!(component, Render));
let loc = self.wrap(loc);
let actors = self.actors.entry(loc).or_default();
actors.insert(0, component.id);
store.add(component)
}
pub fn move_to(&mut self, id: ComponentId, old_loc: Point, new_loc: Point) {
let old_loc = self.wrap(old_loc);
let new_loc = self.wrap(new_loc);
let old_ids = self.actors.get_mut(&old_loc).unwrap();
let index = old_ids.iter().position(|e| *e == id).unwrap();
old_ids.remove(index);
let new_ids = self.actors.entry(new_loc).or_default();
new_ids.push(id);
}
pub fn remove(&mut self, store: &Store, id: ComponentId, loc: Point) {
let loc = self.wrap(loc);
let old_ids = self.actors.get_mut(&loc).unwrap();
let index = old_ids.iter().position(|e| *e == id).unwrap();
old_ids.remove(index);
store.remove(id);
if let Some(index) = self
.pending
.iter()
.position(|(pt, i)| *pt == loc && *i == id)
{
self.pending.remove(index);
}
}
pub fn all<P>(&self, loc: Point, radius: i32, predicate: P) -> Vec<Point>
where
P: Fn(Point) -> bool,
{
let mut cells = Vec::new();
let loc = self.wrap(loc);
for dy in -radius..=radius {
let y = loc.y + dy;
for dx in -radius..=radius {
let x = loc.x + dx;
let candidate = Point::new(x, y);
if predicate(candidate) {
cells.push(candidate);
}
}
}
cells
}
pub fn step(&mut self, store: &mut Store) {
assert!(self.pending.is_empty());
for (loc, ids) in self.actors.iter() {
for id in ids {
self.pending.push((*loc, *id));
}
}
self.pending[..].shuffle(self.rng.borrow_mut().as_mut());
while !self.pending.is_empty() {
let (loc, id) = self.pending.pop().unwrap();
{
let context = Context {
world: self,
store: &store,
loc,
id,
};
let component = store.get(id);
let mut action = find_trait_mut!(component, Action).unwrap();
if action.act(context) == LifeCycle::Dead {
let ids = self.actors.get_mut(&loc).unwrap();
let index = ids.iter().position(|e| *e == id).unwrap();
ids.remove(index);
store.remove(id);
}
}
store.sync();
}
self.ticks += 1;
}
pub fn render(&self, store: &Store) -> LifeCycle {
let mut cycle = LifeCycle::Dead;
println!("ticks: {}", self.ticks);
if self.verbose >= 1 {
print!(" ");
for x in 0..self.width {
print!("{}", x % 10);
}
println!();
}
for y in 0..self.height {
if self.verbose >= 1 {
print!("{} ", y % 10);
}
for x in 0..self.width {
let loc = Point::new(x, y);
if let Some(id) = self.actors.get(&loc).map(|v| v.last()).flatten() {
let component = store.get(*id);
let render = find_trait!(component, Render).unwrap();
let ch = render.render();
if ch != "|".normal() && ch != " ".normal() {
cycle = LifeCycle::Alive;
}
print!("{}", ch);
} else {
print!(" ");
}
}
println!();
}
println!();
println!("{}", "-".repeat(self.width as usize));
cycle
}
pub fn distance2(&self, loc1: Point, loc2: Point) -> i32 {
let mut dx = i32::abs(loc1.x - loc2.x);
let mut dy = i32::abs(loc1.y - loc2.y);
if dx > self.width / 2 {
dx = self.width / 2 - dx;
}
if dy > self.height / 2 {
dy = self.height - dy;
}
dx * dx + dy * dy
}
fn wrap(&self, loc: Point) -> Point {
let mut x = loc.x;
let mut y = loc.y;
x = if x >= 0 {
x % self.width
} else {
x + self.width
};
y = if y >= 0 {
y % self.height
} else {
y + self.height
};
Point::new(x, y)
}
}