Last active
July 9, 2019 23:24
-
-
Save reckenrode/dd103588725717813f3696de20cf9322 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "ability-scores" | |
version = "0.1.0" | |
authors = ["Randy Eckenrode <[email protected]>"] | |
edition = "2018" | |
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |
[dependencies] | |
rand = "0.7" | |
streaming-stats = "0.2" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use rand::prelude::*; | |
// use statrs::statistics::*; | |
use stats::{MinMax, OnlineStats}; | |
use std::thread; | |
#[derive(Debug)] | |
struct AbilityScores([u8; 6]); | |
impl AbilityScores { | |
fn point_buy_value(&self) -> i16 { | |
const POINT_BUY_MAPPING: [i16; 19] = [ | |
-30, -25, -20, -16, -12, -9, -6, -4, -2, -1, 0, 1, 2, 3, 5, 7, 10, 13, 17, | |
]; | |
let AbilityScores(arr) = self; | |
arr.iter().fold(0, |point_buy, score| { | |
point_buy + POINT_BUY_MAPPING[usize::from(*score)] | |
}) | |
} | |
} | |
impl<I: IntoIterator<Item = u8>> From<I> for AbilityScores { | |
fn from(v: I) -> Self { | |
let mut storage = [0u8; 6]; | |
let index = v.into_iter().take(6).fold(0, |index, x| { | |
storage[index] = x; | |
index + 1 | |
}); | |
if index != storage.len() { | |
panic!("tried to convert an iterator containing fewer than 6 elements") | |
} else { | |
AbilityScores(storage) | |
} | |
} | |
} | |
fn deal_scores(rng: &mut impl Rng) -> AbilityScores { | |
let cards = { | |
let mut deck1: Vec<u8> = (4..=9).collect(); | |
let mut deck2: Vec<u8> = (4..=9).collect(); | |
deck1.shuffle(rng); | |
deck2.shuffle(rng); | |
deck1.into_iter().zip(deck2.into_iter()).map(|(x, y)| x + y) | |
}; | |
AbilityScores::from(cards) | |
} | |
fn roll_4d6_drop_lowest(rng: &mut impl Rng) -> u8 { | |
let rolls: [u8; 4] = [ | |
rng.gen_range(1, 7), | |
rng.gen_range(1, 7), | |
rng.gen_range(1, 7), | |
rng.gen_range(1, 7), | |
]; | |
let (total, lowest) = rolls.iter().fold((0, std::u8::MAX), |(x, min), y| { | |
let min = std::cmp::min(min, *y); | |
(x + y, min) | |
}); | |
total - lowest | |
} | |
fn roll_scores(rng: &mut impl Rng) -> AbilityScores { | |
let scores: [u8; 6] = [ | |
roll_4d6_drop_lowest(rng), | |
roll_4d6_drop_lowest(rng), | |
roll_4d6_drop_lowest(rng), | |
roll_4d6_drop_lowest(rng), | |
roll_4d6_drop_lowest(rng), | |
roll_4d6_drop_lowest(rng), | |
]; | |
AbilityScores::from(scores.iter().cloned()) | |
} | |
fn print_stats(heading: &str, scores: (f64, f64, f64, f64)) { | |
let (mean, std_dev, min, max) = scores; | |
println!("{}", heading); | |
println!("Mean: {: >6.2}, StdDev: {: >6.2}", mean, std_dev); | |
println!("Min: {: >6.2}, Max: {: >6.2}", min, max); | |
} | |
fn main() { | |
const MAX_ITER: usize = 10_000_000; | |
let card_result = thread::spawn(|| { | |
let mut rng = rand::thread_rng(); | |
let mut minmax = MinMax::new(); | |
let mut stats = OnlineStats::new(); | |
for _ in 0..MAX_ITER { | |
let value = deal_scores(&mut rng).point_buy_value(); | |
minmax.add(value); | |
stats.add(value); | |
} | |
( | |
stats.mean(), | |
stats.stddev(), | |
f64::from(*minmax.min().unwrap()), | |
f64::from(*minmax.max().unwrap()), | |
) | |
}); | |
let roll_result = thread::spawn(|| { | |
let mut rng = rand::thread_rng(); | |
let mut minmax = MinMax::new(); | |
let mut stats = OnlineStats::new(); | |
for _ in 0..MAX_ITER { | |
let value = roll_scores(&mut rng).point_buy_value(); | |
minmax.add(value); | |
stats.add(value); | |
} | |
( | |
stats.mean(), | |
stats.stddev(), | |
f64::from(*minmax.min().unwrap()), | |
f64::from(*minmax.max().unwrap()), | |
) | |
}); | |
let scores = card_result.join().unwrap(); | |
print_stats("Card Method", scores); | |
let scores = roll_result.join().unwrap(); | |
print_stats("Rolling Method", scores); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment