diff --git a/engine/src/collections.rs b/engine/src/collections.rs new file mode 100644 index 0000000..dcb2150 --- /dev/null +++ b/engine/src/collections.rs @@ -0,0 +1,124 @@ +/// A vec-backed set for cache-friendly queries +#[derive(Debug)] +pub struct VecSet { + inner: Vec> +} + +impl VecSet { + pub fn insert(&mut self, value: T) { + // Check if value already in set, otherwise add it + if !self.contains(&value) { + self.inner.push(Some(value)) + } + } + + pub fn remove(&mut self, value: &T) { + self.inner.iter_mut().for_each(|val| { + if val.as_ref() == Some(value) { + *val = None; + } + }) + } + + pub fn compact(&mut self) { + self.inner.retain(|val| val.is_some()) + } + + pub fn clear(&mut self) { + self.inner.clear(); + } + + pub fn contains(&self, value: &T) -> bool { + self.iter().any(|v| v == value) + } + + pub fn iter(&self) -> impl Iterator { + self.inner.iter().filter_map(|v| v.as_ref()) + } +} + +impl FromIterator for VecSet { + fn from_iter>(iter: I) -> Self { + VecSet:: { + inner: iter.into_iter().map(|this| Some(this)).collect() + } + } +} + +impl Default for VecSet { + fn default() -> Self { + VecSet { inner: Vec::new() } + } +} + +/// A vec-backed map for cache-friendly queries +#[derive(Debug)] +pub struct VecMap { + inner: Vec> +} + +impl VecMap { + pub fn insert(&mut self, key: K, val: V) { + // Check and remove/overwrite existing entry + if let Some(v) = self.get_mut(&key) { + // Update value at key + *v = val; + } else { + self.inner.push(Some((key, val))); + } + } + + pub fn get(&self, key: &K) -> Option<&V> { + self.iter().find_map(|(k, v)| (k == key).then_some(v)) + } + + pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { + self.iter_mut().find_map(|(k, v)| (k == key).then_some(v)) + } + + pub fn remove(&mut self, key: &K) { + self.get_mut(key).take(); + } + + pub fn iter(&self) -> impl Iterator { + self.inner.iter().filter_map(|kv| kv.as_ref()) + } + + pub fn iter_mut(&mut self) -> impl Iterator { + self.inner.iter_mut().filter_map(|kv| kv.as_mut()) + } + + pub fn compact(&mut self) { + self.inner.retain(|v| v.is_some()); + } + + pub fn clear(&mut self) { + self.inner.clear(); + } + + pub fn keys(&self) -> impl Iterator { + self.inner.iter().filter_map(|this| this.as_ref().map(|(k,_)| k)) + } + + pub fn contains_key(&self, key: &K) -> bool { + self.keys().any(|k| k == key) + } + + pub fn values(&self) -> impl Iterator { + self.inner.iter().filter_map(|this| this.as_ref().map(|(_,v)| v)) + } +} + +impl FromIterator<(K,V)> for VecMap { + fn from_iter>(iter: I) -> Self { + VecMap:: { + inner: iter.into_iter().map(|this| Some(this)).collect() + } + } +} + +impl Default for VecMap { + fn default() -> Self { + VecMap { inner: Vec::new() } + } +} diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 5e4347b..6f0ffe9 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -1,6 +1,11 @@ +/// Actions handler framework pub mod actions; pub use actions::*; +/// Custom collections using Vec for cache-friendly queries +pub mod collections; +pub use collections::*; + pub mod color; use bevy::ecs::query::QueryFilter; pub use color::*; diff --git a/life/src/main.rs b/life/src/main.rs index 02d3617..0a8149d 100644 --- a/life/src/main.rs +++ b/life/src/main.rs @@ -820,10 +820,10 @@ enum SimulationPlayback { fn simulation_step( q: Query<(Entity, &Coordinates), With>, mut writer: MessageWriter, - mut alive: Local>, - mut candidates: Local>, + mut alive: Local>, + mut candidates: Local>, ) { - let living_neighbors = |a: &HashMap, c: &Coordinates| -> usize { + let living_neighbors = |a: &VecMap, c: &Coordinates| -> usize { c.neighbors() .filter(|neighbor| { a.contains_key(neighbor) @@ -938,8 +938,8 @@ fn cooldown_secs(input: f32) -> impl FnMut(Local, Res