1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
extern crate rand;
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;

use rand::{Rng, SeedableRng};
use rand::distributions::Uniform;
use rand::rngs::SmallRng;
use std::num::NonZeroU32;

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct Die {
    faces: NonZeroU32,
}

impl Default for Die {
    fn default() -> Self {
        Die { faces: NonZeroU32::new(20).unwrap() }
    }
}

impl Die {
    /// Creates a new Die with the specified number of faces
    pub fn new(faces: NonZeroU32) -> Die {
        Die { faces }
    }

    /// Roll one die. Returns random number in the range from 1 to [`Die::faces()`].
    pub fn roll(&self) -> u32 {
        let mut rng = SmallRng::from_entropy();

        rng.sample(Uniform::new_inclusive(1, self.faces().get()))
    }

    /// Number of Die sides
    pub fn faces(&self) -> NonZeroU32 {
        self.faces
    }
}

/// Roll the Dice and return results of the Roll for each Die.
///
/// # Examples
///
/// Roll 3d6 `3 six-sided die` and get sum of the results
/// ```
/// # use roll_dice::roll;
/// # use std::num::NonZeroU32;
///
/// let rolled = roll(3, NonZeroU32::new(6).unwrap() );
/// let sum: u32 = rolled.iter().sum();
///
/// assert!(sum > 2 && sum < 19)
/// ```
pub fn roll(amount: u32, faces: NonZeroU32) -> Vec<u32> {
    let die = Die::new(faces);
    let mut results = Vec::with_capacity(amount as usize);

    for _ in 0..amount {
        results.push(die.roll());
    }

    results
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn die_roll_d6() {
        let faces = NonZeroU32::new(6).unwrap();
        let d6 = Die::new(faces);

        for _ in 0..99 {
            let roll = d6.roll();
            assert!(roll > 0 && roll <= faces.get());
        }
    }

    #[test]
    fn die_roll_d1() {
        let d1 = Die::new(NonZeroU32::new(1).unwrap());

        assert_eq!(d1.roll(), 1);
    }

    #[test]
    fn die_faces() {
        let d6 = Die::new(NonZeroU32::new(6).unwrap());

        assert_eq!(d6.faces().get(), 6)
    }

    #[test]
    fn roll_vector_length_equals_number_of_dice() {
        let length = roll(3, NonZeroU32::new(6).unwrap()).len();

        assert_eq!(length, 3);
    }

    #[test]
    fn roll_vector_contains_numbers_from_valid_range() {
        let amount = 3;
        let faces = NonZeroU32::new(6).unwrap();

        for _ in 0..99 {
            let vec = roll(amount, faces);
            assert!(vec.iter().all(|v| v <= &faces.get() && v > &0));
        }
    }

    #[test]
    fn roll_zero_dice() {
        let amount = 0;
        let faces = NonZeroU32::new(6).unwrap();

        assert_eq!(roll(amount, faces).len(), 0)
    }
}