// Copyright 2017 Kyle Mayes // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #![feature(i128_type)] #[cfg(feature="benchmark")] extern crate microbench; #[macro_use] extern crate dice; extern crate rand; use dice::{Binary, Expression, Fold, Ratio, Reroll}; macro_rules! assert_results_eq { ($left:expr, $right:expr) => ({ assert_eq!($left.collect::>(), $right.collect::>()); }); } macro_rules! assert_probabilities_eq { ($left:expr, [$(($r:expr, ($n:expr, $d:expr))), +,]) => ({ let left = $left; let right = vec![$(($r, Ratio::new($n, $d))), *]; if left.len() == right.len() { for (l, r) in left.iter().zip(right.iter()) { assert_eq!(l, r); } } else { assert_eq!(left, right); } }); } #[test] fn test_statistics_die() { let die = d!(6); assert_eq!(die.minimum(), 1); assert_eq!(die.maximum(), 6); assert_eq!(die.average(None), Ratio::new(7, 2)); assert_eq!(die.average(Some(Reroll::new(2))), Ratio::new(25, 6)); assert_results_eq!(die.results(), (1..7)); assert_probabilities_eq!(die.probabilities(None), [ (1, (1, 6)), (2, (1, 6)), (3, (1, 6)), (4, (1, 6)), (5, (1, 6)), (6, (1, 6)), ]); assert_probabilities_eq!(die.probabilities(Some(Reroll::new(2))), [ (1, (2, 36)), (2, (2, 36)), (3, (8, 36)), (4, (8, 36)), (5, (8, 36)), (6, (8, 36)), ]); } #[test] fn test_statistics_dice() { let dice = d!(2, 6); assert_eq!(dice.minimum(Fold::DropMinimum), 1); assert_eq!(dice.minimum(Fold::DropMaximum), 1); assert_eq!(dice.minimum(Fold::Minimum), 1); assert_eq!(dice.minimum(Fold::Maximum), 1); assert_eq!(dice.minimum(Fold::Sum), 2); assert_eq!(dice.maximum(Fold::DropMinimum), 6); assert_eq!(dice.maximum(Fold::DropMaximum), 6); assert_eq!(dice.maximum(Fold::Minimum), 6); assert_eq!(dice.maximum(Fold::Maximum), 6); assert_eq!(dice.maximum(Fold::Sum), 12); assert_eq!(dice.average(None, Fold::DropMinimum), Ratio::new(161, 36)); assert_eq!(dice.average(Some(Reroll::new(2)), Fold::DropMinimum), Ratio::new(1607, 324)); assert_eq!(dice.average(None, Fold::DropMaximum), Ratio::new(91, 36)); assert_eq!(dice.average(Some(Reroll::new(2)), Fold::DropMaximum), Ratio::new(1093, 324)); assert_eq!(dice.average(None, Fold::Minimum), Ratio::new(91, 36)); assert_eq!(dice.average(Some(Reroll::new(2)), Fold::Minimum), Ratio::new(1093, 324)); assert_eq!(dice.average(None, Fold::Maximum), Ratio::new(161, 36)); assert_eq!(dice.average(Some(Reroll::new(2)), Fold::Maximum), Ratio::new(1607, 324)); assert_eq!(dice.average(None, Fold::Sum), Ratio::new(7, 1)); assert_eq!(dice.average(Some(Reroll::new(2)), Fold::Sum), Ratio::new(25, 3)); assert_results_eq!(dice.results(Fold::DropMinimum), (1..7)); assert_results_eq!(dice.results(Fold::DropMaximum), (1..7)); assert_results_eq!(dice.results(Fold::Minimum), (1..7)); assert_results_eq!(dice.results(Fold::Maximum), (1..7)); assert_results_eq!(dice.results(Fold::Sum), (2..13)); assert_probabilities_eq!(dice.probabilities(None, Fold::DropMinimum), [ (1, (1, 36)), (2, (3, 36)), (3, (5, 36)), (4, (7, 36)), (5, (9, 36)), (6, (11, 36)), ]); assert_probabilities_eq!(dice.probabilities(Some(Reroll::new(2)), Fold::DropMinimum), [ (1, (4, 1296)), (2, (12, 1296)), (3, (128, 1296)), (4, (256, 1296)), (5, (384, 1296)), (6, (512, 1296)), ]); assert_probabilities_eq!(dice.probabilities(None, Fold::DropMaximum), [ (1, (11, 36)), (2, (9, 36)), (3, (7, 36)), (4, (5, 36)), (5, (3, 36)), (6, (1, 36)), ]); assert_probabilities_eq!(dice.probabilities(Some(Reroll::new(2)), Fold::DropMaximum), [ (1, (140, 1296)), (2, (132, 1296)), (3, (448, 1296)), (4, (320, 1296)), (5, (192, 1296)), (6, (64, 1296)), ]); assert_probabilities_eq!(dice.probabilities(None, Fold::Minimum), [ (1, (11, 36)), (2, (9, 36)), (3, (7, 36)), (4, (5, 36)), (5, (3, 36)), (6, (1, 36)), ]); assert_probabilities_eq!(dice.probabilities(Some(Reroll::new(2)), Fold::Minimum), [ (1, (140, 1296)), (2, (132, 1296)), (3, (448, 1296)), (4, (320, 1296)), (5, (192, 1296)), (6, (64, 1296)), ]); assert_probabilities_eq!(dice.probabilities(None, Fold::Maximum), [ (1, (1, 36)), (2, (3, 36)), (3, (5, 36)), (4, (7, 36)), (5, (9, 36)), (6, (11, 36)), ]); assert_probabilities_eq!(dice.probabilities(Some(Reroll::new(2)), Fold::Maximum), [ (1, (4, 1296)), (2, (12, 1296)), (3, (128, 1296)), (4, (256, 1296)), (5, (384, 1296)), (6, (512, 1296)), ]); assert_probabilities_eq!(dice.probabilities(None, Fold::Sum), [ (2, (1, 36)), (3, (2, 36)), (4, (3, 36)), (5, (4, 36)), (6, (5, 36)), (7, (6, 36)), (8, (5, 36)), (9, (4, 36)), (10, (3, 36)), (11, (2, 36)), (12, (1, 36)), ]); assert_probabilities_eq!(dice.probabilities(Some(Reroll::new(2)), Fold::Sum), [ (2, (4, 1296)), (3, (8, 1296)), (4, (36, 1296)), (5, (64, 1296)), (6, (128, 1296)), (7, (192, 1296)), (8, (224, 1296)), (9, (256, 1296)), (10, (192, 1296)), (11, (128, 1296)), (12, (64, 1296)), ]); let dice = d!(3, 6); assert_eq!(dice.minimum(Fold::DropMinimum), 2); assert_eq!(dice.minimum(Fold::DropMaximum), 2); assert_eq!(dice.minimum(Fold::Minimum), 1); assert_eq!(dice.minimum(Fold::Maximum), 1); assert_eq!(dice.minimum(Fold::Sum), 3); assert_eq!(dice.maximum(Fold::DropMinimum), 12); assert_eq!(dice.maximum(Fold::DropMaximum), 12); assert_eq!(dice.maximum(Fold::Minimum), 6); assert_eq!(dice.maximum(Fold::Maximum), 6); assert_eq!(dice.maximum(Fold::Sum), 18); assert_eq!(dice.average(None, Fold::DropMinimum), Ratio::new(203, 24)); assert_eq!(dice.average(Some(Reroll::new(2)), Fold::DropMinimum), Ratio::new(2065, 216)); assert_eq!(dice.average(None, Fold::DropMaximum), Ratio::new(133, 24)); assert_eq!(dice.average(Some(Reroll::new(2)), Fold::DropMaximum), Ratio::new(517, 72)); assert_eq!(dice.average(None, Fold::Minimum), Ratio::new(49, 24)); assert_eq!(dice.average(Some(Reroll::new(2)), Fold::Minimum), Ratio::new(635, 216)); assert_eq!(dice.average(None, Fold::Maximum), Ratio::new(119, 24)); assert_eq!(dice.average(Some(Reroll::new(2)), Fold::Maximum), Ratio::new(383, 72)); assert_eq!(dice.average(None, Fold::Sum), Ratio::new(21, 2)); assert_eq!(dice.average(Some(Reroll::new(2)), Fold::Sum), Ratio::new(25, 2)); assert_results_eq!(dice.results(Fold::DropMinimum), (2..13)); assert_results_eq!(dice.results(Fold::DropMaximum), (2..13)); assert_results_eq!(dice.results(Fold::Minimum), (1..7)); assert_results_eq!(dice.results(Fold::Maximum), (1..7)); assert_results_eq!(dice.results(Fold::Sum), (3..19)); assert_probabilities_eq!(dice.probabilities(None, Fold::DropMinimum), [ (2, (1, 216)), (3, (3, 216)), (4, (7, 216)), (5, (12, 216)), (6, (19, 216)), (7, (27, 216)), (8, (34, 216)), (9, (36, 216)), (10, (34, 216)), (11, (27, 216)), (12, (16, 216)), ]); assert_probabilities_eq!(dice.probabilities(Some(Reroll::new(2)), Fold::DropMinimum), [ (2, (8, 46656)), (3, (24, 46656)), (4, (128, 46656)), (5, (384, 46656)), (6, (1664, 46656)), (7, (3456, 46656)), (8, (6176, 46656)), (9, (9216, 46656)), (10, (10496, 46656)), (11, (9216, 46656)), (12, (5888, 46656)), ]); assert_probabilities_eq!(dice.probabilities(None, Fold::DropMaximum), [ (2, (16, 216)), (3, (27, 216)), (4, (34, 216)), (5, (36, 216)), (6, (34, 216)), (7, (27, 216)), (8, (19, 216)), (9, (12, 216)), (10, (7, 216)), (11, (3, 216)), (12, (1, 216)), ]); assert_probabilities_eq!(dice.probabilities(Some(Reroll::new(2)), Fold::DropMaximum), [ (2, (416, 46656)), (3, (792, 46656)), (4, (3080, 46656)), (5, (4608, 46656)), (6, (8192, 46656)), (7, (9216, 46656)), (8, (8576, 46656)), (9, (6144, 46656)), (10, (3584, 46656)), (11, (1536, 46656)), (12, (512, 46656)), ]); assert_probabilities_eq!(dice.probabilities(None, Fold::Minimum), [ (1, (91, 216)), (2, (61, 216)), (3, (37, 216)), (4, (19, 216)), (5, (7, 216)), (6, (1, 216)), ]); assert_probabilities_eq!(dice.probabilities(Some(Reroll::new(2)), Fold::Minimum), [ (1, (7352, 46656)), (2, (6536, 46656)), (3, (18944, 46656)), (4, (9728, 46656)), (5, (3584, 46656)), (6, (512, 46656)), ]); assert_probabilities_eq!(dice.probabilities(None, Fold::Maximum), [ (1, (1, 216)), (2, (7, 216)), (3, (19, 216)), (4, (37, 216)), (5, (61, 216)), (6, (91, 216)), ]); assert_probabilities_eq!(dice.probabilities(Some(Reroll::new(2)), Fold::Maximum), [ (1, (8, 46656)), (2, (56, 46656)), (3, (1664, 46656)), (4, (6272, 46656)), (5, (13952, 46656)), (6, (24704, 46656)), ]); assert_probabilities_eq!(dice.probabilities(None, Fold::Sum), [ (3, (1, 216)), (4, (3, 216)), (5, (6, 216)), (6, (10, 216)), (7, (15, 216)), (8, (21, 216)), (9, (25, 216)), (10, (27, 216)), (11, (27, 216)), (12, (25, 216)), (13, (21, 216)), (14, (15, 216)), (15, (10, 216)), (16, (6, 216)), (17, (3, 216)), (18, (1, 216)), ]); assert_probabilities_eq!(dice.probabilities(Some(Reroll::new(2)), Fold::Sum), [ (3, (8, 46656)), (4, (24, 46656)), (5, (120, 46656)), (6, (296, 46656)), (7, (768, 46656)), (8, (1536, 46656)), (9, (2720, 46656)), (10, (4320, 46656)), (11, (5760, 46656)), (12, (7040, 46656)), (13, (7296, 46656)), (14, (6528, 46656)), (15, (5120, 46656)), (16, (3072, 46656)), (17, (1536, 46656)), (18, (512, 46656)), ]); } #[test] fn test_statistics_expression() { macro_rules! binary { ($binary:ident, $left:ident($($ltt:tt)*), $right:ident($($rtt:tt)*)) => ({ let left = Box::new(Expression::$left($($ltt)*)); let right = Box::new(Expression::$right($($rtt)*)); Expression::Binary(Binary::$binary, left, right) }); } let expression = binary!(Add, Die(d!(4), None), Die(d!(8), None)); assert_eq!(expression.minimum(), Some(2)); assert_eq!(expression.maximum(), Some(12)); assert_eq!(expression.average(), Some(Ratio::new(7, 1))); assert_eq!(expression.results(), Some((2..13).collect::>())); assert_probabilities_eq!(expression.probabilities().unwrap(), [ (2, (1, 32)), (3, (2, 32)), (4, (3, 32)), (5, (4, 32)), (6, (4, 32)), (7, (4, 32)), (8, (4, 32)), (9, (4, 32)), (10, (3, 32)), (11, (2, 32)), (12, (1, 32)), ]); let expression = binary!(Divide, Die(d!(4), None), Die(d!(8), None)); assert_eq!(expression.minimum(), Some(0)); assert_eq!(expression.maximum(), Some(4)); assert_eq!(expression.average(), Some(Ratio::new(17, 32))); assert_eq!(expression.results(), Some((0..5).collect::>())); assert_probabilities_eq!(expression.probabilities().unwrap(), [ (0, (22, 32)), (1, (6, 32)), (2, (2, 32)), (3, (1, 32)), (4, (1, 32)), ]); let expression = binary!(Multiply, Die(d!(4), None), Die(d!(8), None)); assert_eq!(expression.minimum(), Some(1)); assert_eq!(expression.maximum(), Some(32)); assert_eq!(expression.average(), Some(Ratio::new(45, 4))); let results = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 24, 28, 32]; assert_eq!(expression.results(), Some(results)); assert_probabilities_eq!(expression.probabilities().unwrap(), [ (1, (1, 32)), (2, (2, 32)), (3, (2, 32)), (4, (3, 32)), (5, (1, 32)), (6, (3, 32)), (7, (1, 32)), (8, (3, 32)), (9, (1, 32)), (10, (1, 32)), (12, (3, 32)), (14, (1, 32)), (15, (1, 32)), (16, (2, 32)), (18, (1, 32)), (20, (1, 32)), (21, (1, 32)), (24, (2, 32)), (28, (1, 32)), (32, (1, 32)), ]); let expression = binary!(Subtract, Die(d!(4), None), Die(d!(8), None)); assert_eq!(expression.minimum(), Some(-7)); assert_eq!(expression.maximum(), Some(3)); assert_eq!(expression.average(), Some(Ratio::new(-2, 1))); assert_eq!(expression.results(), Some((-7..4).collect::>())); assert_probabilities_eq!(expression.probabilities().unwrap(), [ (-7, (1, 32)), (-6, (2, 32)), (-5, (3, 32)), (-4, (4, 32)), (-3, (4, 32)), (-2, (4, 32)), (-1, (4, 32)), (0, (4, 32)), (1, (3, 32)), (2, (2, 32)), (3, (1, 32)), ]); } #[cfg(feature="benchmark")] #[test] fn bench_statistics_dice_probabilities() { println!(); let options = microbench::Options::default(); let dice = d!(8, 8); microbench::bench(&options, "8d8", || dice.probabilities(None, Fold::Sum)); } #[cfg(feature="benchmark")] #[test] fn bench_statistics_expression_probabilities() { println!(); let options = microbench::Options::default(); let left = Box::new(Expression::Dice(d!(4, 8), None, Fold::Sum)); let right = Box::new(Expression::Dice(d!(4, 8), None, Fold::Sum)); let expression = Expression::Binary(Binary::Add, left, right); microbench::bench(&options, "4d8 + 4d8", || expression.probabilities()); }