roll_dice/
lib.rs

1extern crate rand;
2#[cfg(feature = "serde")]
3#[macro_use]
4extern crate serde;
5
6use rand::{Rng, SeedableRng};
7use rand::distributions::Uniform;
8use rand::rngs::SmallRng;
9use std::num::NonZeroU32;
10
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12#[derive(Debug, Clone)]
13pub struct Die {
14    faces: NonZeroU32,
15}
16
17impl Default for Die {
18    fn default() -> Self {
19        Die { faces: NonZeroU32::new(20).unwrap() }
20    }
21}
22
23impl Die {
24    /// Creates a new Die with the specified number of faces
25    pub fn new(faces: NonZeroU32) -> Die {
26        Die { faces }
27    }
28
29    /// Roll one die. Returns random number in the range from 1 to [`Die::faces()`].
30    pub fn roll(&self) -> u32 {
31        let mut rng = SmallRng::from_entropy();
32
33        rng.sample(Uniform::new_inclusive(1, self.faces().get()))
34    }
35
36    /// Number of Die sides
37    pub fn faces(&self) -> NonZeroU32 {
38        self.faces
39    }
40}
41
42/// Roll the Dice and return results of the Roll for each Die.
43///
44/// # Examples
45///
46/// Roll 3d6 `3 six-sided die` and get sum of the results
47/// ```
48/// # use roll_dice::roll;
49/// # use std::num::NonZeroU32;
50///
51/// let rolled = roll(3, NonZeroU32::new(6).unwrap() );
52/// let sum: u32 = rolled.iter().sum();
53///
54/// assert!(sum > 2 && sum < 19)
55/// ```
56pub fn roll(amount: u32, faces: NonZeroU32) -> Vec<u32> {
57    let die = Die::new(faces);
58    let mut results = Vec::with_capacity(amount as usize);
59
60    for _ in 0..amount {
61        results.push(die.roll());
62    }
63
64    results
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn die_roll_d6() {
73        let faces = NonZeroU32::new(6).unwrap();
74        let d6 = Die::new(faces);
75
76        for _ in 0..99 {
77            let roll = d6.roll();
78            assert!(roll > 0 && roll <= faces.get());
79        }
80    }
81
82    #[test]
83    fn die_roll_d1() {
84        let d1 = Die::new(NonZeroU32::new(1).unwrap());
85
86        assert_eq!(d1.roll(), 1);
87    }
88
89    #[test]
90    fn die_faces() {
91        let d6 = Die::new(NonZeroU32::new(6).unwrap());
92
93        assert_eq!(d6.faces().get(), 6)
94    }
95
96    #[test]
97    fn roll_vector_length_equals_number_of_dice() {
98        let length = roll(3, NonZeroU32::new(6).unwrap()).len();
99
100        assert_eq!(length, 3);
101    }
102
103    #[test]
104    fn roll_vector_contains_numbers_from_valid_range() {
105        let amount = 3;
106        let faces = NonZeroU32::new(6).unwrap();
107
108        for _ in 0..99 {
109            let vec = roll(amount, faces);
110            assert!(vec.iter().all(|v| v <= &faces.get() && v > &0));
111        }
112    }
113
114    #[test]
115    fn roll_zero_dice() {
116        let amount = 0;
117        let faces = NonZeroU32::new(6).unwrap();
118
119        assert_eq!(roll(amount, faces).len(), 0)
120    }
121}