141 lines
4.0 KiB
Rust
141 lines
4.0 KiB
Rust
//! A struct in which our data is held for rolling. By ensuring that
|
|
//! only `usize`s are used we prevent the need for additional type
|
|
//! checking at time of use. */
|
|
extern crate rand;
|
|
use rand::Rng;
|
|
use std::fmt;
|
|
use std::num::ParseIntError;
|
|
use super::std;
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct Roll {
|
|
die: usize,
|
|
number: usize,
|
|
}
|
|
impl Roll {
|
|
/// A fn to create new `Roll` structs, private to prevent
|
|
/// being used outside the library.
|
|
pub fn new(number: usize, die: usize) -> Roll {
|
|
Roll {
|
|
die: die,
|
|
number: number,
|
|
}
|
|
}
|
|
|
|
///Returns the value stored in the number field of a roll.
|
|
///Allows the fields of `Roll` to be private (and unchangable)
|
|
///while still allowing access to act on their internal value
|
|
/// ```
|
|
/// let x = Roll::new(&2, &6);
|
|
/// assert!(x.number() == 2);
|
|
/// ```
|
|
pub fn number(&self) -> usize {
|
|
self.number
|
|
}
|
|
/// Returns the value stored in the die field of a roll.
|
|
/// Allows the fields of `Roll` to be private (and unchangable)
|
|
/// while still allowing access to act on their internal value
|
|
pub fn die(&self) -> usize {
|
|
self.die
|
|
}
|
|
/// Creates a Vector to hold each of the values rolled. Making
|
|
/// a Vector ensures the ability to work on exactly the same
|
|
/// die rolls easily and accurately, since calling the `Roll`
|
|
/// iterator again will produce new results.
|
|
pub fn rolls(self) -> Vec<usize> {
|
|
self.collect()
|
|
}
|
|
|
|
/// Used on a `Roll` to return only the final value, ignoring
|
|
/// the values used to add up to that result. Allows `.total()`
|
|
/// syntax when used
|
|
pub fn total(self) -> usize {
|
|
self.sum()
|
|
}
|
|
|
|
/// Takes arguments and turns them into a `Roll` if possible,
|
|
/// failing that it returns an error to be handled.
|
|
/// Keeps taking args until less than 2 remain so it returns a
|
|
/// `Vec<Roll>`
|
|
pub fn from_args(args: &mut std::env::Args) -> Result<Vec<Roll>, ParseIntError> {
|
|
//first arg is the path to the executable so skip it
|
|
args.next();
|
|
|
|
let mut total: Vec<Roll> = vec![];
|
|
|
|
// loop that checks that there's enough elements in args remaining
|
|
// to make a `Roll` from.
|
|
while args.len() >= 2 {
|
|
// Collect 2 args into a `Vec<String>` and turn them into
|
|
// a `Roll`
|
|
let roll: Vec<String> = args.take(2).collect();
|
|
total.push(Roll::new(
|
|
roll[0].parse::<usize>()?,
|
|
roll[1].parse::<usize>()?,
|
|
));
|
|
}
|
|
Ok(total)
|
|
}
|
|
}
|
|
|
|
/// Turns `Roll` into an `Iterator`, causing it to create a series of random
|
|
///numbers between 1 and the chosen die size. It counts down the number field
|
|
///of the `Roll` and returns `None` to end the iteration. It's only capable of
|
|
///returning usize values, which should never be a problem.
|
|
impl Iterator for Roll {
|
|
type Item = usize;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.number != 0 {
|
|
self.number -= 1;
|
|
Some(rand::thread_rng().gen_range(1, self.die + 1))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Defines a `Display` format for the `Roll` type. Makes it much easier
|
|
///to use the `Roll` type as you should be able to just use
|
|
///```
|
|
/// ```
|
|
impl fmt::Display for Roll {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(f, "{}d{}", self.number, self.die)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
#[test]
|
|
fn rollnew() {
|
|
let x = Roll::new(2, 6);
|
|
let y = Roll { number: 2, die: 6 };
|
|
assert_eq!(x, y);
|
|
}
|
|
#[test]
|
|
fn displays() {
|
|
let x = Roll::new(2, 6);
|
|
assert_eq!(2, x.number());
|
|
assert_eq!(6, x.die());
|
|
}
|
|
#[test]
|
|
fn rolls() {
|
|
let x = Roll::new(20, 6);
|
|
for y in x.clone().rolls() {
|
|
assert!(y <= x.die());
|
|
}
|
|
}
|
|
#[test]
|
|
fn totaling() {
|
|
for _ in 0..21 {
|
|
let x = Roll::new(2, 6);
|
|
let y = x.total();
|
|
assert!(y <= 12 && y >= 2);
|
|
let z = Roll::new(3, 20);
|
|
let y = z.total();
|
|
assert!(y <= 60_usize && y >= 3_usize);
|
|
}
|
|
}
|