use super::{characteristic::Characteristic, dice::DiceThrow, hero::Hero}; /// A skill identifier with a value how good the hero is in it. pub struct Skill { /// The identifier takes the form of `/` pub identifier: &'static str, pub value: i8, pub characteristics: [&'static str; 3], pub hero: Hero, } impl Skill { pub fn new( identifier: &'static str, value: i8, characteristics: [&'static str; 3], hero: Hero, ) -> Skill { Skill { identifier, value, characteristics, hero, } } pub fn trial( &self, dice_throws: [DiceThrow; 3], modifier: i8, ) -> ([DiceThrow; 3], SkillTrialResult) { let mut ones = 0; let mut twenties = 0; let mut per_trait_mod = 0; let mut remainder = self.value + modifier; if remainder < 0 { per_trait_mod = remainder; remainder = 0; } for i in 0..3 { let trait_trial_result = self.hero .characteristics .get(self.characteristics[i]) .unwrap() .trial(dice_throws[i], per_trait_mod); if trait_trial_result < 0 { remainder += trait_trial_result; } match dice_throws[i].throw { 20 => twenties += 1, 1 => ones += 1, _ => {} } } { use self::SkillTrialResult::*; ( dice_throws, match (ones, twenties, remainder) { (_, 3, _) => TripleTwenty, (_, 2, _) => DoubleTwenty, (3, _, _) => TripleOne, (2, _, _) => DoubleOne, (_, _, n) if n < 0 => Failure, (_, _, n) if n >= 0 && n <= 3 => Success(1), (_, _, n) if n >= 4 && n <= 6 => Success(2), (_, _, n) if n >= 7 && n <= 9 => Success(3), (_, _, n) if n >= 10 && n <= 12 => Success(4), (_, _, n) if n >= 13 && n <= 15 => Success(5), (_, _, n) if n >= 16 => Success(6), (_, _, _) => Failure, // TODO: exhaustive integer matching, this is unreachable, removal blocked by // https://github.com/rust-lang/rust/pull/50912 being in stable }, ) } } } pub enum SkillTrialResult { TripleTwenty, DoubleTwenty, Failure, Success(u8), DoubleOne, TripleOne, } #[cfg(test)] mod tests { use super::super::{ characteristic::Characteristic, dice::Dice, hero::Hero, skill::{Skill, SkillTrialResult}, }; #[test] fn create_skill() { let skill = skill(); } fn skill() -> Skill { let mut hero = Hero::new(); hero.add_characteristic("AA", 12); hero.add_characteristic("BB", 15); hero.add_characteristic("CC", 09); Skill::new("skill", 6, ["AA", "BB", "CC"], hero) } #[test] fn triple_twenty() { let skill = skill(); let d20 = Dice::new(20); let throws = [ d20.throw_with_result(20), d20.throw_with_result(20), d20.throw_with_result(20), ]; match skill.trial(throws, 0) { (_, SkillTrialResult::TripleTwenty) => {} (_, _) => panic!("Putting in three twenties doesn't return triple twenty"), } } #[test] fn double_twenty() { let skill = skill(); let d20 = Dice::new(20); let throws = [ d20.throw_with_result(20), d20.throw_with_result(19), d20.throw_with_result(20), ]; match skill.trial(throws, 0) { (_, SkillTrialResult::DoubleTwenty) => {} (_, _) => panic!("Putting in two twenties doesn't return double twenty"), } } #[test] fn triple_one() { let skill = skill(); let d20 = Dice::new(20); let throws = [ d20.throw_with_result(1), d20.throw_with_result(1), d20.throw_with_result(1), ]; match skill.trial(throws, 0) { (_, SkillTrialResult::TripleOne) => {} (_, _) => panic!("Putting in three ones doesn't return triple one"), } } #[test] fn double_one() { let skill = skill(); let d20 = Dice::new(20); let throws = [ d20.throw_with_result(1), d20.throw_with_result(1), d20.throw_with_result(20), ]; match skill.trial(throws, 0) { (_, SkillTrialResult::DoubleOne) => {} (_, _) => panic!("Putting in two ones doesn't return double one"), } } #[test] fn failure() { let skill = skill(); let d20 = Dice::new(20); let throws = [ d20.throw_with_result(19), d20.throw_with_result(19), d20.throw_with_result(19), ]; match skill.trial(throws, 0) { (_, SkillTrialResult::Failure) => {} (_, _) => panic!("Failing doesn't fail"), } } #[test] fn success_qs1() { let skill = skill(); let d20 = Dice::new(20); let throws = [ d20.throw_with_result(2), d20.throw_with_result(2), d20.throw_with_result(2), ]; match skill.trial(throws, -4) { (_, SkillTrialResult::Success(1)) => {} (_, _) => panic!("QS 1 is failing"), } } #[test] fn success_qs2() { let skill = skill(); let d20 = Dice::new(20); let throws = [ d20.throw_with_result(2), d20.throw_with_result(2), d20.throw_with_result(2), ]; match skill.trial(throws, -1) { (_, SkillTrialResult::Success(2)) => {} (_, _) => panic!("QS 1 is failing"), } } #[test] fn success_qs3() { let skill = skill(); let d20 = Dice::new(20); let throws = [ d20.throw_with_result(2), d20.throw_with_result(2), d20.throw_with_result(2), ]; match skill.trial(throws, 2) { (_, SkillTrialResult::Success(3)) => {} (_, _) => panic!("QS 1 is failing"), } } #[test] fn success_qs4() { let skill = skill(); let d20 = Dice::new(20); let throws = [ d20.throw_with_result(2), d20.throw_with_result(2), d20.throw_with_result(2), ]; match skill.trial(throws, 5) { (_, SkillTrialResult::Success(4)) => {} (_, _) => panic!("QS 1 is failing"), } } #[test] fn success_qs5() { let skill = skill(); let d20 = Dice::new(20); let throws = [ d20.throw_with_result(2), d20.throw_with_result(2), d20.throw_with_result(2), ]; match skill.trial(throws, 8) { (_, SkillTrialResult::Success(5)) => {} (_, _) => panic!("QS 1 is failing"), } } #[test] fn success_qs6() { let skill = skill(); let d20 = Dice::new(20); let throws = [ d20.throw_with_result(2), d20.throw_with_result(2), d20.throw_with_result(2), ]; match skill.trial(throws, 11) { (_, SkillTrialResult::Success(6)) => {} (_, _) => panic!("QS 1 is failing"), } } #[test] fn success_with_19_qs6() { let skill = skill(); let d20 = Dice::new(20); let throws = [ d20.throw_with_result(19), d20.throw_with_result(2), d20.throw_with_result(2), ]; match skill.trial(throws, 20) { (_, SkillTrialResult::Success(6)) => {} (_, _) => panic!("QS 1 is failing"), } } } struct A { bs: Vec, } struct B { a: A }