1use std::fmt;
2use std::ops::Add;
3use std::str::FromStr;
4
5use crate::{
6 DiceTotal,
7 Die,
8 Rollable,
9};
10
11pub 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 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 pub fn from(dice: Box<[Die<T>]>) -> Self {
155 let dice = dice.into_vec();
156
157 Dice { dice }
158 }
159
160 pub fn current_faces(&self) -> Vec<T> {
174 self.dice.iter().map(|die| die.current_face()).collect()
175 }
176
177 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 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}