one_d_six/
dice.rs

1use std::fmt;
2use std::ops::Add;
3use std::str::FromStr;
4
5use crate::{
6    DiceTotal,
7    Die,
8    Rollable,
9};
10
11/// A Handful of dice.
12///
13/// # Examples
14///
15/// ```
16/// use one_d_six::Dice;
17///
18/// let mut dice: Dice = "3d6".parse().unwrap();
19///
20/// assert!(dice.roll_all().total() >= 3);
21/// assert!(dice.total() <= 18);
22/// ```
23///
24/// ## Adding two collections of dice
25///
26/// ```
27/// use one_d_six::Dice;
28///
29/// let one_d6: Dice = "1d6".parse().unwrap();
30/// let three_d4 = Dice::new(3, 4);
31///
32/// let dice = one_d6 + three_d4;
33///
34/// assert!(dice.total() >= 4);
35/// assert!(dice.total() <= 18);
36/// ```
37pub struct Dice<T: Rollable = u32> {
38    dice: Vec<Die<T>>,
39}
40
41impl<T: Rollable> Add for Dice<T> {
42    type Output = Self;
43
44    fn add(self, other: Self) -> Self::Output {
45        let mut dice: Vec<Die<T>> = Vec::new();
46        for die in self.dice.into_iter() {
47            dice.push(die);
48        }
49        for die in other.dice.into_iter() {
50            dice.push(die);
51        }
52        Dice { dice }
53    }
54}
55
56impl<T: Rollable> FromStr for Dice<T> where T: FromStr {
57    type Err = String;
58
59    fn from_str(s: &str) -> Result<Self, Self::Err> {
60        let (dice_amount, dice_faces): (usize, T) = {
61            let mut s = s.split('d');
62            let values = if let (Some(d), Some(f)) = (s.next(), s.next()) {
63                (d.parse(), f.parse())
64            } else {
65                return Err(String::from("Missing 'd'"));
66            };
67
68            if let (Ok(d), Ok(f)) = values {
69                (d, f)
70            } else {
71                return Err(String::from("Improper dice format"));
72            }
73        };
74        Ok(Dice::new(dice_amount, dice_faces))
75    }
76}
77
78impl<T: Rollable> fmt::Display for Dice<T>
79where
80    T: DiceTotal<T>,
81    T: fmt::Display,
82{
83    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84        write!(f, "{}", self.total())
85    }
86}
87
88impl<T: Rollable> fmt::Debug for Dice<T> where T: fmt::Display {
89    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90        let mut iter = self.dice.iter();
91        let first = match iter.next() {
92            Some(d) => d,
93            None => return Err(fmt::Error),
94        };
95        if let Err(e) = write!(f, "{}", first.current_face()) {
96            return Err(e);
97        }
98
99        for die in iter {
100            if let Err(e) = write!(f, " {}", die.current_face()) {
101                return Err(e);
102            }
103        }
104        Ok(())
105    }
106}
107
108impl<T: Rollable> Dice<T> {
109    /// Creates a new set of dice.
110    /// Each die in the set has an initial starting value.
111    /// Only allows dice of same type. No mixture of d4 and d6.
112    ///
113    /// # Example
114    ///
115    /// ```
116    /// use one_d_six::Dice;
117    ///
118    /// // Creates 3d6 dice collection
119    /// let dice = Dice::new(3, 6);
120    /// ```
121    pub fn new(dice: usize, faces: T) -> Self {
122        let dice = {
123            let mut v: Vec<Die<T>> = Vec::with_capacity(dice);
124            for _ in 0..dice {
125                v.push(Die::new(faces));
126            }
127            v
128        };
129
130        Dice { dice }
131    }
132
133    /// Creates a set of dice from a `Vec<Die>`.
134    /// Allows for mixture of Die types (d4, d6, etc.).
135    ///
136    /// # Example
137    ///
138    /// ```
139    /// use one_d_six::{
140    ///     Dice,
141    ///     Die,
142    /// };
143    ///
144    /// // Creates 2d6 + 1d4 dice collection
145    /// let dice = {
146    ///     let dice = [
147    ///         Die::new(6),
148    ///         Die::new(6),
149    ///         Die::new(4),
150    ///     ];
151    ///     Dice::from(Box::new(dice))
152    /// };
153    /// ```
154    pub fn from(dice: Box<[Die<T>]>) -> Self {
155        let dice = dice.into_vec();
156
157        Dice { dice }
158    }
159
160    /// Gets the current face of each die in the dice set.
161    ///
162    /// # Example
163    ///
164    /// ```
165    /// use one_d_six::Dice;
166    ///
167    /// let four_coins = Dice::new(4, 2);
168    ///
169    /// for val in four_coins.current_faces().iter() {
170    ///     assert!(val == &1 || val == &2);
171    /// }
172    /// ```
173    pub fn current_faces(&self) -> Vec<T> {
174        self.dice.iter().map(|die| die.current_face()).collect()
175    }
176
177    /// Rolls all dice and returns self.
178    ///
179    /// # Example
180    ///
181    /// ```
182    /// use one_d_six::Dice;
183    ///
184    /// let mut ten_d_4 = Dice::new(10, 4);
185    ///
186    /// for val in ten_d_4.roll_all().current_faces().iter() {
187    ///     let val: u32 = *val;
188    ///     assert!(val >= 1);
189    ///     assert!(val <= 4);
190    /// }
191    /// ```
192    pub fn roll_all(&mut self) -> &Self {
193        let iter = self.dice.iter_mut().map(|die| {
194            die.roll();
195        });
196        for _ in iter {}
197        self
198    }
199
200    /// Gets the total of the current faces of the dice.
201    ///
202    /// # Example
203    ///
204    /// ```
205    /// use one_d_six::Dice;
206    ///
207    /// let two_d_4 = Dice::new(2, 4);
208    ///
209    /// assert!(two_d_4.total() >= 2);
210    /// assert!(two_d_4.total() <= 8);
211    /// ```
212    pub fn total(&self) -> T
213    where
214        T: DiceTotal<T>,
215    {
216        T::dice_total(self.current_faces())
217    }
218}
219
220#[cfg(test)]
221mod tests {
222    use super::*;
223
224    #[test]
225    fn current_faces() {
226        for _ in 0..100 {
227            let dice = Dice::new(3, 6);
228
229            let sum: u32 = dice.current_faces().iter().sum();
230
231            assert!(sum >= 3);
232            assert!(sum <= 18);
233        }
234    }
235
236    #[test]
237    fn roll_all() {
238        for _ in 0..100 {
239            let mut dice = Dice::new(4, 2);
240
241            let sum: u32 = dice.roll_all().current_faces().iter().sum();
242
243            assert!(sum >= 4);
244            assert!(sum <= 8);
245        }
246    }
247
248    #[test]
249    fn total() {
250        for _ in 0..100 {
251            let dice: Dice<u16> = Dice::new(2, 3);
252            let total = dice.total();
253
254            assert!(total >= 2);
255            assert!(total <= 6);
256        }
257    }
258
259    #[test]
260    fn add_dice() {
261        let one_d_6: Dice<u8> = Dice::new(1, 6);
262        let two_d_4: Dice<u8> = Dice::new(2, 4);
263        let mut dice = one_d_6 + two_d_4;
264
265        for _ in 0..100 {
266            let total = dice.roll_all().total();
267            assert!(total >= 2);
268            assert!(total <= 14);
269        }
270    }
271
272    #[test]
273    fn dice_from_str() {
274        let mut dice: Dice<u32> = "3d4".parse().unwrap();
275
276        for _ in 0..100 {
277            let total = dice.roll_all().total();
278            assert!(total >= 3);
279            assert!(total <= 12);
280        }
281    }
282}