summaryrefslogtreecommitdiff
path: root/src/dice.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/dice.rs')
-rw-r--r--src/dice.rs227
1 files changed, 227 insertions, 0 deletions
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<Self, Error>
+ {
+ if (level as usize) < RISK_DIE_VALUES.len() {
+ Ok(Self(level))
+ }
+ else {
+ Err(Error::InvalidLevel)
+ }
+ }
+
+ pub fn from_value(value: u8) -> Result<Self, Error>
+ {
+ 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<Self>
+ {
+ let mut rng = rand::thread_rng();
+ self.roll_with_rng(&mut rng)
+ }
+
+ pub fn roll_with_rng(self, rng: &mut ThreadRng) -> Option<Self>
+ {
+ 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<Self>
+ {
+ 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);
+ }
+}