#![warn(missing_docs)]
mod dice;
mod dice_builder;
mod dice_string_parser;
mod wasm_safe;
pub use dice::Dice;
pub use dice_builder::DiceBuilder;
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
#[cfg(feature = "wasm")]
#[cfg_attr(feature = "wasm", wasm_bindgen)]
pub fn greet() -> String {
format!("Hello, from dices instantly")
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use fraction::{ToPrimitive, Zero};
use crate::{
dice_builder::{DiceBuilder, DistributionHashMap, Prob, Value},
Dice,
};
#[test]
fn adding_distributions_coin_times_2() {
let f1 = DiceBuilder::Constant(2);
let f2 = DiceBuilder::FairDie { min: 0, max: 1 };
let f3 = DiceBuilder::ProductCompound(vec![f1, f2]);
let dice = f3.build();
let d_vec = dice.distribution;
assert_eq!(
d_vec,
vec![(0, Prob::new(1u64, 2u64)), (2, Prob::new(1u64, 2u64))]
);
}
#[test]
fn adding_distributions_two_dice() {
let f1 = DiceBuilder::FairDie { min: 1, max: 5 };
let f2 = DiceBuilder::FairDie { min: 1, max: 5 };
let f3 = DiceBuilder::SumCompound(vec![f1, f2]);
let dice = f3.build();
let d_vec = dice.distribution;
println!("{:?}", d_vec);
assert_eq!(d_vec[0], (2, Prob::new(1u64, 25u64)));
}
#[test]
fn test_division() {
let d1 = Dice::build_from_string("d6/2").unwrap();
let d2 = Dice::build_from_string("d3").unwrap();
assert_eq!(d1.distribution, d2.distribution);
}
#[test]
fn adding_20_dice() {
let mut f = Box::new(DiceBuilder::Constant(0));
for _ in 0..20 {
f = f + Box::new(DiceBuilder::FairDie { min: 1, max: 6 });
}
let maxval = f.build().distribution.iter().map(|e| e.0).max().unwrap();
assert_eq!(maxval, 120);
}
#[test]
fn sample_sum_convolute_1() {
let f1 = DiceBuilder::Constant(2);
let f2 = DiceBuilder::FairDie { min: 1, max: 2 };
let f = DiceBuilder::SampleSumCompound(vec![f1, f2]);
let dice = f.build();
let d = dice.distribution;
assert_eq!(d, unif(vec![2, 3, 3, 4]));
}
#[test]
fn sample_sum_convolute_2() {
let f1 = DiceBuilder::FairDie { min: 1, max: 2 };
let f2 = DiceBuilder::FairDie { min: 1, max: 2 };
let f = DiceBuilder::SampleSumCompound(vec![f1, f2]);
let dice = f.build();
let d = dice.distribution;
assert_eq!(d, unif(vec![1, 2, 1, 2, 2, 3, 3, 4]));
}
#[test]
fn sample_sum_convolute_3() {
let f1 = DiceBuilder::FairDie { min: 0, max: 1 };
let f2 = DiceBuilder::FairDie { min: 1, max: 2 };
let f = DiceBuilder::SampleSumCompound(vec![f1, f2]);
let dice = f.build();
let d = dice.distribution;
assert_eq!(d, unif(vec![0, 0, 1, 2]));
}
#[test]
fn sample_sum_convolute_4() {
let f1 = DiceBuilder::Constant(0);
let f2 = DiceBuilder::FairDie { min: 1, max: 6 };
let f = DiceBuilder::SampleSumCompound(vec![f1, f2]);
let dice = f.build();
let d = dice.distribution;
assert_eq!(d, unif(vec![0]));
}
fn unif(v: Vec<Value>) -> Vec<(Value, Prob)> {
let mut hashmap = DistributionHashMap::new();
let l = v.len();
let prob = Prob::new(1u64, l as u64);
v.iter().for_each(|e| {
if hashmap.contains_key(e) {
*hashmap.get_mut(e).unwrap() += &prob;
} else {
hashmap.insert(*e, prob.clone());
}
});
let mut distribution_vec = hashmap.into_iter().collect::<Vec<(Value, Prob)>>();
distribution_vec.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
return distribution_vec;
}
#[test]
fn calculating_accumulated_distribution_test() {
let dices = vec!["1w6+1", "3w8-3", "max(1,2,3)"];
let last_elements_of_acc_distr: Vec<Prob> = dices
.iter()
.map(|e| {
DiceBuilder::from_string(&e)
.unwrap()
.build()
.cumulative_distribution
.last()
.unwrap()
.1
.clone()
})
.collect();
for e in last_elements_of_acc_distr {
assert_eq!(e, Prob::new(1u64, 1u64));
}
}
#[test]
fn test_dice_builder_to_string() {
let string_in = "1xd6+7";
let string_out = DiceBuilder::from_string(string_in).unwrap().to_string();
assert_eq!(string_in, string_out)
}
#[test]
fn test_build_and_mean() {
let dice_builder = DiceBuilder::from_string("2d6+4").unwrap();
let dice = dice_builder.build();
let mean = dice.mean;
assert_eq!(mean.to_f64().unwrap(), 11.0);
}
#[test]
fn prob_tests() {
let d = Dice::build_from_string("2w6").unwrap();
assert_eq!(d.prob(7), Prob::new(1u64, 6u64));
assert_eq!(d.prob_lt(7), Prob::new(15u64, 36u64));
assert_eq!(d.prob_gt(7), Prob::new(15u64, 36u64));
assert_eq!(d.prob_lte(7), Prob::new(21u64, 36u64));
assert_eq!(d.prob_gte(7), Prob::new(21u64, 36u64));
assert_eq!(d.prob_lt(-3), Prob::zero());
assert_eq!(d.prob_lt(-3), Prob::zero());
}
#[test]
fn quantile_tests() {
let d = Dice::build_from_string("2d6").unwrap();
assert_eq!(d.quantile(1.0), 12);
assert_eq!(d.quantile(0.0), 2);
assert_eq!(d.quantile(0.5), 7);
assert_eq!(d.quantile(Prob::from_str("1/2").unwrap()), 7);
assert_eq!(d.quantile(Prob::from_str("-1/8").unwrap()), 2);
}
}