use std::fmt;
use std::ops::Add;
use std::str::FromStr;
use num::Unsigned;
pub trait Rollable: Unsigned + fmt::Display + FromStr + Copy {
fn roll(max: Self) -> Self;
}
impl Rollable for u8 {
fn roll(max: u8) -> u8 {
let r: u8 = rand::random();
(r % max) + 1
}
}
impl Rollable for u16 {
fn roll(max: u16) -> u16 {
let r: u16 = rand::random();
(r % max) + 1
}
}
impl Rollable for u32 {
fn roll(max: u32) -> u32 {
let r: u32 = rand::random();
(r % max) + 1
}
}
impl Rollable for u64 {
fn roll(max: u64) -> u64 {
let r: u64 = rand::random();
(r % max) + 1
}
}
impl Rollable for u128 {
fn roll(max: u128) -> u128 {
let r: u128 = rand::random();
(r % max) + 1
}
}
impl Rollable for usize {
fn roll(max: usize) -> usize {
let r: usize = rand::random();
(r % max) + 1
}
}
pub fn try_quickroll<T: Rollable>(dice_format: &str) -> Result<T, String> {
let dice: Dice<T> = dice_format.parse()?;
Ok(dice.total())
}
pub fn quickroll<T: Rollable>(dice_format: &str) -> T {
let dice: Dice<T> = dice_format.parse().unwrap();
dice.total()
}
pub struct Die<T: Rollable = u32> {
faces: T,
current_value: T,
}
pub struct Dice<T: Rollable = u32> {
dice: Vec<Die<T>>,
}
impl<T: Rollable> Die<T> {
pub fn new(faces: T) -> Self {
let mut die = Die {
faces,
current_value: T::one(),
};
die.roll();
die
}
pub fn roll(&mut self) -> T {
self.current_value = T::roll(self.faces);
self.current_value
}
pub fn current_face(&self) -> T {
self.current_value
}
}
impl<T: Rollable> Add for Die<T> {
type Output = T;
fn add(self, other: Self) -> Self::Output {
self.current_value + other.current_value
}
}
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 {
let mut total = T::zero();
for f in self.current_faces().iter() {
total = total + *f;
}
total
}
}
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> {
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> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.total())
}
}
impl<T: Rollable> fmt::Debug for Dice<T> {
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<u128> = Die::new(2);
for _ in 0..100 {
assert!(coin.current_face() >= 1);
assert!(coin.current_face() <= 2);
}
}
#[test]
fn roll() {
let mut d12: Die<u64> = 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<u8> = Die::new(2);
let quarter: Die<u8> = 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<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 {
dice = dice.roll_all();
let total = dice.total();
assert!(total >= 2);
assert!(total <= 14);
}
}
#[test]
fn dice_from_str() {
let mut dice: Dice<u32> = "3d4".parse().unwrap();
for _ in 0..100 {
dice = dice.roll_all();
let total = dice.total();
assert!(total >= 3);
assert!(total <= 12);
}
}
}