rolldice/
lib.rs

1extern crate rand;
2
3use rand:: Rng;
4use rand::distributions::{Range, IndependentSample};
5
6
7#[derive(Debug, PartialEq, Eq)]
8pub struct Dice {
9    pub number: u32,
10    pub sides: u32,
11}
12
13impl Dice {
14    /// Create a Dice instance given a standard dice format.
15    /// Expected format is <number>d<sides> where <number>
16    /// is the number of dice to roll and <sides> is the number
17    /// of sides per dice rolled.
18    ///
19    /// ```
20    /// let result = rolldice::Dice::parse("4d6").unwrap();
21    ///
22    /// assert_eq!(result, rolldice::Dice { number: 4, sides: 6 });
23    /// ```
24    pub fn parse(dice: &str) -> Result<Dice, String> {
25        let tokens: Vec<&str> = dice.split("d").collect();
26
27        if tokens.len() != 2 {
28            return Err(format!("Invalid dice format '{}'", dice));
29        }
30
31        let number = match tokens[0].parse() {
32            Ok(num) => num,
33            Err(_) => return Err(format!("Invalid number of dice '{}'", tokens[0])),
34        };
35        let sides = match tokens[1].parse() {
36            Ok(num) => num,
37            Err(_) => return Err(format!("Invalid number of sides '{}'", tokens[1])),
38        };
39
40        Ok(Dice { number, sides })
41    }
42
43    /// Generate a dice role from a Dice instance. Requires a random
44    /// number generator from the "rand" crate to be passed in.
45    ///
46    /// ```
47    /// extern crate rand;
48    /// extern crate rolldice;
49    ///
50    /// let dice = rolldice::Dice { number: 4, sides: 8 };
51    /// let mut rng = rand::thread_rng();
52    ///
53    /// let result = dice.generate(&mut rng);
54    /// ```
55    pub fn generate<R: Rng>(&self, mut rng: &mut R) -> u32 {
56        let between = Range::new(1, self.sides);
57        let mut total = 0;
58        for _ in 0..self.number {
59            total += between.ind_sample(&mut rng);
60        }
61        total
62    }
63}
64
65
66#[cfg(test)]
67mod test {
68    use super::*;
69    use rand::{SeedableRng, StdRng};
70
71    #[test]
72    fn dice_generate() {
73        let dice = Dice { sides: 6, number: 4 };
74
75        let seed: &[_] = &[1, 2, 3, 4];
76        let mut rng: StdRng = SeedableRng::from_seed(seed);
77        let result = dice.generate(&mut rng);
78        assert_eq!(result, 10);
79
80        let mut rng = rand::thread_rng();
81        for _ in 1..100 {
82            let result = dice.generate(&mut rng);
83            assert!(result >= 4);
84            assert!(result <= 24);
85        }
86    }
87
88    #[test]
89    fn dice_parse_invalid_number_of_dice() {
90        assert_eq!(Dice::parse("d6").unwrap_err(), "Invalid number of dice ''");
91        assert_eq!(Dice::parse("Td10").unwrap_err(), "Invalid number of dice 'T'");
92    }
93
94    #[test]
95    fn dice_parse_invalid_number_of_sides() {
96        assert_eq!(Dice::parse("1d").unwrap_err(), "Invalid number of sides ''");
97        assert_eq!(Dice::parse("1dY").unwrap_err(), "Invalid number of sides 'Y'");
98    }
99
100    #[test]
101    fn dice_parse_invalid_dice_format() {
102        assert_eq!(Dice::parse("").unwrap_err(), "Invalid dice format ''");
103        assert_eq!(Dice::parse("something").unwrap_err(), "Invalid dice format 'something'");
104    }
105
106    #[test]
107    fn dice_parse_correct() {
108        assert_eq!(
109            Dice::parse("6d100"),
110            Ok(Dice { number: 6, sides: 100 }));
111        assert_eq!(
112            Dice::parse("1d5"),
113            Ok(Dice { number: 1, sides: 5}));
114    }
115}