diff --git a/Cargo.toml b/Cargo.toml index 3778407..b2ae6ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ rpath = false [dependencies] # Used for PrimInt, so we can check if a generic is an integer num-traits = "0.2" -atomic_prim_traits = "0.2.0" +atomig = "0.4.0" # Allow us to assert whether floats are approximately a value assert_approx_eq = "1.1.0" # Used for time based code (i.e. randomness) diff --git a/src/dynamic_data/libraries/battle_stat_calculator.rs b/src/dynamic_data/libraries/battle_stat_calculator.rs index 32ca1da..b058af0 100644 --- a/src/dynamic_data/libraries/battle_stat_calculator.rs +++ b/src/dynamic_data/libraries/battle_stat_calculator.rs @@ -1,5 +1,4 @@ use std::fmt::Debug; -use std::sync::atomic::AtomicU32; use crate::dynamic_data::Pokemon; use crate::static_data::Statistic; @@ -8,11 +7,11 @@ use crate::static_data::StatisticSet; /// A battle stat calculator is used to calculate stats for a Pokemon. pub trait BattleStatCalculator: Debug { /// Calculate all the flat stats of a Pokemon, disregarding stat boosts. - fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet); + fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet); /// Calculate a single flat stat of a Pokemon, disregarding stat boost fn calculate_flat_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32; /// Calculate all the boosted stats of a Pokemon, including stat boosts. - fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet); + fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet); /// Calculate a single boosted stat of a Pokemon, including stat boosts. fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32; } @@ -64,7 +63,7 @@ impl Gen7BattleStatCalculator { } impl BattleStatCalculator for Gen7BattleStatCalculator { - fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet) { + fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet) { stats.set_stat(Statistic::HP, self.calculate_health_stat(pokemon)); stats.set_stat(Statistic::Attack, self.calculate_other_stat(pokemon, Statistic::Attack)); stats.set_stat( @@ -90,7 +89,7 @@ impl BattleStatCalculator for Gen7BattleStatCalculator { } } - fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet) { + fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet) { stats.set_stat(Statistic::HP, self.calculate_boosted_stat(pokemon, Statistic::HP)); stats.set_stat( Statistic::Attack, diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs index 00e598a..1c5afe3 100644 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -1,5 +1,5 @@ use std::ops::{Deref, DerefMut}; -use std::sync::atomic::{AtomicBool, AtomicI8, AtomicU32, AtomicU8, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU8, Ordering}; use std::sync::{Arc, Weak}; use atomic::Atomic; @@ -67,16 +67,16 @@ where height: Atomic, /// The stats of the Pokemon when disregarding any stat boosts. - flat_stats: StatisticSet, + flat_stats: StatisticSet, /// The statistics boosts of the Pokemon. Will prevent the value from going above 6, and below /// -6. - stat_boost: ClampedStatisticSet, + stat_boost: ClampedStatisticSet, /// The stats of the Pokemon including the stat boosts - boosted_stats: StatisticSet, + boosted_stats: StatisticSet, /// The [individual values](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon. - individual_values: ClampedStatisticSet, + individual_values: ClampedStatisticSet, /// The [effort values](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon. - effort_values: ClampedStatisticSet, + effort_values: ClampedStatisticSet, /// The [nature](https://bulbapedia.bulbagarden.net/wiki/Nature) of the Pokemon. nature: &'own Nature, @@ -304,11 +304,11 @@ impl<'own, 'library> Pokemon<'own, 'library> { } /// The stats of the Pokemon when disregarding any stat boosts. - pub fn flat_stats(&self) -> &StatisticSet { + pub fn flat_stats(&self) -> &StatisticSet { &self.flat_stats } /// The stats of the Pokemon including the stat boosts - pub fn boosted_stats(&self) -> &StatisticSet { + pub fn boosted_stats(&self) -> &StatisticSet { &self.boosted_stats } /// Get the stat boosts for a specific stat. @@ -365,11 +365,11 @@ impl<'own, 'library> Pokemon<'own, 'library> { } /// The [individual values](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon. - pub fn individual_values(&self) -> &ClampedStatisticSet { + pub fn individual_values(&self) -> &ClampedStatisticSet { &self.individual_values } /// The [effort values](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon. - pub fn effort_values(&self) -> &ClampedStatisticSet { + pub fn effort_values(&self) -> &ClampedStatisticSet { &self.effort_values } diff --git a/src/static_data/statistic_set.rs b/src/static_data/statistic_set.rs index f71af37..9356059 100644 --- a/src/static_data/statistic_set.rs +++ b/src/static_data/statistic_set.rs @@ -1,6 +1,7 @@ use std::sync::atomic::Ordering; -use atomic_prim_traits::AtomicInt; +use atomig::impls::{PrimitiveAtom, PrimitiveAtomInteger}; +use atomig::{Atom, AtomInteger, Atomic}; use num_traits::{clamp, NumCast, PrimInt}; use super::statistics::Statistic; @@ -9,75 +10,76 @@ use super::statistics::Statistic; /// type, and can be modified at will. /// /// As all data in this type is atomic, threaded access to this struct is completely legal. -#[derive(Default, Eq, PartialEq, Clone, Debug)] +#[derive(Default, Debug)] pub struct StatisticSet where - T: AtomicInt, + T: PrimitiveAtom, + T: Atom, + T: PrimitiveAtomInteger, + ::Repr: PrimitiveAtomInteger, + T: AtomInteger, { /// The health point stat value. - hp: T, + hp: Atomic, /// The physical attack stat value. - attack: T, + attack: Atomic, /// The physical defense stat value. - defense: T, + defense: Atomic, /// The special attack stat value. - special_attack: T, + special_attack: Atomic, /// The special defense stat value. - special_defense: T, + special_defense: Atomic, /// The speed stat value. - speed: T, + speed: Atomic, } impl StatisticSet where - T: AtomicInt, + T: PrimitiveAtom, + T: Atom, + T: PrimitiveAtomInteger, + ::Repr: PrimitiveAtomInteger, + T: AtomInteger, { /// Creates a new statistic set with given stats. - pub fn new( - hp: T::Prim, - attack: T::Prim, - defense: T::Prim, - special_attack: T::Prim, - special_defense: T::Prim, - speed: T::Prim, - ) -> Self { + pub fn new(hp: T, attack: T, defense: T, special_attack: T, special_defense: T, speed: T) -> Self { Self { - hp: T::new(hp), - attack: T::new(attack), - defense: T::new(defense), - special_attack: T::new(special_attack), - special_defense: T::new(special_defense), - speed: T::new(speed), + hp: Atomic::::new(hp), + attack: Atomic::::new(attack), + defense: Atomic::::new(defense), + special_attack: Atomic::::new(special_attack), + special_defense: Atomic::::new(special_defense), + speed: Atomic::::new(speed), } } /// The health point stat value. - pub fn hp(&self) -> T::Prim { + pub fn hp(&self) -> T { self.hp.load(Ordering::Relaxed) } /// The physical attack stat value. - pub fn attack(&self) -> T::Prim { + pub fn attack(&self) -> T { self.attack.load(Ordering::Relaxed) } /// The physical defense stat value. - pub fn defense(&self) -> T::Prim { + pub fn defense(&self) -> T { self.defense.load(Ordering::Relaxed) } /// The special attack stat value. - pub fn special_attack(&self) -> T::Prim { + pub fn special_attack(&self) -> T { self.special_attack.load(Ordering::Relaxed) } /// The special defense stat value. - pub fn special_defense(&self) -> T::Prim { + pub fn special_defense(&self) -> T { self.special_defense.load(Ordering::Relaxed) } /// The speed stat value. - pub fn speed(&self) -> T::Prim { + pub fn speed(&self) -> T { self.speed.load(Ordering::Relaxed) } /// Get the value of a specific stat - pub fn get_stat(&self, stat: Statistic) -> T::Prim { + pub fn get_stat(&self, stat: Statistic) -> T { match stat { Statistic::HP => self.hp.load(Ordering::Relaxed), Statistic::Attack => self.attack.load(Ordering::Relaxed), @@ -89,7 +91,7 @@ where } /// Modify the value of a specific stat. - pub fn set_stat(&self, stat: Statistic, value: T::Prim) { + pub fn set_stat(&self, stat: Statistic, value: T) { match stat { Statistic::HP => self.hp.store(value, Ordering::SeqCst), Statistic::Attack => self.attack.store(value, Ordering::SeqCst), @@ -101,7 +103,7 @@ where } /// Increase the value of a given stat by a value. - pub fn increase_stat(&self, stat: Statistic, value: T::Prim) { + pub fn increase_stat(&self, stat: Statistic, value: T) { match stat { Statistic::HP => self.hp.fetch_add(value, Ordering::SeqCst), Statistic::Attack => self.attack.fetch_add(value, Ordering::SeqCst), @@ -113,7 +115,7 @@ where } /// Decrease the value of a given stat by a value. - pub fn decrease_stat(&self, stat: Statistic, value: T::Prim) { + pub fn decrease_stat(&self, stat: Statistic, value: T) { match stat { Statistic::HP => self.hp.fetch_sub(value, Ordering::SeqCst), Statistic::Attack => self.attack.fetch_sub(value, Ordering::SeqCst), @@ -163,7 +165,7 @@ where } } - /// The health point stat value.= + /// The health point stat value. pub const fn hp(&self) -> T { self.hp } @@ -201,53 +203,64 @@ where } } -#[derive(Default, Eq, PartialEq, Clone, Debug)] +/// A clamped statistic set holds the 6 normal stats for a Pokemon, but ensures it always remains +/// between two values (inclusive on the two values). +#[derive(Default, Debug)] pub struct ClampedStatisticSet where - T: AtomicInt, + T: PrimitiveAtom, + T: Atom, + T: PrimitiveAtomInteger, + ::Repr: PrimitiveAtomInteger, + T: AtomInteger, + T: NumCast, + T: PrimInt, { /// The health point stat value. - hp: T, + hp: Atomic, /// The physical attack stat value. - attack: T, + attack: Atomic, /// The physical defense stat value. - defense: T, + defense: Atomic, /// The special attack stat value. - special_attack: T, + special_attack: Atomic, /// The special defense stat value. - special_defense: T, + special_defense: Atomic, /// The speed stat value. - speed: T, + speed: Atomic, } /// A clamped statistic set is a collection of each statistics that can be modified, but that will -/// always remain between two compile time constant values (Min, Max). +/// always remain between two compile time constant values (Min, Max). Values here are stored as +/// atomics to ensure thread safety. impl ClampedStatisticSet where - T: AtomicInt, - ::Prim: NumCast, - ::Prim: PrimInt, + T: PrimitiveAtom, + T: Atom, + T: PrimitiveAtomInteger, + ::Repr: PrimitiveAtomInteger, + T: AtomInteger, + T: NumCast, + T: PrimInt, { - pub fn min() -> T::Prim { - <::Prim as NumCast>::from(MIN).unwrap() + /// The lowest value a value on the set can have. + pub fn min() -> T { + ::from(MIN).unwrap() } - pub fn max() -> T::Prim { - <::Prim as NumCast>::from(MAX).unwrap() + /// The highest value a value on the set can have. + pub fn max() -> T { + ::from(MAX).unwrap() } - fn clamped_cast(v: T::Prim) -> T { + /// Takes the underlying primary value, clamp it between the two values, and give it back as + /// atomic. + fn clamped_cast(v: T) -> Atomic { let v = clamp(v, Self::min(), Self::max()); - T::new(v) + Atomic::::new(v) } - pub fn new( - hp: T::Prim, - attack: T::Prim, - defense: T::Prim, - special_attack: T::Prim, - special_defense: T::Prim, - speed: T::Prim, - ) -> Self { + /// Instantiates a new clamped statistic set. + pub fn new(hp: T, attack: T, defense: T, special_attack: T, special_defense: T, speed: T) -> Self { Self { hp: Self::clamped_cast(hp), attack: Self::clamped_cast(attack), @@ -258,26 +271,33 @@ where } } - pub fn hp(&self) -> T::Prim { + /// The health point stat value. + pub fn hp(&self) -> T { self.hp.load(Ordering::Relaxed) } - pub fn attack(&self) -> T::Prim { + /// The physical attack stat value. + pub fn attack(&self) -> T { self.attack.load(Ordering::Relaxed) } - pub fn defense(&self) -> T::Prim { + /// The physical defense stat value. + pub fn defense(&self) -> T { self.defense.load(Ordering::Relaxed) } - pub fn special_attack(&self) -> T::Prim { + /// The special attack stat value. + pub fn special_attack(&self) -> T { self.special_attack.load(Ordering::Relaxed) } - pub fn special_defense(&self) -> T::Prim { + /// The special defense stat value. + pub fn special_defense(&self) -> T { self.special_defense.load(Ordering::Relaxed) } - pub fn speed(&self) -> T::Prim { + /// The speed stat value. + pub fn speed(&self) -> T { self.speed.load(Ordering::Relaxed) } - pub fn get_stat(&self, stat: Statistic) -> T::Prim { + /// Gets a specific stat. + pub fn get_stat(&self, stat: Statistic) -> T { match stat { Statistic::HP => self.hp.load(Ordering::Relaxed), Statistic::Attack => self.attack.load(Ordering::Relaxed), @@ -288,7 +308,8 @@ where } } - pub fn set_stat(&self, stat: Statistic, mut value: T::Prim) { + /// Modifies a specific stat. This will ensure the value remains within the min/max range. + pub fn set_stat(&self, stat: Statistic, mut value: T) { if value < Self::min() { value = Self::min(); } else if value > Self::max() { @@ -304,27 +325,32 @@ where } } - fn change_stat(v: &T, f: F) -> bool + /// Changes a value to a certain amount. Returns false if the value already has that value. True + /// if the value was changed. + fn change_stat(v: &Atomic, f: F) -> bool where - F: FnOnce(T::Prim) -> T::Prim, + F: Fn(T) -> T, { - let original_value = v.load(Ordering::Relaxed); - let mut new_value = f(original_value); - if new_value < Self::min() { - new_value = Self::min(); - } else if new_value > Self::max() { - new_value = Self::max(); - } - if original_value == new_value { - return false; - } - v.store(new_value, Ordering::SeqCst); - true + let res = v.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |original_value| { + let mut new_value = f(original_value); + if new_value < Self::min() { + new_value = Self::min(); + } else if new_value > Self::max() { + new_value = Self::max(); + } + if original_value == new_value { + None + } else { + Some(new_value) + } + }); + res.is_ok() } - pub fn increase_stat(&self, stat: Statistic, value: T::Prim) -> bool + /// Increases a stat by a certain amount. Returns false if the stat was not changed. + pub fn increase_stat(&self, stat: Statistic, value: T) -> bool where - ::Prim: PrimInt, + ::Repr: PrimInt, { match stat { Statistic::HP => Self::change_stat(&self.hp, |a| a + value), @@ -336,7 +362,8 @@ where } } - pub fn decrease_stat(&self, stat: Statistic, value: T::Prim) -> bool { + /// Decreases a stat by a certain amount. Returns false if the stat was not changed. + pub fn decrease_stat(&self, stat: Statistic, value: T) -> bool { match stat { Statistic::HP => Self::change_stat(&self.hp, |a| a - value), Statistic::Attack => Self::change_stat(&self.attack, |a| a - value), @@ -350,13 +377,11 @@ where #[cfg(test)] mod tests { - use std::sync::atomic::AtomicI32; - use super::*; #[test] fn create_get_values() { - let set = StatisticSet::::new(1, 2, 3, 4, 5, 6); + let set = StatisticSet::::new(1, 2, 3, 4, 5, 6); assert_eq!(set.hp(), 1); assert_eq!(set.get_stat(Statistic::HP), 1); assert_eq!(set.attack(), 2); @@ -373,7 +398,7 @@ mod tests { #[test] fn create_set_value() { - let set = StatisticSet::::new(1, 2, 3, 4, 5, 6); + let set = StatisticSet::::new(1, 2, 3, 4, 5, 6); set.set_stat(Statistic::HP, 20); assert_eq!(set.hp(), 20); @@ -391,21 +416,21 @@ mod tests { #[test] fn create_increase_value() { - let set = StatisticSet::::new(1, 2, 3, 4, 5, 6); + let set = StatisticSet::::new(1, 2, 3, 4, 5, 6); set.increase_stat(Statistic::SpecialAttack, 5); assert_eq!(set.special_attack(), 9); } #[test] fn create_decrease_value() { - let set = StatisticSet::::new(1, 2, 3, 4, 5, 6); + let set = StatisticSet::::new(1, 2, 3, 4, 5, 6); set.decrease_stat(Statistic::SpecialAttack, 5); assert_eq!(set.special_attack(), -1); } #[test] fn create_clamped_get_values() { - let set = ClampedStatisticSet::::new(-5, 2, 3, 4, 5, 6); + let set = ClampedStatisticSet::::new(-5, 2, 3, 4, 5, 6); assert_eq!(set.hp(), -2); assert_eq!(set.get_stat(Statistic::HP), -2); assert_eq!(set.attack(), 2); @@ -422,7 +447,7 @@ mod tests { #[test] fn create_clamped_set_value() { - let set = ClampedStatisticSet::::new(1, 2, 3, 4, 5, 6); + let set = ClampedStatisticSet::::new(1, 2, 3, 4, 5, 6); set.set_stat(Statistic::SpecialAttack, 20); assert_eq!(set.special_attack(), 4); set.set_stat(Statistic::SpecialAttack, -10); @@ -431,7 +456,7 @@ mod tests { #[test] fn create_clamped_increase_value() { - let set = ClampedStatisticSet::::new(1, 2, 3, 2, 2, 6); + let set = ClampedStatisticSet::::new(1, 2, 3, 2, 2, 6); let mut has_changed = set.increase_stat(Statistic::SpecialAttack, 20); assert!(has_changed); assert_eq!(set.special_attack(), 4); @@ -442,7 +467,7 @@ mod tests { #[test] fn create_clamped_decrease_value() { - let set = ClampedStatisticSet::::new(1, 2, 3, 2, 2, 6); + let set = ClampedStatisticSet::::new(1, 2, 3, 2, 2, 6); let mut has_changed = set.decrease_stat(Statistic::SpecialAttack, 20); assert!(has_changed); assert_eq!(set.special_attack(), -2);