use std::fmt;
use std::ops::Add;
use std::str::FromStr;
use crate::{
DiceTotal,
Die,
Rollable,
};
pub struct Dice<T: Rollable = u32> {
dice: Vec<Die<T>>,
}
impl<T: Rollable> Add for Dice<T> {
type Output = Self;
fn add(self, other: Self) -> Self::Output {
let mut dice: Vec<Die<T>> = Vec::new();
for die in self.dice.into_iter() {
dice.push(die);
}
for die in other.dice.into_iter() {
dice.push(die);
}
Dice { dice }
}
}
impl<T: Rollable> FromStr for Dice<T> where T: FromStr {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (dice_amount, dice_faces): (usize, T) = {
let mut s = s.split('d');
let values = if let (Some(d), Some(f)) = (s.next(), s.next()) {
(d.parse(), f.parse())
} else {
return Err(String::from("Missing 'd'"));
};
if let (Ok(d), Ok(f)) = values {
(d, f)
} else {
return Err(String::from("Improper dice format"));
}
};
Ok(Dice::new(dice_amount, dice_faces))
}
}
impl<T: Rollable> fmt::Display for Dice<T>
where
T: DiceTotal<T>,
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.total())
}
}
impl<T: Rollable> fmt::Debug for Dice<T> where T: fmt::Display {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut iter = self.dice.iter();
let first = match iter.next() {
Some(d) => d,
None => return Err(fmt::Error),
};
if let Err(e) = write!(f, "{}", first.current_face()) {
return Err(e);
}
for die in iter {
if let Err(e) = write!(f, " {}", die.current_face()) {
return Err(e);
}
}
Ok(())
}
}
impl<T: Rollable> Dice<T> {
pub fn new(dice: usize, faces: T) -> Self {
let dice = {
let mut v: Vec<Die<T>> = Vec::with_capacity(dice);
for _ in 0..dice {
v.push(Die::new(faces));
}
v
};
Dice { dice }
}
pub fn from(dice: Box<[Die<T>]>) -> Self {
let dice = dice.into_vec();
Dice { dice }
}
pub fn current_faces(&self) -> Vec<T> {
self.dice.iter().map(|die| die.current_face()).collect()
}
pub fn roll_all(&mut self) -> &Self {
let iter = self.dice.iter_mut().map(|die| {
die.roll();
});
for _ in iter {}
self
}
pub fn total(&self) -> T
where
T: DiceTotal<T>,
{
T::dice_total(self.current_faces())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn current_faces() {
for _ in 0..100 {
let dice = Dice::new(3, 6);
let sum: u32 = dice.current_faces().iter().sum();
assert!(sum >= 3);
assert!(sum <= 18);
}
}
#[test]
fn roll_all() {
for _ in 0..100 {
let mut dice = Dice::new(4, 2);
let sum: u32 = dice.roll_all().current_faces().iter().sum();
assert!(sum >= 4);
assert!(sum <= 8);
}
}
#[test]
fn total() {
for _ in 0..100 {
let dice: Dice<u16> = Dice::new(2, 3);
let total = dice.total();
assert!(total >= 2);
assert!(total <= 6);
}
}
#[test]
fn add_dice() {
let one_d_6: Dice<u8> = Dice::new(1, 6);
let two_d_4: Dice<u8> = Dice::new(2, 4);
let mut dice = one_d_6 + two_d_4;
for _ in 0..100 {
let total = dice.roll_all().total();
assert!(total >= 2);
assert!(total <= 14);
}
}
#[test]
fn dice_from_str() {
let mut dice: Dice<u32> = "3d4".parse().unwrap();
for _ in 0..100 {
let total = dice.roll_all().total();
assert!(total >= 3);
assert!(total <= 12);
}
}
}