63 lines
1.1 KiB
Rust
63 lines
1.1 KiB
Rust
use rand::prelude::*;
|
|
|
|
enum Tok {
|
|
Roll(u32),
|
|
Flat(u32),
|
|
}
|
|
|
|
use Tok::*;
|
|
|
|
/// Rolls dice corresponding to a ttrpg dice specifier
|
|
/// ```
|
|
/// assert_eq!(roll("4d6 + 1d4 + 1").1.len(), 2)
|
|
/// ```
|
|
pub fn roll(spec: &str) -> (u32, Vec<u32>) {
|
|
let mut toks = spec.split_whitespace();
|
|
let mut rolls = vec![];
|
|
let mut total = 0;
|
|
|
|
let first = toks.next().unwrap();
|
|
match parse_tok(first) {
|
|
Roll(v) => {
|
|
total += v;
|
|
rolls.push(v);
|
|
}
|
|
Flat(v) => total += v,
|
|
};
|
|
let toks : Vec<&str> = toks.collect();
|
|
let mut toks = toks.chunks(2);
|
|
while let Some(&[op, tok]) = toks.next() {
|
|
let val = match parse_tok(tok) {
|
|
Roll(v) => {
|
|
rolls.push(v);
|
|
v
|
|
}
|
|
Flat(v) => v,
|
|
};
|
|
if op == "-" {
|
|
total -= val;
|
|
} else {
|
|
total += val;
|
|
}
|
|
}
|
|
(total, rolls)
|
|
}
|
|
|
|
fn parse_tok(tok: &str) -> Tok {
|
|
let mut rng = thread_rng();
|
|
match *tok.split('d')
|
|
.map(|x| x.parse().unwrap())
|
|
.collect::<Vec<_>>()
|
|
.as_slice() {
|
|
[v] => Flat(v),
|
|
[n, d] => {
|
|
let mut total = 0;
|
|
for _ in 0..n {
|
|
total += rng.gen_range(1..=d)
|
|
}
|
|
Roll(total)
|
|
}
|
|
_ => panic!()
|
|
}
|
|
}
|