use atomig::Atom; use hashbrown::HashMap; use crate::StringKey; /// A unique key that can be used to store a reference to a type. Opaque reference to a byte /// internally. #[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Atom)] #[repr(C)] pub struct TypeIdentifier { /// The unique internal value. val: u8, } impl From for TypeIdentifier { fn from(val: u8) -> Self { Self { val } } } impl From for u8 { fn from(id: TypeIdentifier) -> Self { id.val } } /// All data related to types and effectiveness. #[derive(Debug)] #[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] pub struct TypeLibrary { /// A list of types types: HashMap, /// The effectiveness of the different types against each other. effectiveness: Vec>, } impl TypeLibrary { /// Instantiates a new type library with a specific capacity. pub fn new(capacity: usize) -> TypeLibrary { TypeLibrary { types: HashMap::with_capacity(capacity), effectiveness: vec![], } } /// Gets the type identifier for a type with a name. pub fn get_type_id(&self, key: &StringKey) -> TypeIdentifier { self.types[key] } /// Gets the effectiveness for a single attacking type against a single defending type. pub fn get_single_effectiveness(&self, attacking: TypeIdentifier, defending: TypeIdentifier) -> f32 { self.effectiveness[attacking.val as usize][defending.val as usize] } /// Gets the effectiveness for a single attacking type against an amount of defending types. /// This is equivalent to running [`get_single_effectiveness`] on each defending type, and /// multiplying the results with each other. pub fn get_effectiveness(&self, attacking: TypeIdentifier, defending: &[TypeIdentifier]) -> f32 { let mut e = 1.0; for def in defending { e *= self.get_single_effectiveness(attacking, *def); } e } /// Registers a new type in the library. pub fn register_type(&mut self, name: &StringKey) -> TypeIdentifier { let id = TypeIdentifier { val: self.types.len() as u8, }; self.types.insert(name.clone(), id); self.effectiveness.resize((id.val + 1) as usize, vec![]); for effectiveness in &mut self.effectiveness { effectiveness.resize((id.val + 1) as usize, 1.0) } id } /// Sets the effectiveness for an attacking type against a defending type. pub fn set_effectiveness(&mut self, attacking: TypeIdentifier, defending: TypeIdentifier, effectiveness: f32) { self.effectiveness[attacking.val as usize][defending.val as usize] = effectiveness; } } #[cfg(test)] pub mod tests { use assert_approx_eq::assert_approx_eq; use crate::static_data::libraries::type_library::TypeLibrary; pub fn build() -> TypeLibrary { let mut lib = TypeLibrary::new(2); // Borrow as mut so we can insert let w = &mut lib; let t0 = w.register_type(&"foo".into()); let t1 = w.register_type(&"bar".into()); // Drops borrow as mut w.set_effectiveness(t0, t1, 0.5); w.set_effectiveness(t1, t0, 2.0); lib } #[test] fn add_two_types_retrieve_them() { let mut lib = TypeLibrary::new(2); // Borrow as mut so we can insert let w = &mut lib; let t0 = w.register_type(&"foo".into()); let t1 = w.register_type(&"bar".into()); // Drops borrow as mut // Borrow as read so we can read let r = &lib; assert_eq!(r.get_type_id(&"foo".into()), t0); assert_eq!(r.get_type_id(&"bar".into()), t1); } #[test] fn add_two_types_set_effectiveness_retrieve() { let mut lib = TypeLibrary::new(2); // Borrow as mut so we can insert let w = &mut lib; let t0 = w.register_type(&"foo".into()); let t1 = w.register_type(&"bar".into()); w.set_effectiveness(t0, t1, 0.5); w.set_effectiveness(t1, t0, 2.0); // Drops borrow as mut // Borrow as read so we can read let r = &lib; assert_approx_eq!(r.get_single_effectiveness(t0, t1), 0.5); assert_approx_eq!(r.get_single_effectiveness(t1, t0), 2.0); } #[test] fn add_two_types_get_aggregate_effectiveness() { let mut lib = TypeLibrary::new(2); // Borrow as mut so we can insert let w = &mut lib; let t0 = w.register_type(&"foo".into()); let t1 = w.register_type(&"bar".into()); w.set_effectiveness(t0, t1, 0.5); w.set_effectiveness(t1, t0, 2.0); // Drops borrow as mut // Borrow as read so we can read let r = &lib; assert_approx_eq!(r.get_effectiveness(t0, &[t1, t1]), 0.25); assert_approx_eq!(r.get_effectiveness(t1, &[t0, t0]), 4.0); } }