use std::collections::{BTreeMap, HashMap};
use super::*;
use tile::*;
mod neighborhood;
mod tile;
#[cfg(feature = "parallel")]
mod scheduler;
pub use neighborhood::*;
pub use tile::TileView;
type Entities<'e, K, C> = Vec<Box<EntityTrait<'e, K, C>>>;
type EntitiesKinds<'e, K, C> = BTreeMap<K, Entities<'e, K, C>>;
#[derive(Debug)]
pub struct Environment<'e, K, C> {
entities: EntitiesKinds<'e, K, C>,
tiles: Tiles<'e, K, C>,
snapshots: Vec<Snapshot<K>>,
generation: u64,
#[cfg(feature = "parallel")]
scheduler: scheduler::Scheduler,
}
#[derive(Debug)]
struct Snapshot<K> {
id: Id,
kind: K,
location: Location,
}
impl<'e, K: Ord, C> Environment<'e, K, C> {
pub fn new(dimension: impl Into<Dimension>) -> Self {
let dimension = dimension.into();
Self {
entities: BTreeMap::new(),
tiles: Tiles::new(dimension),
snapshots: Vec::default(),
generation: 0,
#[cfg(feature = "parallel")]
scheduler: scheduler::Scheduler::new(
dimension,
rayon::current_num_threads(),
),
}
}
pub fn dimension(&self) -> Dimension {
self.tiles.dimension()
}
#[cfg(not(feature = "parallel"))]
pub fn insert<E>(&mut self, entity: E)
where
E: Entity<'e, Kind = K, Context = C> + 'e,
{
self.insert_boxed(Box::new(entity));
}
#[cfg(feature = "parallel")]
pub fn insert<E>(&mut self, entity: E)
where
E: Entity<'e, Kind = K, Context = C> + 'e + Send + Sync,
{
self.insert_boxed(Box::new(entity));
}
fn insert_boxed(&mut self, mut entity: Box<EntityTrait<'e, K, C>>) {
self.tiles.insert(&mut *entity);
let entities = self.entities.entry(entity.kind()).or_default();
entities.push(entity);
}
pub fn draw(
&self,
ctx: &mut C,
transform: impl Into<Transform>,
) -> Result<(), Error> {
let transform = transform.into();
for entities in self.entities.values() {
for entity in entities {
entity.draw(ctx, transform)?;
}
}
Ok(())
}
pub fn is_empty(&self) -> bool {
self.count() == 0
}
pub fn count(&self) -> usize {
self.entities.values().map(|entities| entities.len()).sum()
}
pub fn count_kind(&self, kind: &K) -> usize {
self.entities
.get(kind)
.map(|entities| entities.len())
.unwrap_or(0)
}
pub fn generation(&self) -> u64 {
self.generation
}
pub fn entities(&self) -> impl Iterator<Item = &EntityTrait<'e, K, C>> {
self.entities
.values()
.map(|e| e.iter().map(|e| &**e))
.flatten()
}
pub fn entities_mut(
&mut self,
) -> impl Iterator<Item = &mut EntityTrait<'e, K, C>> {
self.entities
.values_mut()
.map(|e| e.iter_mut().map(|e| &mut **e))
.flatten()
}
pub fn entities_at(
&self,
location: impl Into<Location>,
) -> impl Iterator<Item = &EntityTrait<'e, K, C>> {
self.tiles.entities_at(location)
}
pub fn entities_at_mut(
&mut self,
location: impl Into<Location>,
) -> impl Iterator<Item = &mut EntityTrait<'e, K, C>> {
self.tiles.entities_at_mut(location)
}
pub fn nextgen(&mut self) -> Result<u64, Error> {
self.record_location();
self.observe_and_react()?;
self.update_location();
self.populate_with_offspring();
self.depopulate_dead();
self.generation = self.generation.wrapping_add(1);
Ok(self.generation)
}
fn record_location(&mut self) {
self.snapshots.clear();
let additional = self.count().saturating_sub(self.snapshots.capacity());
self.snapshots.reserve(additional);
for entities in self.entities.values() {
for (i, entity) in entities.iter().enumerate() {
if let Some(location) = entity.location() {
self.snapshots.push(Snapshot {
id: i,
kind: entity.kind(),
location,
});
}
}
}
}
fn update_location(&mut self) {
let entities = &self.entities;
let find_entity = |snapshot: &Snapshot<K>| {
let entity = entities.get(&snapshot.kind)?.get(snapshot.id)?;
let location = entity.location()?;
if location != snapshot.location {
Some((entity.id(), location))
} else {
None
}
};
for snapshot in &self.snapshots {
if let Some((id, location)) = find_entity(snapshot) {
debug_assert_ne!(location, snapshot.location);
self.tiles.relocate(id, snapshot.location, location);
}
}
}
fn populate_with_offspring(&mut self) {
let offspring: Vec<Box<EntityTrait<'e, K, C>>> = self
.entities
.values_mut()
.map(|e| e.iter_mut())
.flatten()
.filter_map(|e| e.offspring())
.map(|offspring| offspring.take_entities())
.flatten()
.collect();
for entity in offspring {
self.insert_boxed(entity);
}
}
fn depopulate_dead(&mut self) {
for entities in self.entities.values_mut() {
for entity in entities.iter() {
match (entity.location(), entity.lifespan()) {
(Some(loc), Some(lifespan)) if !lifespan.is_alive() => {
self.tiles.remove(entity.id(), loc);
}
_ => (),
};
}
entities.retain(|entity| {
if let Some(lifespan) = entity.lifespan() {
lifespan.is_alive()
} else {
true
}
});
}
}
#[cfg(not(feature = "parallel"))]
fn observe_and_react(&mut self) -> Result<(), Error> {
for entities in self.entities.values_mut() {
for entity in entities.iter_mut() {
let neighborhood = self.tiles.neighborhood(&**entity);
entity.observe(neighborhood)?;
}
}
for entities in self.entities.values_mut() {
for entity in entities.iter_mut() {
let neighborhood = self.tiles.neighborhood(&**entity);
entity.react(neighborhood)?;
}
}
Ok(())
}
#[cfg(feature = "parallel")]
fn observe_and_react(&mut self) -> Result<(), Error> {
use rayon::prelude::*;
let entities = self
.entities
.values_mut()
.map(|e| e.iter_mut())
.flatten()
.map(|e| &mut **e);
let scheduler::Tasks {
mut sync,
mut unsync,
} = self.scheduler.get_tasks(entities);
let tiles = &self.tiles;
sync.par_iter_mut().try_for_each(|entities| {
for e in entities.iter_mut() {
let neighborhood = tiles.neighborhood(*e);
e.observe(neighborhood)?;
}
Ok(())
})?;
for e in &mut unsync {
let neighborhood = self.tiles.neighborhood(*e);
e.observe(neighborhood)?;
}
sync.par_iter_mut().try_for_each(|entities| {
for e in entities.iter_mut() {
let neighborhood = tiles.neighborhood(*e);
e.react(neighborhood)?;
}
Ok(())
})?;
for e in unsync {
let neighborhood = self.tiles.neighborhood(e);
e.react(neighborhood)?;
}
Ok(())
}
}