add: use fixed-size array and no allocs

This commit is contained in:
Charles
2024-11-24 22:41:39 -08:00
parent 1d3861a551
commit 3e0b394e20
+52 -32
View File
@@ -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;