add: use fixed-size array and no allocs
This commit is contained in:
+52
-32
@@ -1,45 +1,55 @@
|
|||||||
use nom::{branch::alt, character::complete, IResult};
|
use nom::{branch::alt, character::complete, IResult};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
pub struct Roller {
|
pub struct Roller<const S: usize> {
|
||||||
expr: Expr,
|
exprs: [Option<Cmd>; S],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Roller {
|
impl<const S: usize> Roller<S> {
|
||||||
/// parse converts the str, of form:
|
/// parse converts the str, of form:
|
||||||
/// 2d8 + 1d8 + 4
|
/// 2d8+1d8+4
|
||||||
/// into a parsed expression, returning an Expr enum
|
/// into a parsed expression.
|
||||||
/// and the remaining unparsed string.
|
pub fn parse(mut expr: &str) -> Result<Roller<S>, nom::Err<nom::error::Error<&str>>> {
|
||||||
pub fn parse(expr: &str) -> Result<Roller, nom::Err<nom::error::Error<&str>>> {
|
let mut op = Oper::Add;
|
||||||
let (mut expr, left) = term(expr)?;
|
let mut exprs = [const { None }; S];
|
||||||
let mut res = Expr::Term(left);
|
let mut i = 0;
|
||||||
while expr.len() > 0 {
|
while expr.len() > 0 {
|
||||||
let (e, oper) = oper(expr)?;
|
let (e, term) = term(expr)?;
|
||||||
let (e, term) = term(e)?;
|
expr = e;
|
||||||
res = Expr::Op(Box::new(res), oper, Box::new(Expr::Term(term)));
|
exprs[i] = Some(Cmd{term, oper: op});
|
||||||
|
i += 1;
|
||||||
|
if i == exprs.len() {
|
||||||
|
return Err(nom::Err::Incomplete(nom::Needed::new(S + 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the next oper
|
||||||
|
if e.len() > 0 {
|
||||||
|
let (e, _op) = oper(expr)?;
|
||||||
|
op = _op;
|
||||||
expr = e;
|
expr = e;
|
||||||
}
|
}
|
||||||
Ok(Roller{expr: res})
|
}
|
||||||
|
Ok(Roller{exprs})
|
||||||
}
|
}
|
||||||
pub fn roll<R: Rng>(&self, rng: &mut R) -> u64 {
|
pub fn roll<R: Rng>(&self, rng: &mut R) -> u64 {
|
||||||
self.expr.val(rng)
|
let mut sum = 0;
|
||||||
|
for expr in &self.exprs {
|
||||||
|
if expr.is_none() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let cmd = expr.as_ref().unwrap();
|
||||||
|
match cmd.oper {
|
||||||
|
Oper::Add => sum += cmd.term.val(rng),
|
||||||
|
Oper::Sub => sum -= cmd.term.val(rng),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
sum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Expr {
|
struct Cmd {
|
||||||
Term(Term),
|
oper: Oper,
|
||||||
Op(Box<Expr>, Oper, Box<Expr>),
|
term: Term,
|
||||||
}
|
|
||||||
|
|
||||||
impl Expr {
|
|
||||||
fn val<R: Rng>(&self, rng: &mut R) -> u64 {
|
|
||||||
match self {
|
|
||||||
Expr::Term(Term::Const(x)) => *x,
|
|
||||||
Expr::Term(Term::Roll(r)) => r.val(rng),
|
|
||||||
Expr::Op(a, Oper::Add, b) => a.val(rng) + b.val(rng),
|
|
||||||
Expr::Op(a, Oper::Sub, b) => a.val(rng) - b.val(rng),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Term {
|
enum Term {
|
||||||
@@ -47,6 +57,16 @@ enum Term {
|
|||||||
Const(u64),
|
Const(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Term {
|
||||||
|
fn val<R: Rng>(&self, rng: &mut R) -> u64 {
|
||||||
|
match self {
|
||||||
|
Term::Const(c) => *c,
|
||||||
|
Term::Roll(r) => r.val(rng),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
enum Oper {
|
enum Oper {
|
||||||
Add,
|
Add,
|
||||||
Sub,
|
Sub,
|
||||||
@@ -106,7 +126,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn roll_many_d6s() {
|
fn roll_many_d6s() {
|
||||||
let mut rng = StdRng::seed_from_u64(1337);
|
let mut rng = StdRng::seed_from_u64(1337);
|
||||||
let roller = Roller::parse("1d6").unwrap();
|
let roller = Roller::<1024>::parse("1d6").unwrap();
|
||||||
let mut results = [0u64; 6];
|
let mut results = [0u64; 6];
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
results[(roller.roll(&mut rng)-1) as usize] += 1;
|
results[(roller.roll(&mut rng)-1) as usize] += 1;
|
||||||
@@ -120,7 +140,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn roll_many_d6s_plus_2() {
|
fn roll_many_d6s_plus_2() {
|
||||||
let mut rng = StdRng::seed_from_u64(1337);
|
let mut rng = StdRng::seed_from_u64(1337);
|
||||||
let roller = Roller::parse("1d6+2").unwrap();
|
let roller = Roller::<1024>::parse("1d6+2").unwrap();
|
||||||
let mut results = [0u64; 6+2];
|
let mut results = [0u64; 6+2];
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
results[(roller.roll(&mut rng)-1) as usize] += 1;
|
results[(roller.roll(&mut rng)-1) as usize] += 1;
|
||||||
@@ -136,7 +156,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn roll_many_d6s_plus_1d6() {
|
fn roll_many_d6s_plus_1d6() {
|
||||||
let mut rng = StdRng::seed_from_u64(1337);
|
let mut rng = StdRng::seed_from_u64(1337);
|
||||||
let roller = Roller::parse("1d6+1d6").unwrap();
|
let roller = Roller::<1024>::parse("1d6+1d6").unwrap();
|
||||||
let mut results = [0u64; 6+6];
|
let mut results = [0u64; 6+6];
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
results[(roller.roll(&mut rng)-1) as usize] += 1;
|
results[(roller.roll(&mut rng)-1) as usize] += 1;
|
||||||
@@ -151,7 +171,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn roll_many_d6s_plus_1d6_minus_one() {
|
fn roll_many_d6s_plus_1d6_minus_one() {
|
||||||
let mut rng = StdRng::seed_from_u64(1337);
|
let mut rng = StdRng::seed_from_u64(1337);
|
||||||
let roller = Roller::parse("1d6+1d6-1").unwrap();
|
let roller = Roller::<1024>::parse("1d6+1d6-1").unwrap();
|
||||||
let mut results = [0u64; 6+6];
|
let mut results = [0u64; 6+6];
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
results[(roller.roll(&mut rng)-1) as usize] += 1;
|
results[(roller.roll(&mut rng)-1) as usize] += 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user