From 7ae33fba15b2adf9f903869b3c896a7490427b04 Mon Sep 17 00:00:00 2001 From: Arne Dußin Date: Mon, 26 Apr 2021 10:06:58 +0200 Subject: Initial commit --- src/dice.rs | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 src/dice.rs (limited to 'src/dice.rs') diff --git a/src/dice.rs b/src/dice.rs new file mode 100644 index 0000000..c89c4c9 --- /dev/null +++ b/src/dice.rs @@ -0,0 +1,227 @@ +use std::ops::Add; + +use rand::rngs::ThreadRng; +use rand::Rng; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct RiskDie(u8); +pub const NUM_DIE_LEVELS: usize = 6; +pub const RISK_DIE_VALUES: [u8; NUM_DIE_LEVELS] = [4, 6, 8, 10, 12, 20]; +pub const DELTA_4: RiskDie = RiskDie(0); +pub const DELTA_6: RiskDie = RiskDie(1); +pub const DELTA_8: RiskDie = RiskDie(2); +pub const DELTA_10: RiskDie = RiskDie(3); +pub const DELTA_12: RiskDie = RiskDie(4); +pub const DELTA_20: RiskDie = RiskDie(5); + +#[derive(Debug, PartialEq, Eq)] +pub enum Error +{ + InvalidLevel, + InvalidValue, +} + +impl RiskDie +{ + pub fn from_level(level: u8) -> Result + { + if (level as usize) < RISK_DIE_VALUES.len() { + Ok(Self(level)) + } + else { + Err(Error::InvalidLevel) + } + } + + pub fn from_value(value: u8) -> Result + { + for (i, val) in RISK_DIE_VALUES.iter().enumerate() { + if *val == value { + return Ok(Self(i as u8)); + } + } + + Err(Error::InvalidValue) + } + + pub fn level(&self) -> u8 { self.0 } + + pub fn value(&self) -> u8 { RISK_DIE_VALUES[self.0 as usize] } + + /// Roll the die. If the die shows 3 or less, it returns the next lower die. + /// If it is more, it returns itself. If it was already the lowest die value + /// and it would be stepped down it returns `None`. + pub fn roll(self) -> Option + { + let mut rng = rand::thread_rng(); + self.roll_with_rng(&mut rng) + } + + pub fn roll_with_rng(self, rng: &mut ThreadRng) -> Option + { + let rolled = rng.gen_range(1..=self.value()); + if rolled > 3 { + Some(self) + } + else { + self.level_down() + } + } + + pub fn can_be_lowered(&self) -> bool { self.0 > 0 } + + pub fn level_down(mut self) -> Option + { + if self.can_be_lowered() { + self.0 -= 1; + Some(self) + } + else { + /* Resource depleted */ + None + } + } + + pub fn can_be_upped(&self) -> bool { self.0 < NUM_DIE_LEVELS as u8 - 1 } + + pub fn level_up(mut self) -> Self + { + if self.can_be_upped() { + self.0 += 1; + } + + self + } +} + +#[rustfmt::skip] +const DELTA_ADDITION_TABLE: [[u8; NUM_DIE_LEVELS]; NUM_DIE_LEVELS] = [ + /* ------------------- with d4 d6 d8 d10 d12 d20 */ + /* delta 4 combinations */ [1, 2, 3, 4, 4, 5], + /* delta 6 combinations */ [2, 2, 3, 4, 5, 5], + /* delta 8 combinations */ [3, 3, 4, 4, 5, 5], + /* delta 10 combinations */ [4, 4, 4, 5, 5, 5], + /* delta 12 combinations */ [4, 5, 5, 5, 5, 5], + /* delta 20 combinations */ [5, 5, 5, 5, 5, 5], +]; +impl Add for RiskDie +{ + type Output = Self; + + fn add(self, other: Self) -> Self::Output + { + RiskDie(DELTA_ADDITION_TABLE[self.0 as usize][other.0 as usize]) + } +} + +#[cfg(test)] +mod tests +{ + use super::*; + + #[test] + fn create_die_from_level() + { + let die = RiskDie::from_level(0).expect("Unable to create die for correct level"); + assert_eq!(die.value(), 4); + let die = RiskDie::from_level(1).expect("Unable to create die for correct level"); + assert_eq!(die.value(), 6); + let die = RiskDie::from_level(2).expect("Unable to create die for correct level"); + assert_eq!(die.value(), 8); + let die = RiskDie::from_level(3).expect("Unable to create die for correct level"); + assert_eq!(die.value(), 10); + let die = RiskDie::from_level(4).expect("Unable to create die for correct level"); + assert_eq!(die.value(), 12); + let die = RiskDie::from_level(5).expect("Unable to create die for correct level"); + assert_eq!(die.value(), 20); + } + + #[test] + fn create_die_from_invalid_level() + { + assert_eq!( + RiskDie::from_level(6).expect_err("Should not be able to create die from level"), + Error::InvalidLevel + ); + } + + #[test] + fn create_from_value() + { + let die = RiskDie::from_value(4).expect("Unable to create die from correct value"); + assert_eq!(die.level(), 0); + let die = RiskDie::from_value(6).expect("Unable to create die from correct value"); + assert_eq!(die.level(), 1); + let die = RiskDie::from_value(8).expect("Unable to create die from correct value"); + assert_eq!(die.level(), 2); + let die = RiskDie::from_value(10).expect("Unable to create die from correct value"); + assert_eq!(die.level(), 3); + let die = RiskDie::from_value(12).expect("Unable to create die from correct value"); + assert_eq!(die.level(), 4); + let die = RiskDie::from_value(20).expect("Unable to create die from correct value"); + assert_eq!(die.level(), 5); + } + + #[test] + fn level_down() + { + assert_eq!(DELTA_4.level_down(), None); + assert_eq!(DELTA_6.level_down(), Some(RiskDie(0))); + assert_eq!(DELTA_8.level_down(), Some(RiskDie(1))); + assert_eq!(DELTA_10.level_down(), Some(RiskDie(2))); + assert_eq!(DELTA_12.level_down(), Some(RiskDie(3))); + assert_eq!(DELTA_20.level_down(), Some(RiskDie(4))); + } + + #[test] + fn level_up() + { + assert_eq!(DELTA_4.level_up(), RiskDie(1)); + assert_eq!(DELTA_6.level_up(), RiskDie(2)); + assert_eq!(DELTA_8.level_up(), RiskDie(3)); + assert_eq!(DELTA_10.level_up(), RiskDie(4)); + assert_eq!(DELTA_12.level_up(), RiskDie(5)); + assert_eq!(DELTA_20.level_up(), RiskDie(5)); + } + + #[test] + fn add_risk_dice() + { + assert_eq!(DELTA_4 + DELTA_4, DELTA_6); + assert_eq!(DELTA_4 + DELTA_6, DELTA_8); + assert_eq!(DELTA_4 + DELTA_8, DELTA_10); + assert_eq!(DELTA_4 + DELTA_10, DELTA_12); + assert_eq!(DELTA_4 + DELTA_12, DELTA_12); + assert_eq!(DELTA_4 + DELTA_20, DELTA_20); + assert_eq!(DELTA_6 + DELTA_4, DELTA_8); + assert_eq!(DELTA_6 + DELTA_6, DELTA_8); + assert_eq!(DELTA_6 + DELTA_8, DELTA_10); + assert_eq!(DELTA_6 + DELTA_10, DELTA_12); + assert_eq!(DELTA_6 + DELTA_12, DELTA_20); + assert_eq!(DELTA_6 + DELTA_20, DELTA_20); + assert_eq!(DELTA_8 + DELTA_4, DELTA_10); + assert_eq!(DELTA_8 + DELTA_6, DELTA_10); + assert_eq!(DELTA_8 + DELTA_8, DELTA_12); + assert_eq!(DELTA_8 + DELTA_10, DELTA_12); + assert_eq!(DELTA_8 + DELTA_12, DELTA_20); + assert_eq!(DELTA_8 + DELTA_20, DELTA_20); + assert_eq!(DELTA_10 + DELTA_4, DELTA_12); + assert_eq!(DELTA_10 + DELTA_6, DELTA_12); + assert_eq!(DELTA_10 + DELTA_8, DELTA_12); + assert_eq!(DELTA_10 + DELTA_10, DELTA_20); + assert_eq!(DELTA_10 + DELTA_12, DELTA_20); + assert_eq!(DELTA_10 + DELTA_20, DELTA_20); + assert_eq!(DELTA_12 + DELTA_4, DELTA_12); + assert_eq!(DELTA_12 + DELTA_6, DELTA_20); + assert_eq!(DELTA_12 + DELTA_8, DELTA_20); + assert_eq!(DELTA_12 + DELTA_10, DELTA_20); + assert_eq!(DELTA_12 + DELTA_12, DELTA_20); + assert_eq!(DELTA_12 + DELTA_20, DELTA_20); + assert_eq!(DELTA_20 + DELTA_4, DELTA_20); + assert_eq!(DELTA_20 + DELTA_6, DELTA_20); + assert_eq!(DELTA_20 + DELTA_8, DELTA_20); + assert_eq!(DELTA_20 + DELTA_10, DELTA_20); + assert_eq!(DELTA_20 + DELTA_12, DELTA_20); + assert_eq!(DELTA_20 + DELTA_20, DELTA_20); + } +} -- cgit v1.2.3-70-g09d2