use rand::Rng;
use num::{ Float, Integer, NumCast, ToPrimitive };
pub trait DiceExt {
fn d(&self, sides: usize) -> Self;
fn d2(&self) -> Self;
fn d3(&self) -> Self;
fn d4(&self) -> Self;
fn d5(&self) -> Self;
fn d6(&self) -> Self;
fn d8(&self) -> Self;
fn d10(&self) -> Self;
fn d12(&self) -> Self;
fn d20(&self) -> Self;
fn d100(&self) -> Self;
fn chance(&self, of:i32) -> Option<i32>;
}
pub trait HiLo {
fn hi(&self) -> bool;
fn lo(&self) -> bool;
}
fn any_i32(num: i32, sides: usize) -> i32 {
let mut result: i32 = 0;
let reverse = num < 0;
for _ in 0..num.abs() {
result += rand::thread_rng().gen_range(1..=(sides as i32));
}
if reverse {-result} else {result}
}
impl DiceExt for i32 {
fn d(&self, sides: usize) -> Self { any_i32(*self, sides) }
fn d2(&self) -> Self { any_i32(*self, 2)}
fn d3(&self) -> Self { any_i32(*self, 3)}
fn d4(&self) -> Self { any_i32(*self, 4)}
fn d5(&self) -> Self { any_i32(*self, 5)}
fn d6(&self) -> Self { any_i32(*self, 6)}
fn d8(&self) -> Self { any_i32(*self, 8)}
fn d10(&self) -> Self { any_i32(*self, 10)}
fn d12(&self) -> Self { any_i32(*self, 12)}
fn d20(&self) -> Self { any_i32(*self, 20)}
fn d100(&self) -> Self { any_i32(*self, 100)}
fn chance(&self, of:i32) -> Option<i32> {
if 3.d6() as Self <= *self {Some(of)} else {None}
}
}
impl HiLo for i32 {
fn hi(&self) -> bool {
self.is_even()
}
fn lo(&self) -> bool {
self.is_odd()
}
}
pub trait PercentageVariance {
fn delta(&self, percentage: i32) -> Self;
}
pub trait FixedNumberVariance<T: Float> {
fn upto_delta(&self, upto: T) -> T;
}
impl FixedNumberVariance<f64> for f64 {
fn upto_delta(&self, upto: Self) -> Self {
self + rand::thread_rng().gen_range(-upto..=upto)
}
}
impl FixedNumberVariance<f32> for f32 {
fn upto_delta(&self, upto: Self) -> Self {
self + rand::thread_rng().gen_range(-upto..=upto)
}
}
fn delta_p<T: Float + ToPrimitive>(original: &T, percentage: i32) -> T {
let p = 0.01 * percentage as f64;
*original * NumCast::from(1.0 + rand::thread_rng().gen_range(-p..=p)).unwrap()
}
impl PercentageVariance for f32 {
fn delta(&self, percentage:i32) -> Self { delta_p::<Self>(self, percentage) }
}
impl PercentageVariance for f64 {
fn delta(&self, percentage:i32) -> Self { delta_p::<Self>(self, percentage) }
}
#[macro_export]
macro_rules! lo {
() => {
1.d2().lo()
}
}
#[macro_export]
macro_rules! hi {
() => {
!low!()
}
}
#[macro_export]
macro_rules! chance_of {
($chance:expr, $v:expr) => {
if 1.d100() <= $chance { $v } else { 0 }
}
}
#[cfg(test)]
mod dice_tests {
use crate::DiceExt;
#[test]
fn d6_stay_in_range() {
for _ in 0..10_000 {
let d = 1.d6();
assert!(d >= 1 && d <= 6);
}
}
#[test]
fn d97_stay_in_range() {
for _ in 0..10_000 {
let d = 1.d(97);
assert!(d >= 1 && d <= 97);
}
}
#[test]
fn chance_macro_works() {
for _ in 0..20 {
println!("{}", chance_of!(5, 50))
}
}
}