use crate::entities::entity::Identified;
use crate::entities::EntityID;
use crate::types::Neighbors;
use crate::types::Position;
use crate::types::Positioned;
use crate::types::PositionedMut;
use alga::general::{
AbstractMagma, AbstractMonoid, AbstractSemigroup, Additive, Identity,
};
use std::collections::{BTreeMap, HashMap};
use std::iter::FromIterator;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct EntityMap<A> {
by_position: BTreeMap<Position, Vec<EntityID>>,
by_id: HashMap<EntityID, A>,
last_id: EntityID,
}
// impl<A: Debug> ArbitraryF1<A> for EntityMap<A> {
// type Parameters = ();
// fn lift1_with<AS>(base: AS, _: Self::Parameters) -> BoxedStrategy<Self>
// where
// AS: Strategy<Value = A> + 'static,
// {
// unimplemented!()
// }
// // type Strategy = strategy::Just<Self>;
// // fn arbitrary_with(params : Self::Parameters) -> Self::Strategy;
// }
// impl<A: Arbitrary> Arbitrary for EntityMap<A> {
// type Parameters = A::Parameters;
// type Strategy = BoxedStrategy<Self>;
// fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
// let a_strat: A::Strategy = Arbitrary::arbitrary_with(params);
// ArbitraryF1::lift1::<A::Strategy>(a_strat)
// }
// }
const BY_POS_INVARIANT: &'static str =
"Invariant: All references in EntityMap.by_position should point to existent references in by_id";
impl<A> EntityMap<A> {
pub fn new() -> EntityMap<A> {
EntityMap {
by_position: BTreeMap::new(),
by_id: HashMap::new(),
last_id: 0,
}
}
pub fn len(&self) -> usize {
self.by_id.len()
}
/// Returns a list of all entities at the given position
pub fn at<'a>(&'a self, pos: Position) -> Vec<&'a A> {
// self.by_position.get(&pos).iter().flat_map(|eids| {
// eids.iter()
// .map(|eid| self.by_id.get(eid).expect(BY_POS_INVARIANT))
// })
// gross.
match self.by_position.get(&pos) {
None => Vec::new(),
Some(eids) => {
let mut res = Vec::new();
for eid in eids {
res.push(self.by_id.get(eid).expect(BY_POS_INVARIANT));
}
res
}
}
}
/// Remove all entities at the given position
pub fn remove_all_at(&mut self, pos: Position) {
self.by_position.remove(&pos).map(|eids| {
for eid in eids {
self.by_id.remove(&eid).expect(BY_POS_INVARIANT);
}
});
}
pub fn get<'a>(&'a self, id: EntityID) -> Option<&'a A> {
self.by_id.get(&id)
}
pub fn get_mut<'a>(&'a mut self, id: EntityID) -> Option<&'a mut A> {
self.by_id.get_mut(&id)
}
pub fn entities<'a>(&'a self) -> impl Iterator<Item = &'a A> {
self.by_id.values()
}
pub fn entities_mut<'a>(&'a mut self) -> impl Iterator<Item = &'a mut A> {
self.by_id.values_mut()
}
pub fn ids(&self) -> impl Iterator<Item = &EntityID> {
self.by_id.keys()
}
fn next_id(&mut self) -> EntityID {
self.last_id += 1;
self.last_id
}
}
impl<A: Positioned + Identified<EntityID>> EntityMap<A> {
pub fn insert(&mut self, mut entity: A) -> EntityID {
let pos = entity.position();
let entity_id = self.next_id();
entity.set_id(entity_id);
self.by_id.entry(entity_id).or_insert(entity);
self.by_position
.entry(pos)
.or_insert(Vec::new())
.push(entity_id);
entity_id
}
/// Remove the entity with the given ID
pub fn remove(&mut self, id: EntityID) -> Option<A> {
self.by_id.remove(&id).map(|e| {
self.by_position
.get_mut(&e.position())
.map(|es| es.retain(|e| *e != id));
e
})
}
/// Moves all elements from `other` into `Self`, leathing `other` empty.
pub fn append(&mut self, other: &mut Self) {
self.by_position.append(&mut other.by_position);
self.by_id.reserve(other.len());
for (k, v) in other.by_id.drain() {
self.by_id.insert(k, v);
}
self.last_id = self.last_id.max(other.last_id);
other.last_id = 0;
}
/// Gets all 8 neighbors of the given position.
pub fn neighbors<'a>(
&'a self,
position: Position,
) -> Neighbors<Vec<(EntityID, &'a A)>> {
Neighbors::of_position(position)
.map(|pos| self.at(*pos))
.mapmap(&|e| (e.id(), *e))
}
pub fn neighbor_entities<'a>(
&'a self,
position: Position,
) -> Neighbors<Vec<&'a A>> {
self.neighbors(position).mapmap(&|(_eid, ent)| *ent)
}
}
impl<'a, A: Positioned + Identified<EntityID>> IntoIterator
for &'a EntityMap<A>
{
type Item = (&'a EntityID, &'a A);
type IntoIter = std::collections::hash_map::Iter<'a, EntityID, A>;
fn into_iter(self) -> Self::IntoIter {
(&self.by_id).into_iter()
}
}
impl<A: Positioned + Identified<EntityID>> IntoIterator for EntityMap<A> {
type Item = (EntityID, A);
type IntoIter = std::collections::hash_map::IntoIter<EntityID, A>;
fn into_iter(self) -> Self::IntoIter {
self.by_id.into_iter()
}
}
impl<A: Positioned + Identified<EntityID>> FromIterator<A> for EntityMap<A> {
fn from_iter<I: IntoIterator<Item = A>>(iter: I) -> Self {
let mut em = EntityMap::new();
for ent in iter {
em.insert(ent);
}
em
}
}
impl<A: Positioned + Identified<EntityID> + Eq + Clone> AbstractMagma<Additive>
for EntityMap<A>
{
fn operate(&self, right: &Self) -> Self {
let mut by_position = self.by_position.clone();
by_position.append(&mut right.by_position.clone());
let mut by_id = self.by_id.clone();
for (k, v) in right.by_id.clone() {
by_id.insert(k, v);
}
EntityMap {
by_position,
by_id,
last_id: self.last_id.max(right.last_id),
}
}
}
impl<A: Positioned + Identified<EntityID> + Eq + Clone>
AbstractSemigroup<Additive> for EntityMap<A>
{
}
impl<A: Positioned + Identified<EntityID> + Eq> Identity<Additive>
for EntityMap<A>
{
fn identity() -> Self {
EntityMap::new()
}
}
impl<A: Positioned + Identified<EntityID> + Eq + Clone> AbstractMonoid<Additive>
for EntityMap<A>
{
}
impl<A: PositionedMut> EntityMap<A> {
pub fn update_position(
&mut self,
entity_id: EntityID,
new_position: Position,
) {
let mut old_pos = None;
if let Some(entity) = self.by_id.get_mut(&entity_id) {
if entity.position() == new_position {
return;
}
old_pos = Some(entity.position());
entity.set_position(new_position);
}
old_pos.map(|p| {
self.by_position
.get_mut(&p)
.map(|es| es.retain(|e| *e != entity_id));
self.by_position
.entry(new_position)
.or_insert(Vec::new())
.push(entity_id);
});
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::PositionedMut;
use proptest::prelude::*;
use proptest_derive::Arbitrary;
#[derive(Debug, Arbitrary, PartialEq, Eq, Clone)]
struct TestEntity {
_id: Option<EntityID>,
position: Position,
name: String,
}
impl Positioned for TestEntity {
fn position(&self) -> Position {
self.position
}
}
impl PositionedMut for TestEntity {
fn set_position(&mut self, pos: Position) {
self.position = pos
}
}
impl Identified<EntityID> for TestEntity {
fn opt_id(&self) -> Option<EntityID> {
self._id
}
fn set_id(&mut self, id: EntityID) {
self._id = Some(id);
}
}
fn gen_entity_map() -> BoxedStrategy<EntityMap<TestEntity>> {
any::<Vec<TestEntity>>()
.prop_map(|ents| {
ents.iter()
.map(|e| e.clone())
.collect::<EntityMap<TestEntity>>()
})
.boxed()
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(10))]
#[test]
fn test_entity_map_len(items: Vec<TestEntity>) {
let mut map = EntityMap::new();
assert_eq!(map.len(), 0);
for ent in &items {
map.insert(ent.clone());
}
assert_eq!(map.len(), items.len());
}
#[test]
fn test_entity_map_getset(
mut em in gen_entity_map(),
ent: TestEntity
) {
em.insert(ent.clone());
assert!(em.at(ent.position).iter().any(|e| e.name == ent.name))
}
#[test]
fn test_entity_map_set_iter_contains(
mut em in gen_entity_map(),
ent: TestEntity
) {
em.insert(ent.clone());
assert!(em.entities().any(|e| e.name == ent.name))
}
#[test]
fn test_update_position(
mut em in gen_entity_map(),
ent: TestEntity,
new_position: Position,
) {
let original_position = ent.position();
let entity_id = em.insert(ent.clone());
em.update_position(entity_id, new_position);
if new_position != original_position {
assert!(em.at(original_position).iter().all(|e| e.name != ent.name));
}
assert_eq!(
em.get(entity_id).map(|e| e.position()),
Some(new_position)
);
assert!(
em.at(new_position).iter().map(
|e| e.name.clone()).any(|en| en == ent.name),
)
}
#[test]
fn test_remove_all_at(
mut em in gen_entity_map(),
pos: Position,
) {
em.remove_all_at(pos);
assert_eq!(em.at(pos).len(), 0);
}
#[test]
fn test_entity_map_semigroup_laws(
em1 in gen_entity_map(),
em2 in gen_entity_map(),
em3 in gen_entity_map(),
) {
assert!(AbstractSemigroup::prop_is_associative((em1, em2, em3)));
}
fn test_entity_map_monoid_laws(
em in gen_entity_map(),
) {
assert!(
AbstractMonoid::prop_operating_identity_element_is_noop((em,))
);
}
#[test]
fn test_entity_map_append(
mut target in gen_entity_map(),
mut source in gen_entity_map(),
) {
let orig_source = source.clone();
target.append(&mut source);
assert_eq!(source, EntityMap::new());
for (eid, e) in orig_source {
assert_eq!(target.get(eid), Some(&e))
}
}
}
}