dicelab(1)

πŸ’– NAME πŸ’–

okay so bestie, welcome to dicelab(1) β€” the manual for dicelab, and it's literally here to roll and examine dice rolling schemes. That's the whole main character energy. 🎲✨


🌈 SYNOPSIS 🌈

dicelab [options] [-f <file>]

✨ OPTIONS ✨

okay so here's the flag menu, bestie. every single one, no skips. πŸ’…

  • -h, --help, -? β€” print a help message
  • --version, -v β€” display version number
  • --calc, -c β€” calculate the distribution
  • --roll, -r β€” roll the dice as specified. This will also be used if no other action is requested
  • --eval, -e β€” reroll many times and sum up the results to get a statistical distribution of values
  • --count, -n β€” specify the number of rerolls for --eval, default is 10000
  • --print-tree, -p β€” print the parse tree (for debugging purposes)
  • -f<file> β€” read the scheme description from file instead from stdin

πŸ’œ DESCRIPTION πŸ’œ

dicelab reads a description of a dice rolling scheme from a file, or from stdin if no file is specified, and then it rolls or examines this scheme. That's the tea. β˜•


πŸŽ€ QUICK START πŸŽ€

Single die rolls? Easy. Use the d operator, then the number of faces on the die you're rolling. So d6 rolls a single six-sided die, and d2 flips a coin. πŸͺ™

Expressions can be modified by the standard arithmetic operators. d10-1 gives you a value between 0 and 9, inclusive.

To roll multiple dice of the same type, use the repetition operator #. So 2#d6 rolls two six-sided dice. This is not the same as 2*d6, which rolls only a single die but multiplies the result by two, or 2d6, which will cause a syntax error. πŸ’€

To get the sum of two six-sided dice, do sum(2#d6). Slay. ✨


🌸 FULL SYNTAX 🌸

here's the whole grammar, no cap. copy it exactly, bestie.

        <integer> ::=
            -?[0-9]+
        <variable> ::=
            [A-Za-z]+
        <scalar> ::=
            <integer>
            | <variable>
            | ( <scalar> )
            | - <scalar>
            | <scalar> + <scalar>
            | <scalar> - <scalar>
            | <scalar> * <scalar>
            | <scalar> / <scalar>
            | <scalar> % <scalar>
            | <scalar> ^ <scalar>
            | <scalar> . <scalar>
            | d<scalar>
            | sum <expr>
            | prod <expr>
            | count <expr>
        <list> ::=
            <scalar> # <expr>
            | ( <list> )
            | <scalar> .. <scalar>
            | <expr> , <expr>
            | perm <expr>
            | sort <expr>
            | rev <expr>
            | (drop|keep)? low <scalar> <expr>
            | (drop|keep)? high <scalar> <expr>
            | (drop|keep)? first <scalar> <expr>
            | (drop|keep)? last <scalar> <expr>
            | (drop|keep)? == <scalar> <expr>
            | (drop|keep)? != <scalar> <expr>
            | (drop|keep)? < <scalar> <expr>
            | (drop|keep)? > <scalar> <expr>
            | (drop|keep)? <= <scalar> <expr>
            | (drop|keep)? >= <scalar> <expr>
            | if <expr> then <expr> else <expr>
            | let <variable> = <expr> in <expr>
            | while <variable> = <expr> do <expr>
            | foreach <variable> in <expr> do <expr>
        <expr> ::=
            <scalar>
            <list>
        <input> ::=
            <expr>
            | <expr> ; <expr>

Comments? Drop them in with double slashes (//), just like C. πŸ’¬


πŸ¦‹ SEMANTICS πŸ¦‹

okay now the meanings, bestie. this is where it gets iconic. πŸ‘‘

+, -, *, /, ^

These are the familiar binary arithmetic operators for addition, subtraction, multiplication, division, and exponentiation. Division rounds toward zero. Examples: 5+7, d6-1, 2^10. πŸ”₯

-

This is the unary minus operator. Examples: -1.

%

This is the modulus operator. x % y gives the remainder of x divided by y. Examples: 11%2, d6%3.

.

This is the scalar concatenation operator. x . y gives xy, the concatenation of x and y. Examples: -10.9, d6.d6.

d

This is the die roll operator. dn gives the value of a single roll of an n-sided die. Examples: d6, 2#d6.

sum, prod

These are the extended sum and product operators. If e is an expression, sum e and prod e give the sum of the members of e and the product of the members of e, respectively. Examples: sum(1..100), prod(3#d6).

count

This is the list size operator. If e is an expression, then count e gives the number of members of e. Examples: count(1,2,3), count(== 6 10#d6).

#

This is the list repetition operator. If n is a nonnegative scalar and e is an expression, then n#e is a list containing the results of n evaluations of e. Examples: 10#8, 3#d10.

..

This is the range operator. If x and y are scalars, then x..y is a list consisting of the interval [x,y]. If x>y, then the resulting list is empty. Examples: 1..10, 4..d10.

,

This is the list concatenation operator. v,u gives the list consisting of all of the members of v, followed by all of the members of u. Examples: 1,2 4,(3#d6).

sort

This is the list sorting operator. sort e sorts the list e in ascending order. Examples: sort(10#d6).

perm

This is the list permutation operator. perm e results in a random permutation of the list e. Use perm to shuffle a list. Examples: perm(1..52).

rev

This is the list reversal operator. rev e results in a list with the same members as the list e, but in reverse order. Examples: rev(1..10), rev sort(10#d8).

low, high

These operators act as filters by finding the least and greatest values in lists. If n is a nonnegative scalar and e is an expression, then low n e gives the n least members of e, and high n e gives the n greatest members of e. Examples: high 3 5#d6.

first, last

These operators act as filters by finding initial and final segments of lists. If n is a nonnegative scalar and e is an expression, then first n e gives the first n members of e, and last n e gives the last n members of e. Examples: first 3 (1..10).

==, !=, <, >, <=, >=

These operators act as filters by finding values in lists which meet given conditions. If x is a scalar and e is an expression, then == x e gives the list of members of e equal to x; != x e gives the list of members of e not equal to x; < x e gives the list of members of e less than x; > x e gives the list of members of e greater than x; <= x e gives the list of members of e less than or equal to x; >= x e gives the list of members of e greater than or equal to x. Examples: >= 3 5#d6.

drop, keep

These operators modify filters on lists. If fop is a filter operation on an expression e, then keep fop e has the same result as fop e and drop fop e evaluates to e less keep fop e. In other words, drop negates filter conditions, and keep affirms them. keep is never necessary and exists only for symmetry. Examples: sum(drop low 1 4#d6).

let

This is the variable assignment and substitution operator. If x is a variable and e and f are expressions, then let x = e in f gives the list which results from evaluating f with the value of e substituted for every occurrence of x in f. Evaluation of e is done prior to substitution. Examples: let x = d6 in x*x.

foreach

This is the bounded iteration operator. If x is a variable and e and f are expressions, then foreach x in e do f gives the list which results from assigning to x each of the members of e and evaluating f. Examples: foreach x in c do x+1.

while

This is the unbounded iteration operator. If x is a variable and e and f are expressions, then while x = e do f is the list v0,v1,...,vn, where v0 is the result of evaluating e and vi+1 is the result of assigning vi to x and evaluating f, stopping at the first vi which is empty. Examples: while x=d6 do ((count <6 x)#d6).

if

This is the branching operator. If e, f, and g are expressions, then if e then f else g gives f if e is nonempty, and g otherwise. Examples: if count(>4 2#d6) then 1 else 0.

okay bestie, buckle UP πŸ’– this is the part where all the theory turns into stuff you can actually type into your terminal and watch the numbers go brrr. every single one of these is a real, working thing. copy, paste, slay.


πŸŽ€ count the dice that beat a 7 πŸŽ€

you got 5#d10, you wanna know how many of them rolled higher than 7. that's it, that's the whole vibe:

count >7 5#d10

πŸ’… count >7 minus count ==1 πŸ’…

same energy, but now you're subtracting the number of dice that rolled exactly a 1 from the number that rolled above 7. double counting, we love to see it:

let c=5#d10 in (count >7 c)-(count ==1 c)

πŸ¦‹ how many rolls until somebody hits a 6 πŸ¦‹

keep rolling d6s until a 6 shows up, count how many rolls it took. the while loop is doing all the heavy lifting here:

count (while x=d6 do ((count <6 x)#d6))

✨ same thing but, like, efficiently ✨

literally the same answer as above, but smarter about it. notice the d6/6 β€” it normalizes a die into a 0-or-1 situation so the loop just stops the second it sees the good stuff. main character optimization energy:

count (while x=(d6/6) do ((count <1 x)#(d6/6)))

πŸ‘‘ D&D character stats, no cap πŸ‘‘

roll four d6, drop the lowest one, add the rest. do it six times. that's an attribute array, bestie, and yes it's the classic:

6#sum(drop low 1 4#d6)

🌸 the 11..66 morale check table 🌸

this is for The Gamers' Civil War Brigade Series. two d6 rolled as tens and ones, giving you a result on the 11..66 table. iconic little dot notation:

d6.d6

⭐ median of 3 d20s ⭐

you want the middle value of three d20s? drop the high one and the low one, keep whatever's left in the middle. high 1 low 2, baby:

high 1 low 2 3#d20

πŸ”₯ 3d6 but you reroll every 6 πŸ”₯

exploding dice behaviour, hand-rolled. keep rerolling all the 6s you got and add up the total. the dice will absolutely humble you:

sum(while x=3#d6 do ((count ==6 x)#d6))

πŸ“ roll 7 d10 and find the biggest pile of matching dice πŸ“

for every possible face value 1..10, sum up how many dice show it, then take the largest of those sums. grouping by value, we stan:

let x = 7#d10 in high 1 (foreach y in 1..10 do sum (==y x))

🧚 the Fibonacci sequence 🧚

the deal: Fn = Fn-1 + Fn-2, with F1 = F2 = 1. we're calculating the first twenty Fibonacci numbers right here. yes this is a dice roller and yes it does recursion, we contain multitudes ✨

let n = 20 in
  let f = (1,1) in
    foreach i in 1..n do
      let f = (f,sum(high 2 f)) in
        if ==n i then f else ()

πŸ’€ Risk battles, but make it dice lab πŸ’€

here's the setup: the attacker rolls 3d6 and the defender rolls 2d6. the highest attacker die gets matched up against the highest defender die, and the second-highest attacker die gets matched against the second-highest defender die. in both of those matchups, the highest wins, and ties go to the defender (sorry, attacker). this counts the number of attacker wins:

let a = 3#d6 in
  let b = 2#d6 in
    count( (<(high 1 a) high 1 b),
           (<(high 1 low 2 a) low 1 b))

🩰 Storyteller roll, target 8, botches at -1 🩰

five d10, target number 8, and botches get flagged at -1. successes go up, ones drag you down, and the whole thing gets wrapped in logic so you never dip below a botch:

let c=5#d10 in
  let succs = count >7 c in
    let ones = count ==1 c in
      if >0 succs then high 1 (0,succs-ones)
      else if >0 ones then -1 else 0

πŸš€ Silent Death combat, the full saga πŸš€

okay this one is a lot, so let's go slow. Combat in Silent Death is rather complex. Three dice are rolled. If their sum is above a target, the roll is a hit. To calculate damage, the same dice are sorted:

  • if all three are equal, all are summed to yield the damage
  • if the least two are equal but the third is higher, the high die is the damage
  • if the two highest are equal but the third is lower, the two high dice are summed to yield the damage
  • if all three dice are different, the middle die is the damage

this example assumes the dice are two d8s and a d10, with a target number of 15:

let x = 2#d8,d10 in
  (count >15 sum x)#
    let a = low 1 x in               // low die
    let b = high 1 low 2 x in        // middle die
    let c = high 1 x in              // high die
      if ==a ==b c then a+b+c        // all equal
      else if ==a <c b then c        // two low equal
      else if >a ==c b then b+c      // two high equal
       else b                            // all different
bestie the comments in that code are doing the lord's work, read them, they explain the whole branch tree πŸ’–

πŸ’– CREDITS πŸ’–

giving credit where credit is due, always:

Dicelab is based on the excellent work "roll" by Torben Mogensen (http://www.diku.dk/~torbenm/Dice.zip). Without his work and comments, this would hardly ever have happened. fr fr, this project literally would not exist.

The current language specification and the extensions to the original language are derived from the work of Joel Uckelman (http://dice.nomic.net/bones.html), and most of the documentation is stolen from him as well. borrowed with love 🫢

This code was written by Robert Lemmen <robertle@semistable.com>, who would be glad to hear your questions and remarks. so if you have questions, remarks, or just wanna say hi β€” that's the human to bother πŸ’Œ


and that's the whole page, bestie!! you made it all the way to the end πŸŽ‰ this gorgeous thing is dicelab v0.2, dated February 2007, and it's been out here rolling dice and serving looks ever since. go roll something unhinged with it, you've earned it. bye bestie πŸ’…βœ¨

πŸ“¦ where this page comes from