use crate::schema::players; /// Unpack a floating value of gold pieces to integer /// values of copper, silver, gold and platinum pieces /// /// # Note /// /// The conversion is slightly different than standard rules : /// ``` 1pp = 100gp = 1000sp = 10000 cp ``` /// fn unpack_gold_value(gold: f64) -> (i32, i32, i32, i32) { let rest = (gold.fract() * 100.0).round() as i32; let gold = gold.trunc() as i32; let pp = gold / 100; let gp = gold % 100; let sp = rest / 10; let cp = rest % 10; (cp, sp, gp, pp) } /// State of a player's wealth /// /// Values are held as individual pieces counts. /// Allows conversion from and to a floating amount of gold pieces. #[derive(Queryable, AsChangeset, Serialize, Deserialize, PartialEq, Copy, Clone, Debug)] #[table_name = "players"] pub struct Wealth { pub cp: i32, pub sp: i32, pub gp: i32, pub pp: i32, } impl Wealth { /// Unpack individual pieces counts from gold value /// /// # Examples /// ``` /// # use lootalot_db::models::Wealth; /// let wealth = Wealth::from_gp(403.21); /// assert_eq!(wealth.as_tuple(), (1, 2, 3, 4)); /// ``` pub fn from_gp(gp: f64) -> Self { let (cp, sp, gp, pp) = unpack_gold_value(gp); Self { cp, sp, gp, pp } } /// Convert total value to a floating value in gold pieces /// /// # Examples /// ``` /// # use lootalot_db::models::Wealth; /// let wealth = Wealth{ pp: 4, gp: 5, sp: 8, cp: 4}; /// assert_eq!(wealth.to_gp(), 405.84); /// ``` pub fn to_gp(&self) -> f64 { let i = self.pp * 100 + self.gp; let f = (self.sp * 10 + self.cp) as f64 / 100.0; i as f64 + f } /// Pack the counts inside a tuple, from lower to higher coin value. pub fn as_tuple(&self) -> (i32, i32, i32, i32) { (self.cp, self.sp, self.gp, self.pp) } } impl std::ops::Sub for Wealth { type Output = Self; /// What needs to be added to 'other' so that /// the result equals 'self' fn sub(self, other: Self) -> Self { Wealth { cp: self.cp - other.cp, sp: self.sp - other.sp, gp: self.gp - other.gp, pp: self.pp - other.pp, } } } impl std::ops::Add for Wealth { type Output = Self; fn add(self, other: Self) -> Self { Wealth { cp: self.cp + other.cp, sp: self.sp + other.sp, gp: self.gp + other.gp, pp: self.pp + other.pp } } } #[cfg(test)] mod tests { #[test] fn test_unpack_gold_values() { use super::unpack_gold_value; let test_values = [ (0.01, (1, 0, 0, 0)), (0.1, (0, 1, 0, 0)), (1.0, (0, 0, 1, 0)), (1.23, (3, 2, 1, 0)), (1.03, (3, 0, 1, 0)), (100.23, (3, 2, 0, 1)), (-100.23, (-3, -2, -0, -1)), (10189.23, (3, 2, 89, 101)), (141805.9, (0, 9, 5, 1418)), (123141805.9, (0, 9, 5, 1231418)), (-8090.20, (0, -2, -90, -80)), ]; for (tested, expected) in test_values.into_iter() { assert_eq!(unpack_gold_value(*tested), *expected); } } #[test] fn test_negative_wealth() { use super::Wealth; assert_eq!( Wealth{ cp: 3, sp: 2, gp: 1, pp: 0 } + Wealth{ cp: -8, pp: 0, sp: 0, gp: 0 }, Wealth::from_gp(1.23 - 0.08) ) } #[test] fn test_negative_wealth_inverse() { use super::Wealth; assert_eq!( (Wealth{ cp: 3, sp: 2, gp: 1, pp: 0 } + Wealth{ cp: -8, pp: 0, sp: 0, gp: 0 }).to_gp(), 1.23 - 0.08 ) } #[test] fn test_diff_adding() { use super::Wealth; /// Let say we add 0.08 gp /// 1.23 + 0.08 gold is 1.31, diff is cp: -2, sp: +1 let old = Wealth::from_gp(1.23); let new = Wealth::from_gp(1.31); let diff = new - old; assert_eq!(diff.as_tuple(), (-2, 1, 0, 0)); assert_eq!(diff.to_gp(), 0.08); assert_eq!(new - diff, old); } #[test] fn test_diff_subbing() { use super::Wealth; /// Let say we sub 0.08 gp /// 1.31 - 0.08 gold is 1.23, diff is cp: +2, sp: -1 let old = Wealth::from_gp(1.31); let new = Wealth::from_gp(1.23); let diff = new - old; assert_eq!(diff.as_tuple(), (2, -1, 0, 0)); assert_eq!(diff.to_gp(), -0.08); assert_eq!(new - diff, old); } }