Make EntityMap::append not overwrite entities

Rather than overwriting entities with the same ID when appending, make
EntityMap::append actually respect the internal invariants of the map
and preserve entities from both sides, with no regard for their id.
This commit is contained in:
Griffin Smith 2019-07-28 20:38:39 -04:00
parent ea648cfcdd
commit 24d38cb589

View file

@ -7,36 +7,22 @@ use crate::types::PositionedMut;
use alga::general::{ use alga::general::{
AbstractMagma, AbstractMonoid, AbstractSemigroup, Additive, Identity, AbstractMagma, AbstractMonoid, AbstractSemigroup, Additive, Identity,
}; };
use std::collections::{BTreeMap, HashMap}; use std::collections::{hash_map, BTreeMap, HashMap};
use std::iter::FromIterator; use std::iter::FromIterator;
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, Clone)]
pub struct EntityMap<A> { pub struct EntityMap<A> {
by_position: BTreeMap<Position, Vec<EntityID>>, by_position: BTreeMap<Position, Vec<EntityID>>,
by_id: HashMap<EntityID, A>, by_id: HashMap<EntityID, A>,
last_id: EntityID, last_id: EntityID,
} }
// impl<A: Debug> ArbitraryF1<A> for EntityMap<A> { impl<A: PartialEq> PartialEq for EntityMap<A> {
// type Parameters = (); fn eq(&self, other: &Self) -> bool {
// fn lift1_with<AS>(base: AS, _: Self::Parameters) -> BoxedStrategy<Self> self.by_position == other.by_position && self.by_id == other.by_id
// where }
// AS: Strategy<Value = A> + 'static, }
// { impl<A: Eq> Eq for EntityMap<A> {}
// 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 = const BY_POS_INVARIANT: &'static str =
"Invariant: All references in EntityMap.by_position should point to existent references in by_id"; "Invariant: All references in EntityMap.by_position should point to existent references in by_id";
@ -98,10 +84,18 @@ impl<A> EntityMap<A> {
self.by_id.values_mut() self.by_id.values_mut()
} }
pub fn ids(&self) -> impl Iterator<Item = &EntityID> { pub fn ids(&self) -> hash_map::Keys<'_, EntityID, A> {
self.by_id.keys() self.by_id.keys()
} }
pub fn drain<'a>(&'a mut self) -> Drain<'a, A> {
let ids = self.ids().map(|e| *e).collect::<Vec<_>>();
Drain {
map: self,
ids_iter: Box::new(ids.into_iter()),
}
}
fn next_id(&mut self) -> EntityID { fn next_id(&mut self) -> EntityID {
self.last_id += 1; self.last_id += 1;
self.last_id self.last_id
@ -124,22 +118,28 @@ impl<A: Positioned + Identified<EntityID>> EntityMap<A> {
/// Remove the entity with the given ID /// Remove the entity with the given ID
pub fn remove(&mut self, id: EntityID) -> Option<A> { pub fn remove(&mut self, id: EntityID) -> Option<A> {
self.by_id.remove(&id).map(|e| { self.by_id.remove(&id).map(|e| {
self.by_position let mut empty = false;
.get_mut(&e.position()) let position = e.position();
.map(|es| es.retain(|e| *e != id)); self.by_position.get_mut(&position).map(|es| {
es.retain(|e| *e != id);
if es.len() == 0 {
empty = true;
}
});
if empty {
self.by_position.remove(&position);
}
e e
}) })
} }
/// Moves all elements from `other` into `Self`, leathing `other` empty. /// Moves all elements from `other` into `Self`, leathing `other` empty.
pub fn append(&mut self, other: &mut Self) { pub fn append(&mut self, other: &mut Self) {
self.by_position.append(&mut other.by_position); // TODO there's probably some perf opportunities here by calling
self.by_id.reserve(other.len()); // reserve() on stuff
for (k, v) in other.by_id.drain() { for (_, entity) in other.drain() {
self.by_id.insert(k, v); self.insert(entity);
} }
self.last_id = self.last_id.max(other.last_id);
other.last_id = 0;
} }
/// Gets all 8 neighbors of the given position. /// Gets all 8 neighbors of the given position.
@ -158,6 +158,19 @@ impl<A: Positioned + Identified<EntityID>> EntityMap<A> {
) -> Neighbors<Vec<&'a A>> { ) -> Neighbors<Vec<&'a A>> {
self.neighbors(position).mapmap(&|(_eid, ent)| *ent) self.neighbors(position).mapmap(&|(_eid, ent)| *ent)
} }
pub fn check_invariants(&self) {
for (id, ent) in &self.by_id {
assert_eq!(*id, ent.id());
}
for (pos, ents) in &self.by_position {
for eid in ents {
let ent = self.by_id.get(eid).unwrap();
assert_eq!(*pos, ent.position())
}
}
}
} }
impl<'a, A: Positioned + Identified<EntityID>> IntoIterator impl<'a, A: Positioned + Identified<EntityID>> IntoIterator
@ -253,6 +266,21 @@ impl<A: PositionedMut> EntityMap<A> {
} }
} }
pub struct Drain<'a, A: 'a> {
map: &'a mut EntityMap<A>,
ids_iter: Box<dyn Iterator<Item = EntityID> + 'a>,
}
impl<A: Positioned + Identified<EntityID>> Iterator for Drain<'_, A> {
type Item = (EntityID, A);
fn next(&mut self) -> Option<Self::Item> {
self.ids_iter
.next()
.map(|eid| (eid, self.map.remove(eid).expect(BY_POS_INVARIANT)))
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -384,11 +412,24 @@ mod tests {
mut target in gen_entity_map(), mut target in gen_entity_map(),
mut source in gen_entity_map(), mut source in gen_entity_map(),
) { ) {
let orig_target = target.clone();
let orig_source = source.clone(); let orig_source = source.clone();
target.append(&mut source); target.append(&mut source);
target.check_invariants();
assert_eq!(source, EntityMap::new()); assert_eq!(source, EntityMap::new());
for (eid, e) in orig_source {
assert_eq!(target.get(eid), Some(&e)) for ent in orig_source.entities() {
assert!(
target.at(ent.position()).iter().any(|e| e.name == ent.name)
);
}
for ent in orig_target.entities() {
assert!(
target.at(ent.position()).iter().any(|e| e.name == ent.name)
);
} }
} }
} }