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 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 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}