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) { 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::>() .as_slice() { [v] => Flat(v), [n, d] => { let mut total = 0; for _ in 0..n { total += rng.gen_range(1..=d) } Roll(total) } _ => panic!() } }