use std::fmt;
use std::ops::Add;
pub fn try_quickroll(dice_format: &str) -> Result<u32, String> {
let dice: Dice = dice_format.parse()?;
Ok(dice.total())
}
pub fn quickroll(dice_format: &str) -> u32 {
let dice: Dice = dice_format.parse().unwrap();
dice.total()
}
pub struct Die {
faces: u32,
current_value: u32,
}
pub struct Dice {
dice: Vec<Die>,
}
impl Die {
pub fn new(faces: u32) -> Self {
let mut die = Die {
faces,
current_value: 1,
};
die.roll();
die
}
pub fn roll(&mut self) -> u32 {
let r: u32 = rand::random();
self.current_value = (r % self.faces) + 1;
self.current_value
}
pub fn current_face(&self) -> u32 {
self.current_value
}
}
impl Add for Die {
type Output = u32;
fn add(self, other: Self) -> Self::Output {
self.current_value + other.current_value
}
}
impl Dice {
pub fn new(dice: usize, faces: u32) -> Self {
let dice = {
let mut v: Vec<Die> = Vec::with_capacity(dice);
for _ in 0..dice {
v.push(Die::new(faces));
}
v
};
Dice {
dice
}
}
pub fn from(dice: Box<[Die]>) -> Self {
let dice = dice.into_vec();
Dice {
dice
}
}
pub fn current_faces(&self) -> Vec<u32> {
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) -> u32 {
self.current_faces().iter().sum()
}
}
impl Add for Dice {
type Output = Self;
fn add(self, other: Self) -> Self::Output {
let mut dice: Vec<Die> = 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 std::str::FromStr for Dice {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (dice_amount, dice_faces): (usize, u32) = {
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 fmt::Display for Dice {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.total())
}
}
impl fmt::Debug for Dice {
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(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn current_face() {
let coin = Die::new(2);
for _ in 0..100 {
assert!(coin.current_face() >= 1);
assert!(coin.current_face() <= 2);
}
}
#[test]
fn roll() {
let mut d12 = Die::new(12);
for _ in 0..100 {
d12.roll();
assert!(d12.current_face() >= 1);
assert!(d12.current_face() <= 12);
}
}
#[test]
fn add_die() {
for _ in 0..100 {
let penny = Die::new(2);
let quarter = Die::new(2);
let sum = penny + quarter;
assert!(sum >= 2);
assert!(sum <= 4);
}
}
#[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 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::new(2, 3);
let total = dice.total();
assert!(total >= 2);
assert!(total <= 6);
}
}
#[test]
fn add_dice() {
let one_d_6 = Dice::new(1, 6);
let two_d_4 = Dice::new(2, 4);
let mut dice = one_d_6 + two_d_4;
for _ in 0..100 {
dice = dice.roll_all();
let total = dice.total();
assert!(total >= 2);
assert!(total <= 14);
}
}
#[test]
fn dice_from_str() {
let mut dice: Dice = "3d4".parse().unwrap();
for _ in 0..100 {
dice = dice.roll_all();
let total = dice.total();
assert!(total >= 3);
assert!(total <= 12);
}
}
}