add: dice roller
This commit is contained in:
Generated
+156
@@ -2,6 +2,162 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.164"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.92"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-roller"
|
name = "rust-roller"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|||||||
@@ -4,3 +4,5 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
nom = "7.1.3"
|
||||||
|
rand = "0.8.5"
|
||||||
|
|||||||
+165
@@ -0,0 +1,165 @@
|
|||||||
|
use nom::{branch::alt, character::complete, IResult};
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
pub struct Roller {
|
||||||
|
expr: Expr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Roller {
|
||||||
|
/// parse converts the str, of form:
|
||||||
|
/// 2d8 + 1d8 + 4
|
||||||
|
/// into a parsed expression, returning an Expr enum
|
||||||
|
/// and the remaining unparsed string.
|
||||||
|
pub fn parse(expr: &str) -> Result<Roller, nom::Err<nom::error::Error<&str>>> {
|
||||||
|
let (mut expr, left) = term(expr)?;
|
||||||
|
let mut res = Expr::Term(left);
|
||||||
|
while expr.len() > 0 {
|
||||||
|
let (e, oper) = oper(expr)?;
|
||||||
|
let (e, term) = term(e)?;
|
||||||
|
res = Expr::Op(Box::new(res), oper, Box::new(Expr::Term(term)));
|
||||||
|
expr = e;
|
||||||
|
}
|
||||||
|
Ok(Roller{expr: res})
|
||||||
|
}
|
||||||
|
pub fn roll<R: Rng>(&self, rng: &mut R) -> u64 {
|
||||||
|
self.expr.val(rng)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Expr {
|
||||||
|
Term(Term),
|
||||||
|
Op(Box<Expr>, Oper, Box<Expr>),
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
Roll(Roll),
|
||||||
|
Const(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Oper {
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Roll{
|
||||||
|
reps: u64,
|
||||||
|
dice: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Roll {
|
||||||
|
fn val<R: Rng>(&self, rng: &mut R) -> u64 {
|
||||||
|
let mut total = 0;
|
||||||
|
for _ in 0..self.reps {
|
||||||
|
total += rng.gen::<u64>() % self.dice + 1;
|
||||||
|
}
|
||||||
|
total
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn term(e: &str) -> IResult<&str, Term> {
|
||||||
|
alt((roll, cnst))(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn roll(e: &str) -> IResult<&str, Term> {
|
||||||
|
let (e, reps) = complete::u64(e)?;
|
||||||
|
let (e, _) = complete::char('d')(e)?;
|
||||||
|
let (e, dice) = complete::u64(e)?;
|
||||||
|
Ok((e, Term::Roll(Roll{reps, dice})))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cnst(e: &str) -> IResult<&str, Term> {
|
||||||
|
let (e, val) = complete::u64(e)?;
|
||||||
|
Ok((e, Term::Const(val)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn oper(e: &str) -> IResult<&str, Oper> {
|
||||||
|
alt((add, sub))(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(e: &str) -> IResult<&str, Oper> {
|
||||||
|
let (e, _) = complete::char('+')(e)?;
|
||||||
|
Ok((e, Oper::Add))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub(e: &str) -> IResult<&str, Oper> {
|
||||||
|
let (e, _) = complete::char('-')(e)?;
|
||||||
|
Ok((e, Oper::Sub))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use rand::{rngs::StdRng, SeedableRng};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn roll_many_d6s() {
|
||||||
|
let mut rng = StdRng::seed_from_u64(1337);
|
||||||
|
let roller = Roller::parse("1d6").unwrap();
|
||||||
|
let mut results = [0u64; 6];
|
||||||
|
for _ in 0..1000 {
|
||||||
|
results[(roller.roll(&mut rng)-1) as usize] += 1;
|
||||||
|
}
|
||||||
|
// We should have some in each bucket
|
||||||
|
for x in results {
|
||||||
|
assert!(x > 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roll_many_d6s_plus_2() {
|
||||||
|
let mut rng = StdRng::seed_from_u64(1337);
|
||||||
|
let roller = Roller::parse("1d6+2").unwrap();
|
||||||
|
let mut results = [0u64; 6+2];
|
||||||
|
for _ in 0..1000 {
|
||||||
|
results[(roller.roll(&mut rng)-1) as usize] += 1;
|
||||||
|
}
|
||||||
|
// Can't get a 1 or 2, but should have many others
|
||||||
|
assert!(results[0] == 0);
|
||||||
|
assert!(results[1] == 0);
|
||||||
|
for x in &results[2..] {
|
||||||
|
assert!(*x > 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roll_many_d6s_plus_1d6() {
|
||||||
|
let mut rng = StdRng::seed_from_u64(1337);
|
||||||
|
let roller = Roller::parse("1d6+1d6").unwrap();
|
||||||
|
let mut results = [0u64; 6+6];
|
||||||
|
for _ in 0..1000 {
|
||||||
|
results[(roller.roll(&mut rng)-1) as usize] += 1;
|
||||||
|
}
|
||||||
|
// Its impossible to roll 1
|
||||||
|
assert!(results[0] == 0);
|
||||||
|
for x in &results[1..] {
|
||||||
|
assert!(*x > 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn roll_many_d6s_plus_1d6_minus_one() {
|
||||||
|
let mut rng = StdRng::seed_from_u64(1337);
|
||||||
|
let roller = Roller::parse("1d6+1d6-1").unwrap();
|
||||||
|
let mut results = [0u64; 6+6];
|
||||||
|
for _ in 0..1000 {
|
||||||
|
results[(roller.roll(&mut rng)-1) as usize] += 1;
|
||||||
|
}
|
||||||
|
// Its impossible to roll 12
|
||||||
|
assert!(results[11] == 0);
|
||||||
|
for x in &results[..11] {
|
||||||
|
assert!(*x > 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user