# Programming Challenge: Dice Roller

Its been a while since we did one of these, which is a shame.

Create a program that takes is an input of the type: "d6 + 3" or "2d20 - 5", and return a valid roll.
The result should display both the actual rolls as well as the final result. The program should accept any valid roll of the type 'xdx'
Bonuses:

• Multiplication "d6 * 3"
• Division "d12 / 6"
• Polish notation "4d6 * (5d4 - 3)"

As a side note, it would be really cool if weekly programming challenges became a thing

1. 
mftrhu
(edited )
I usually write Python code, but Pascal was my first programming language - almost two decades ago now - and I have been using it a bit lately to solve some DMOJ problems. It's a decent fit for a...

I usually write Python code, but Pascal was my first programming language - almost two decades ago now - and I have been using it a bit lately to solve some DMOJ problems. It's a decent fit for a lot of them, and it produces ridiculously lean programs to boot.

The bonuses, though, will have to wait. Unless reverse polish notation is acceptable?

# Pascal (fpc 3.0.0)

``````program DiceRoller;
{\$mode objfpc}
uses
SysUtils, RegExpr;
var
line : String;

function DiceRoll(spec : String; show : Boolean = False) : Integer;
var
dice, faces, roll, i : Integer;
re : TRegExpr;
begin
re := TRegExpr.Create('(\d*)\s*d\s*(\d+)\s*(([+-])\s*(\d+))?');
if re.Exec(spec) then
begin
if Length(re.Match) > 0 then
dice := StrToInt(re.Match)
else
dice := 1;
faces := StrToInt(re.Match);
if re.Match = '+' then DiceRoll := + StrToInt(re.Match);
if re.Match = '-' then DiceRoll := - StrToInt(re.Match);
for i := 1 to dice do
begin
roll := Random(faces) + 1;
DiceRoll := DiceRoll + roll;
if show then Write(roll, ' ');
end;
end;
if show then WriteLn;
re.Free;
end;

begin
Randomize;
repeat
WriteLn(DiceRoll(line, True));
until line = '';
end.
``````

# Python 3.x

``````import re, sys, random, operator as op

def dice_roll(spec, show=False):
match = re.match(r"(\d*)d(\d+)(([+-])(\d+))?", spec)
dice = int(match.group(1) or "1")
faces = int(match.group(2))
adds = [1, -1][match.group(4) == "-"] * int(match.group(5)) if match.group(3) else 0
rolls = [random.randint(1, faces) for i in range(dice)]
if show:
print(" ".join(map(str, rolls)))

def rpn(line):
operators = {"+": op.add, "-": op.sub, "*": op.mul, "/": op.truediv}
stack = []
for token in line.split():
if "d" in token:
stack.append(dice_roll(token))
elif token in operators:
b, a = stack.pop(-1), stack.pop(-1)
stack.append(operators[token](a, b))
else:
try:
stack.append(int(token))
except ValueError:
pass
return(stack[-1])

if __name__ == "__main__":
for line in sys.stdin:
print(rpn(line.strip()))
``````

Edit: `s/dices/dice/g`, `s|(\d*)d(\d+)(([+-])(\d+))?|(\d*)\s*d\s*(\d+)\s*(([+-])\s*(\d+))?|`

1. 
sniper24
The input "4d6 + 12" throws an error, as does any addition attempt

The input "4d6 + 12" throws an error, as does any addition attempt

1. 
mftrhu
The Pascal one will just ignore the + 12, as I forgot to sprinkle around enough \s* in the regex (and (\d*)\s*d\s*(\d+)\s*(([+-])\s*(\d+))? should do the trick), but, as far as the Python version...

The Pascal one will just ignore the ` + 12`, as I forgot to sprinkle around enough `\s*` in the regex (and `(\d*)\s*d\s*(\d+)\s*(([+-])\s*(\d+))?` should do the trick), but, as far as the Python version is concerned, that's an user error.

It's using reverse polish notation, not infix notation, so it should either be `4d6+12` (and thus a single "dice roll" token), or `4d6 12 +`: `4d6 + 12` reads `4d6`, reads `+`, tries to pop two elements from a single-element-tall stack, and throws up its hands in failure at the stack underflow it gets as a result.

1. 
sniper24
(edited )
Ah I see, I misunderstood. A simple line.replace(" ", "") would fix that no? edit: indeed it does Edit 2: multiplication and division don't work, inputs of "2d1 * 2 1 1 2" and "3d5 / 5 4 5 1 10"...

Ah I see, I misunderstood.
A simple

`````` line.replace(" ", "")
``````

would fix that no?
edit: indeed it does

Edit 2: multiplication and division don't work, inputs of
"2d1 * 2
1 1
2" and "3d5 / 5
4 5 1
10" are wrong

Ok this is bothering me.
elif token in operators:
b, a = stack.pop(-1), stack.pop(-1)
stack.append(operators[token](a, b))
print(stack)
never executes

OK for operators only entries of the form 3d6 12 * are accepted, but if its + or - , 3d6+12 works fine, I assume cuz the other function works with them

1. 
mftrhu
No, that would break the tokenizer. I'm following Forth's example and just splitting the stream on spaces. I need to distinguish between A and B in A B +, removing spaces would just make the...

No, that would break the tokenizer. I'm following Forth's example and just splitting the stream on spaces. I need to distinguish between `A` and `B` in `A B +`, removing spaces would just make the program see `C+` and break - this is not aiming at being smart, or parsing infix notation at all, and the fact that `3d6+12` works at all is a more-or-less happy mistake, as I copy-pasted the regex before deciding to write the RPN function.

1. sniper24
Ignore me I was being a fool

Ignore me I was being a fool

2. sniper24
Ok so I'm just being an idiot, the rolling function can do addition and subtraction, but not the others, so infix worked for those, while multiplication and division only work for post fix

Ok so I'm just being an idiot, the rolling function can do addition and subtraction, but not the others, so infix worked for those, while multiplication and division only work for post fix

1 vote
2. 
teaearlgraycold
Looking at the Python code it seems like this requires inputs use postfix operators, right?

Looking at the Python code it seems like this requires inputs use postfix operators, right?

1 vote
1. mftrhu
Yeah, reverse polish notation. I mentioned it at the beginning of my comment.

Yeah, reverse polish notation. I mentioned it at the beginning of my comment.

1 vote
3. sniper24
Feel free to use any form of notation, the point is more to accept full mathematical formulas

Feel free to use any form of notation, the point is more to accept full mathematical formulas

1 vote
4. 
ThatFanficGuy
Yo, just a heads-up: "dice" is already a plural of "die". Python code, line 5.

Yo, just a heads-up: "dice" is already a plural of "die". Python code, line 5.

1 vote
1. mftrhu
Yeeeah, I always forget that. Thanks.

Yeeeah, I always forget that. Thanks.

2. Hvv
This one was really fun to implement and a good reminder that I know how to do a tiny subset of things in C++ very well and many, many things very badly. Does 2.5/3 bonuses (takes expressions in...

This one was really fun to implement and a good reminder that I know how to do a tiny subset of things in C++ very well and many, many things very badly.

Does 2.5/3 bonuses (takes expressions in polish notation)
C++

``````#include <bits/stdc++.h>
using namespace std;
typedef vector<int> vi;
typedef vector<vi> vvi;
int roll(int n) {
return ((rng()%n)+n)%n+1;
}
bool isOp(char c) {
return (c == '+') || (c=='-') || (c=='*') || (c=='/');
}
vvi gentree(vector<string>& vs) {
stack<int> st;
vvi g;
g.assign(vs.size(),vi());
if(isOp(vs)) {st.push(0);st.push(0);}
for(int i=1;i<vs.size();i++) {
if(st.empty()) {return vvi();}
int id = st.top();st.pop();
if(isOp(vs[i])) {
st.push(i);
st.push(i);
}
g[id].push_back(i);
}
if(!st.empty()) {return vvi();}
return g;
}
int parseDice(string& s) {
int val = 0;
int pos = -1;
bool dc = false;
for(int i=0;i<s.size();i++) {
if(s[i] == 'd') {
dc = true;
pos = i;
break;
}
val *= 10;
val += s[i]-'0';
}
if(dc) {
val = max(val,1);
int num = 0;
int tot = 0;
for(int i=pos+1;i<s.size();i++) {
num *= 10;
num += s[i]-'0';
}
for(int i=0;i<val;i++) {
int r = roll(num);
tot += r;
cout << "d" << num << ": " << r << '\n';
}
} else {
return val;
}
}
int eval(vvi& tr,vector<string>& vs, int u) {
if(tr[u].size() > 0) {
int lf = eval(tr,vs,tr[u]);
int rt = eval(tr,vs,tr[u]);
switch(vs[u]) {
case '+':
return lf+rt;
case '-':
return lf-rt;
case '*':
return lf*rt;
case '/':
return lf/rt;
default:
return -1;
}
} else {
return parseDice(vs[u]);
}
}
int main() {
while(1) {
string st;
vector<string> vs;
getline(cin,st);
stringstream ss(st);
string t;
while(getline(ss,t,' ')) {vs.push_back(t);}
if(vs.size() == 0) break;
vvi tr = gentree(vs);
if(tr.size() == 0) {
printf("Invalid dice expression\n");
continue;
}
int res = eval(tr,vs,0);
cout << "Result: " << res << '\n';
}
}
``````
3. Elronnd
In perl6, my first time really using grammars; any critique is appreciated. Supports any number of operations in any configuration (e.g. 5 + 6d6 - 80 * 3 / 5d4 is fine), but no order of operations...

In perl6, my first time really using grammars; any critique is appreciated. Supports any number of operations in any configuration (e.g. `5 + 6d6 - 80 * 3 / 5d4` is fine), but no order of operations or parentheses yet.

``````use MONKEY-SEE-NO-EVAL;

grammar Dice {
rule TOP { <atom> [ <op> <atom> ]* }
token op { '+' | '-' | '*' | '/' }
token atom { <number> | <dice>}
token dice { <number> d <number> }
token number { \d+ }
}

class Dicer {
method TOP(\$/) {
# if it's a number, return the number; if it's some dice, role them
sub process(\$a) { \$a<dice> ?? [+] [(\$a<dice><number> + 1).rand.Int for ^\$a<dice><number>] !! +\$a<number>; }
my @atoms = \$<atom>.map(&process);
my \$total = @atoms.shift;

for \$<op> -> \$op {
\$total = EVAL "\$total \$op {@atoms.shift}";
}

make \$total;
}
}

loop { say Dice.parse(prompt('> '), actions => Dicer).made; }
``````
4. 
telharmonium
I know this isn't in keeping with the spirit of a challenge, but like @onyxleopard, I'd like to share one of these I wrote several years ago, in Python 2.7. It was part of an RPG project which...

I know this isn't in keeping with the spirit of a challenge, but like @onyxleopard, I'd like to share one of these I wrote several years ago, in Python 2.7. It was part of an RPG project which never took off, but this feels like a good chance to dust it off and set it free, for whatever it's worth.

It satisfies the main criteria and the 3 bonuses, and can also handle exponentiation and implied multiplication with parentheses, like `2d4(3d2+9)^2`. It will even work with eldritch dice expressions in which the number of dice, the number of sides the dice have, and the exponent are themselves determined by dice rolls: `(2d4)d((3d20)^2d20)`.

Finally, it can output the result as a number, or it can return a dictionary listing the results of each individual roll, and how the dice expression was tokenized:

``````> python roll.py -v "2d20 + 1d6 + 3d10"
{'diceRolls': [{'rolls': [11, 1], 'sides': 20, 'sum': 12},
{'rolls': , 'sides': 6, 'sum': 5},
{'rolls': [2, 6, 6], 'sides': 10, 'sum': 14}],
'error': False,
'errorCode': False,
'origString': '2d20 + 1d6 + 3d10',
'result': 31,
'tokenized': [{'tokType': 'number', 'value': 2},
{'thisRoll': {'rolls': [11, 1], 'sides': 20, 'sum': 12},
'tokType': 'operator',
'value': 'd'},
{'rollResult': {'rolls': [11, 1], 'sides': 20, 'sum': 12},
'tokType': 'number',
'value': 20},
{'tokType': 'operator', 'value': '+'},
{'tokType': 'number', 'value': 1},
{'thisRoll': {'rolls': , 'sides': 6, 'sum': 5},
'tokType': 'operator',
'value': 'd'},
{'rollResult': {'rolls': , 'sides': 6, 'sum': 5},
'tokType': 'number',
'value': 6},
{'tokType': 'operator', 'value': '+'},
{'tokType': 'number', 'value': 3},
{'thisRoll': {'rolls': [2, 6, 6], 'sides': 10, 'sum': 14},
'tokType': 'operator',
'value': 'd'},
{'rollResult': {'rolls': [2, 6, 6], 'sides': 10, 'sum': 14},
'tokType': 'number',
'value': 10}]}
``````

The original idea was that this structure could be used by other libraries, or marshaled to JSON. The code theoretically uses a Top down operator precedence parser or Pratt parser, but I wasn't a CS major, so it probably shouldn't be used as an exemplar of the form. All the same, I hope this code might be useful to someone, or at least entertaining.

1. onyxleopard
It’s fascinating to me that the influence of tabletop games (which I personally haven’t even played much) have led to a de facto standard for dice rolling notation. I like this, and esp. the JSON...

It’s fascinating to me that the influence of tabletop games (which I personally haven’t even played much) have led to a de facto standard for dice rolling notation. I like this, and esp. the JSON schema you created, but it also seems a bit like overkill. I think tabular data would be preferable, as the only structured info that is required for the output is very amenable to simple columns. The detailed explanation of the parsing works better as JSON, but is probably only useful for debugging (not a bad motivation for implementing it, but I’ve been on a kick lately to try to force myself to think harder about how to do things as simply as possible).

5. 
Litmus2336
Extremely fun challenge! This is actually a lot like making a very simple compiler, which is always very fun. I will take a look at completing it tonight :)

Extremely fun challenge! This is actually a lot like making a very simple compiler, which is always very fun. I will take a look at completing it tonight :)

1. Litmus2336
Some SML for functional action. Might implement RPN in a bit: Note: Somehow I'm screwing up the random function so it is currently deterministic (*Given a list of strings, evaluate each string*)...

Some SML for functional action. Might implement RPN in a bit:

Note: Somehow I'm screwing up the random function so it is currently deterministic

``````(*Given a list of strings, evaluate each string*)
fun recurseProcessTokens (token::[]) =  (resolveToken(token)) :: []
| recurseProcessTokens (token::tail) = (resolveToken(token) :: recurseProcessTokens(tail))
| recurseProcessTokens ([]) = []

(*Evaluated dice rolls recursively*)
and evalDiceRoll(0,diceFaces) = 0
| evalDiceRoll(num,diceFaces) = (Random.randRange (1,diceFaces) (Random.rand(1,5)) + evalDiceRoll(num-1,diceFaces))

(*Resolves tokens, either with enum mapping for operands, converting to int for digits
or solving dice rolls*)
and resolveToken("+") = ~1 | resolveToken("-") = ~2 | resolveToken("*") = ~3 | resolveToken("/") = ~4
| resolveToken(token) : int=
if(List.all (Char.isDigit) (explode token))
then (valOf(Int.fromString token))
else (*Is dicer oll*)
let val tok = (String.tokens (fn #"d" => true | _ => false) token)
val num = (valOf(Int.fromString (hd tok)))
val diceFaces = (valOf(Int.fromString (hd (tl tok))))
val p = print((hd tok) ^ "d" ^ (hd (tl tok) ^ "=" ))
val rollVal = evalDiceRoll(num,diceFaces)
val p2 = print((Int.toString rollVal) ^ "\n")
in
rollVal
end
;
(*Recursively solve the list of evaluated tokens*)
fun evaluate ([]) = 0
| evaluate (last :: []) = last
| evaluate(arg1::(~1)::arg2 :: tail) = evaluate( (arg1 + arg2) :: tail)
| evaluate(arg1::(~2)::arg2 :: tail) = evaluate( (arg1 - arg2) :: tail)
| evaluate(arg1::(~3)::arg2 :: tail) = evaluate( (arg1 * arg2) :: tail)
| evaluate(arg1::(~4)::arg2 :: tail) = evaluate( (arg1 div arg2) :: tail)
| evaluate(arg1::el::arg2 :: tail) = ~1 (*Error*)
;
(*Main*)
let val input = "2d4 + 1d2 + 5"
val strArray = (String.tokens (fn #" " => true | _ => false) input)
val intArray = recurseProcessTokens(strArray)
in
evaluate(intArray)
end
``````
6. onyxleopard
(edited )
This is a Python3 dice rolling simulator (I wrote a few years ago). #!/usr/bin/env python3 """Roll N M-sided dice from the command line. N: number of dice to roll (implicitly N=1 if omitted) M:...

This is a Python3 dice rolling simulator (I wrote a few years ago).

``````#!/usr/bin/env python3
"""Roll N M-sided dice from the command line.

N: number of dice to roll (implicitly N=1 if omitted)
M: the maximum value on the die (must be at least 1)
e.g.:   'd6' -> roll one six-sided die
'3d20' -> roll three twenty-sided dice
'd2 d3 d4' -> roll a two-, three-, and four-sided die
"""

import random
import sys

from argparse import ArgumentError

def die(value):
"""Parse a die string of the form (N)dM int a tuple of integers where:
N: an optional number of dice (implicitly N=1 if omitted)
M: the number of sides on the die

E.g.:
die('d6') -> (1, 6)
die('1d6') -> (1, 6)
die('2d4') -> (2, 4)
"""
try:
n, m = value.lower().split('d', 1)
return int(n or 1), int(m)
except:
raise ArgumentError('invalid die string')

def roll(n, m):
"""Generate n random integers in the range [1..m]"""
for i in range(n):
yield random.randint(1, m)

if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description=__doc__
)
'dice',
nargs='+',
type=die,
metavar='[N]dM',
help='Roll N M-sided dice',
)
args = parser.parse_args()
for n, m in args.dice:
print('{}d{}\t{}'.format(n, m, sum(roll(n, m))))
``````
``````\$ ./rolldice -h
usage: rolldice [-h] [N]dM [[N]dM ...]

Roll N M-sided dice from the command line. N: number of dice to roll
(implicitly N=1 if omitted) M: the maximum value on the die (must be at least
1) e.g.: 'd6' -> roll one six-sided die '3d20' -> roll three twenty-sided dice
'd2 d3 d4' -> roll a two-, three-, and four-sided die

positional arguments:
[N]dM       Roll N M-sided dice

optional arguments:
-h, --help  show this help message and exit
\$ ./rolldice d6
1d6	5
\$ ./rolldice 3d6
3d6	11
\$ ./rolldice {1..6}d6
1d6	3
2d6	10
3d6	10
4d6	10
5d6	14
6d6	26
\$ ./rolldice {1..3}d{1..6}
1d1	1
1d2	2
1d3	2
1d4	1
1d5	3
1d6	4
2d1	2
2d2	4
2d3	4
2d4	5
2d5	6
2d6	12
3d1	3
3d2	6
3d3	5
3d4	9
3d5	6
3d6	13
# oh, and for modifiers
./rolldice {1..3}d{1..6} | awk '{print \$1 "     " \$2 +3}'
1d1	4
1d2	4
1d3	5
1d4	5
1d5	8
1d6	9
2d1	5
2d2	7
2d3	7
2d4	8
2d5	9
2d6	11
3d1	6
3d2	6
3d3	9
3d4	14
3d5	14
3d6	14
\$ ./rolldice {1..3}d{1..6} | awk '{print \$1 "     " \$2 -6}'
1d1	-5
1d2	-5
1d3	-4
1d4	-4
1d5	-3
1d6	-1
2d1	-4
2d2	-3
2d3	-1
2d4	-1
2d5	1
2d6	-1
3d1	-3
3d2	-1
3d3	0
3d4	-1
3d5	5
3d6	7
# oh and you need to sum everything? https://www.gnu.org/software/datamash/
\$ ./rolldice {1..3}d{1..6} | awk '{print \$1 "     " \$2 -6}' | datamash sum 2
-37
# oh you’re Mr. fancy-pants?
./rolldice {1..3}d{1..6} | awk '{print \$1 "     " \$2 -6}' | datamash --header-out min 2 max 2 mean 2 sstdev 2 sum 2 | csvlook -It
| min(field-2) | max(field-2) | mean(field-2) | sstdev(field-2) | sum(field-2) |
| ------------ | ------------ | ------------- | --------------- | ------------ |
| -5           | 4            | -1.5          | 2.8952293417235 | -27          |
``````
7. tomf
(edited )
I can't code.. but I'm alright with Sheets. I didn't know what 2d20 - 5 meant, but a friend said its 2x 20 sided dice minus 5 -- so I went with that. =QUERY( TRANSPOSE( ARRAYFORMULA( RANDBETWEEN(...

I can't code.. but I'm alright with Sheets. I didn't know what `2d20 - 5` meant, but a friend said its 2x 20 sided dice minus 5 -- so I went with that.

``````=QUERY(
TRANSPOSE(
ARRAYFORMULA(
RANDBETWEEN(
ARRAYFORMULA(ROW(A2:A3)-ROW(A1:A2)),20))),
"select Col1+0, Col2+0, Col1+Col2-5
label Col1+0 'Die 1', Col2+0 'Die 2', Col1+Col2-5 'Result'")
``````

edit: this is overkill

``````=QUERY({RANDBETWEEN(1,20),RANDBETWEEN(1,20)},
"select Col1+0, Col2+0, Col1+Col2-5
label Col1+0 'Die 1', Col2+0 'Die 2', Col1+Col2-5 'Result'")
``````
8. 
cge
(edited )
These solutions seem very nice and well-written, but they're all enormous: the smallest of the two Python versions is almost an entire kilobyte, without even implementing parentheses in the input....

These solutions seem very nice and well-written, but they're all enormous: the smallest of the two Python versions is almost an entire kilobyte, without even implementing parentheses in the input.

The problem here is trying to figure out the parsing. That seems like too much work for my style of approaching these challenges, so I'll just avoid doing it entirely:

``````import re
from random import randint as ri
i=int;z=[]
def f(x):z.append([ri(1,i(x)+1)for _ in range(0,i(max('1',x)))]);return str(sum(z[-1]))
print(eval(re.sub(r"(\d*)d(\d+)",f,input())),z)
``````

By avoiding every challenging part of this challenge, this code is only 196 bytes. It accepts as input any valid Python expression, where XdY can replace numbers, and will return first the final value, then all the rolls involved.

Thus:

• `d6+3 ==> 21 []`
• `4d6 * (5d4 - 3) ==> 312 [[5, 7, 7, 7], [2, 3, 5, 3, 2]]`
• `4d6**2d4 ==> 7776 [[2, 2, 1, 1], [4, 1]]`
• `4d8 > 15 ==> True [[7, 4, 9, 1]]`

Obviously, this allows arbitrary code execution. But in this case, that's arguably a feature. For example:

• `__import__("math").exp(-(5d8+3))`
• ` __import__("numpy").average([3d7,8d8])`
• `["Failure","Success"][4d8 > 15]`
• (Disclaimer: don't run this one. Adapted from these redditors.) `(__import__("time").sleep(4d8),[o.fork() for (o,i) in [(__import__('os'), __import__('itertools'))] for x in i.count()])`

There are two known issues:

• Zero should never be the leading digit in X for XdY (0d5 and 05d5 will act as 1d5).
• XdY handling is done as preprocessing. Thus, eg, `sum(4d8 for _ in range(0,1000))` will not roll 4000 d8s. This could be handled, but the challenge would be to keep track of each roll.
1. Elronnd
I did it in 66 characters in perl6: say EVAL S:g/(\d)d(\d)/{[+] [1+\$1.rand.Int for ^\$0]}/ given prompt

I did it in 66 characters in perl6:

``````say EVAL S:g/(\d)d(\d)/{[+] [1+\$1.rand.Int for ^\$0]}/ given prompt
``````
9. Diff
(edited )
Woo that was a trip. Never done anything like this before, so this a pretty horrific and fragile way to handle it. I started out with the idea of using a tree to represent the math-y witchcraft,...

Woo that was a trip. Never done anything like this before, so this a pretty horrific and fragile way to handle it.

I started out with the idea of using a tree to represent the math-y witchcraft, which seemed like a good idea at the time. Had a super hard time actually turning a string into a tree structure though.

First attempt worked pretty okay, but didn't implement any order of operations. So `2d5+5` was fine but `5 + 2d5` was run as `(5+2)d5`. Figured that out after I fed it `2d5000000 + 5d5`. It just kept spitting out dice rolls lol.

Second version creates an "unfinished" flat list of tokens then uses a new per-operator Priority to gradually fold it into a tree. Then once the tree is made you ask the root node for its value and the request cascades down the tree until a result appears.

Output:

``````\$ ./dice2 "2d8+2"
Rolling 2d8: ( 2 4 )

\$ ./dice2 "5+5d5+5*5+5d5"
Rolling 5d5: ( 1 1 5 1 2 )
Multiplying 5 and 5...Adding 15 and 25...
Rolling 5d5: ( 3 5 1 3 4 )
``````
``````package main

import (
"fmt"
"math/rand"
"os"
"time"
)

func main() {
rand.Seed(time.Now().UnixNano())

if len(os.Args) < 2 {
usage()
return
}

var input string
for i := 1; i < len(os.Args); i++ {
input += os.Args[i]
}

defer func() {
if r := recover(); r != nil {
fmt.Println(r)
usage()
}
}()

parser := Parser{}
value := parser.Parse(input).GetValue()
fmt.Printf("And your total is %v.\n", value)
}

var Operations = map[rune]func(int, int) int{
'd': DiceRollOp,
'-': SubtractionOp,
'*': MultiplicationOp,
'/': DivisionOp,
}

var Priorities = map[rune]int{
'd': 2,
'+': 0,
'-': 0,
'*': 1,
'/': 1,
}

func usage() {
fmt.Println("Usage: dice <expression>")
fmt.Println("Expressions are made up of alternating numbers and operators.")
fmt.Println()
fmt.Println("Supported operators are 'd', '+', and '-'.")
fmt.Println("XdY rolls a die with Y sides X times.")
fmt.Println("I'll leave it up to you to guess what + and - do.")
}

type Parser struct{}

func (p *Parser) Parse(input string) MathThing {
var tokens TokenString
var currentNum int
for _, char := range input {
if char >= '0' && char <= '9' {
currentNum *= 10
currentNum += int(char - '0')
continue
}

tokens = append(tokens, Number{currentNum})
currentNum = 0

switch char {
case ' ':
continue

case 'd', '+', '-', '*', '/':
tokens = append(tokens, Operation{Op: Operations[char], Priority: Priorities[char]})

default:
panic(fmt.Sprintf("Unknown character %c", char))
}
}

tokens = append(tokens, Number{currentNum})

for i := tokens.GetNext(); i >= 0; i = tokens.GetNext() {
left := tokens[i-1]
right := tokens[i+1]

if !left.IsComplete() || !right.IsComplete() {
panic("Syntax error. Somewhere. Good luck. If it helps, looks like there's too many operators and not enough numbers. Could be some weird order of operations crap idk.")
}

this := tokens[i].Complete(left, right)

tokens = append(tokens[:i], tokens[i+2:]...) // Consume 2 tokens.
tokens[i-1] = this                           // Replace the third token with our new one.
}

if len(tokens) != 1 {
panic("There's some left over bits. Or not enough left over bits. Good luck.")
}

}

type TokenString []MathThing

func (t TokenString) GetNext() int {
highestToken := -1
highestPriority := -1

for i, token := range t {
if !token.IsComplete() && token.GetPriority() > highestPriority {
highestToken = i
highestPriority = token.GetPriority()
}
}

return highestToken
}

// Is a thing that has a value.
type MathThing interface {
GetValue() int
IsComplete() bool
Complete(MathThing, MathThing) MathThing
GetPriority() int
}

// Number is just a constant value.
type Number struct {
Value int
}

func (n Number) GetValue() int {
return n.Value
}

func (n Number) IsComplete() bool {
return true
}

func (n Number) Complete(_, _ MathThing) MathThing {
return n
}

func (n Number) GetPriority() int {
return -1
}

// OpFunc is a function that does some math magic to 2 values.
type OpFunc func(int, int) int

// Operation has a value that depends on 2 other values and some unspecified black magic.
type Operation struct {
Left     MathThing
Right    MathThing
Op       OpFunc
Priority int
}

func (o Operation) GetValue() int {
return o.Op(o.Left.GetValue(), o.Right.GetValue())
}

func (o Operation) IsComplete() bool {
/*if o.Left == nil || o.Right == nil {
return false
}
return o.Left.IsComplete() && o.Right.IsComplete()*/
return o.Left != nil && o.Right != nil
}

func (o Operation) Complete(Left, Right MathThing) MathThing {
o.Left = Left
o.Right = Right

return o
}

func (o Operation) GetPriority() int {
return o.Priority
}

func DiceRollOp(diceCount, diceSize int) int {
fmt.Printf("Rolling %vd%v: (", diceCount, diceSize)
var total int
var roll int
for i := 0; i < diceCount; i++ {
roll = rand.Intn(diceSize) + 1
total += roll
fmt.Printf(" %v", roll)
}
fmt.Println(" )")
}

func AdditionOp(a, b int) int {
fmt.Printf("Adding %v and %v...\n", a, b)
return a + b
}

func SubtractionOp(a, b int) int {
fmt.Printf("Subtracting %v from %v...\n", b, a)
return a - b
}

func DivisionOp(a, b int) int {
fmt.Printf("Dividing %v by %v...\n", a, b)
return a / b
}

func MultiplicationOp(a, b int) int {
fmt.Printf("Multiplying %v and %v...\n", a, b)
return a * b
}
``````

I think it'd be really cool to try and print out the finished tree structure but I could not figure out how to pull that off.

10. gpl
I am a little late to this since I was travelling, but I wanted to share something a little different. I present the Quantum Dice Roller. This was written in Python 3.7 using IBM's qiskit...

I am a little late to this since I was travelling, but I wanted to share something a little different. I present the Quantum Dice Roller. This was written in Python 3.7 using IBM's qiskit platform. In order to generate randomness, this program places a single quantum bit (qubit) into a superposition of 0 and 1 with equal probability, and then measures this state in order to return 0 or 1 with perfectly equal probability. It uses these random 0s and 1s to build a random string of 0s and 1s which is interpreted as a binary number in order to get the desired random result. So, for example, if you are rolling a d4 it will find the number of bits needed to represent "4" and create a string of that length. The only catch here is that sometimes the result will be greater than the number of sides. For example, for a d4 the program will create a bit string of length 3, which in principle could be "111" which is greater than 4. Thus, it checks the final result and tries again until a valid result is obtained.

In principle, this program could be run on one of IBM's actual quantum computers. As it is written, it uses a simulated quantum processor (and thus probably some standard PRNG). However all it would take is changing the specified backend and making an IBM account to change this. I might do so later just for the novelty of it. I'm happy to answer questions if anyone has them

``````import math
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, execute, Aer

def parse(string):
params = [] #will hold relevant data
if "+" in string:
dice_info, modifier = string.split("+")
dice_info = dice_info.strip()
modifier = int(modifier.strip())
nrolls = int(dice_info.split("d"))
nsides = int(dice_info.split("d"))
params=[nrolls, nsides, "+", modifier]
elif "-" in string:
dice_info, modifier = string.split("-")
dice_info = dice_info.strip()
modifier = int(modifier.strip())
nrolls = int(dice_info.split("d"))
nsides = int(dice_info.split("d"))
params=[nrolls, nsides, "-", modifier]
else:
nrolls = int(string.split("d"))
nsides = int(string.split("d"))
params=[nrolls, nsides]

return params

def quantum_roll(nbits, nsides):

max_flag = True

while(max_flag):
my_backend = Aer.get_backend("qasm_simulator")
bit_string = ''
for i in range(nbits):
q = QuantumRegister(1)
c = ClassicalRegister(1)
qc = QuantumCircuit(q, c)
qc.h(q)
qc.measure(q, c)

job_sim = execute(qc, my_backend, shots=1)
sim_result = job_sim.result()
counts = sim_result.get_counts(qc)
bit = list(counts.keys())

bit_string += bit

result = int(bit_string, 2) + 1
if (result<=nsides):
max_flag=False

return result

def main():
user_in = input("Enter a dice roll in the form _d__ + _ (e.g. 3d4+2): ")

roll_params = parse(user_in)
nrolls=roll_params
nsides=roll_params

# is nrolls,  is nsides  is modifier if any

nbits = nsides.bit_length()

rolls = []
for roll in range(nrolls):
rolls.append(quantum_roll(nbits, nsides))

total = sum(rolls)

if len(roll_params) > 2:
if roll_params == "+":
total += roll_params
elif roll_params == "-":
total -= roll_params
else:
print("Invalid modifier, ignoring")

roll_str = ",".join([str(x) for x in rolls])
out = "You rolled the quantum dice {0} times, with results of {1}. This gave a total of {2}".format(nrolls, roll_str, total)

if len(roll_params) > 2:
out = out+", after a modifier of {} was applied.".format(roll_params+str(roll_params))
else:
out = out+"."

print(out)

if __name__=="__main__":
main()
``````
11. 
krg
Cool. Trying this in Rust. I'm not a super adept programmer, so I'm having a bit of trouble splitting the input string by regular expression. If I gather number of dice, number of sides, and...

Cool. Trying this in Rust. I'm not a super adept programmer, so I'm having a bit of trouble splitting the input string by regular expression. If I gather number of dice, number of sides, and modifiers separately it's a fairly trivial task, but of course that's not in the spirit of the challenge.

Also, does anyone know how to extract the last digit of a number? I'm trying to be clever and print proper ordinal numbers, but what I have right now only works for the first ten digits.

``````match (i + 1) {
1 => println!("{}st roll: {}", (i + 1), rolls[i]),
2 => println!("{}nd roll: {}", (i + 1), rolls[i]),
3 => println!("{}rd roll: {}", (i + 1), rolls[i]),
_ => println!("{}th roll: {}", (i + 1), rolls[i]),
}
``````

Also, there's a lot of repetition, so I imagine it can get even more clever.

1. 
mftrhu
What are you trying to do? Print the rolls as 1st, 2nd, 3rd, 40th? If so, use the remainder operator, but you might want to keep in mind the rules for forming ordinals in English: So you could...

Also, does anyone know how to extract the last digit of a number? I'm trying to be clever and print proper ordinal numbers, but what I have right now only works for the first ten digits.

What are you trying to do? Print the rolls as `1st, 2nd, 3rd, 40th`?

If so, use the remainder operator, but you might want to keep in mind the rules for forming ordinals in English:

• If the tens digit of a number is 1, then "th" is written after the number. For example: 13th, 19th, 112th, 9,311th.
• If the tens digit is not equal to 1, then the following table could be used:
 If the units digit is: 0 1 2 3 4-9 This is written after the number: th st nd rd th
For example: 2nd, 7th, 20th, 23rd, 52nd, 135th, 301st.

So you could write something like

``````let tens = (i + 1) % 100 / 10;
let units = (i + 1) % 10;
if (tens == 1) { return "th"; }
return match (units) {
1 => "st",
2 => "nd",
3 => "rd",
_ => "th"
}
``````
1. 
krg
Okay, so this also seems to work: match if (i + 1) < 20 {(i + 1)} else {(i + 1) % 10} { 1 => println!("{}st roll: {}", (i + 1), rolls[i]), 2 => println!("{}nd roll: {}", (i + 1), rolls[i]), 3 =>...

Okay, so this also seems to work:

``````	match if (i + 1) < 20 {(i + 1)} else {(i + 1) % 10} {
1 => println!("{}st roll: {}", (i + 1), rolls[i]),
2 => println!("{}nd roll: {}", (i + 1), rolls[i]),
3 => println!("{}rd roll: {}", (i + 1), rolls[i]),
11 ... 20 | _ => println!("{}th roll: {}", (i + 1), rolls[i]),
}
``````

but maybe it can be cleaner. I guess I can separate it into its own function.

1. 
mftrhu
match if (i + 1) < 20 {(i + 1)} else {(i + 1) % 10} { [...] 11 ... 20 | _ => println!("{}th roll: {}", (i + 1), rolls[i]), Oh, this is clever. You are matching the result of an if..else...
``````match if (i + 1) < 20 {(i + 1)} else {(i + 1) % 10} {
[...]
11 ... 20 | _ => println!("{}th roll: {}", (i + 1), rolls[i]),
``````

Oh, this is clever. You are matching the result of an if..else expression, yes? On both `i + 1 % 10` and `i + 1`, that is? I didn't know that Rust could do that.

Still, I would definitely move it to its own function, which would also avoid the whole "having to repeat `i + 1` seven times" thing.

1. 
krg
(edited )
Yea, I wasn't quite sure if it'd evaluate, either..until I tried it! This is the function I ended up with: fn ordinal_append(n: u32) -> String { match if (n % 100) < 20 { n % 100 } else { n % 10 }...

Yea, I wasn't quite sure if it'd evaluate, either..until I tried it!

This is the function I ended up with:

``````fn ordinal_append(n: u32) -> String {
match if (n % 100) < 20 { n % 100 } else { n % 10 } {
1 => "st",
2 => "nd",
3 => "rd",
11 ... 20 | _ => "th",
}.to_string()
}
``````

The only pain being that Rust is strongly typed and anything that gets thrown into the function has to be cast as a u32. I initiated the dice values as u8, since that seems to be enough for dice and maybe I save a few bytes of memory?

Edit: Ooo, also didn't consider anything past 100, so 111 shows as "111st" instead of "111th." Well, better fix that...

EditEdit: Okay, fixed. I think.

1. 
mftrhu
(edited )
Unless you are on a microcontroller or another such system where memory is measured in kBs, don't bother. Really. Hell, I'd go for u64 - you lose quite a bit by going down to u8 and, more...

I initiated the dice values as u8, since that seems to be enough for dice and maybe I save a few bytes of memory?

Unless you are on a microcontroller or another such system where memory is measured in kBs, don't bother. Really. Hell, I'd go for u64 - you lose quite a bit by going down to u8 and, more importantly, you are not really gaining anything. Unless you are dealing with an array of thousands of such elements, the 3 bytes you are gaining from that (and from other such premature optimizations) are not going to be significant.

I have been doing a few DMOJ problems each day for a while, and I picked Pascal back up because I wanted to tackle one such problem with a fairly restrictive memory limit (I hit the time limit instead). I have been looking at the best solutions leaderboards for a bit, and - Pascal often uses as little as 4 kB, while Python 3 is usually around 8.9 MB. Python 2, 5.5 MB or so. C, around one MB (890 kB+ even for things as trivial as Hello, World!). C++, ~1.5 MB.

Rust seems to use 2x to 1.1x more memory than C - 1.0 MB to 1.9 MB for Hello, World!, and any gains measured in bytes are going to be absolutely swamped by whatever it does setting up its environment.

Edit: apparently DMOJ doesn't let you link to a particular submission, you need to be logged in and to have solved that problem to see it. In any case, here's the best submissions for Hello, World!, just to get an idea of how much base memory your program will end up using in different programming languages.

1. 
krg
Haha, yea. Figured it didn't really matter on any modern system. For some reason it just feels more satisfying using as small a variable as possible. I also want to be exposed to type errors so I...

Haha, yea. Figured it didn't really matter on any modern system. For some reason it just feels more satisfying using as small a variable as possible. I also want to be exposed to type errors so I can figure out how to fix em. For example, `ordinal_append()` initially didn't have `.to_string()` attached, which gave me a type error.

DMOJ looks like a cool spot for problems. Gotta practice my Rust syntax some more...

1. mftrhu
Check also out Project Euler (heavy on mathematics) and Rosalind (bio-informatics, heavy on string manipulation) while you are at it.

DMOJ looks like a cool spot for problems. Gotta practice my Rust syntax some more...

Check also out Project Euler (heavy on mathematics) and Rosalind (bio-informatics, heavy on string manipulation) while you are at it.

2. krg
Ah, yea. Didn't consider that case. And I figured I'd use the remainder operator, but I must've forgot how it worked. Ha, that was going to be my go to solution but talked myself out of it.

Ah, yea. Didn't consider that case. And I figured I'd use the remainder operator, but I must've forgot how it worked. Ha, that was going to be my go to solution but talked myself out of it.

2. 
Litmus2336
Hm, why do you need the last digit of a number? What I recommend is to split around the "d", separating it into the before the d (number of dice) and after the d (faces on each die).

Hm, why do you need the last digit of a number? What I recommend is to split around the "d", separating it into the before the d (number of dice) and after the d (faces on each die).

1 vote
1. krg
Purely for nice looking ordinal printing. E.g., "1st roll, 2nd roll, 3rd roll" etc. My solution handles up to "10th", but adds "th" to each subsequent number. If I checked only the last digit I...

Purely for nice looking ordinal printing. E.g., "1st roll, 2nd roll, 3rd roll" etc. My solution handles up to "10th", but adds "th" to each subsequent number. If I checked only the last digit I believe I'd handle every case. Just an aesthetics thing, not really part of thtw challenge, but a nice little challenge in itself. :)

I looked up the documentation and some examples on Rust's regular expressions and I understand where I should split, but the syntax is a little confusing to me.

1 vote
12. 0d_billie
I'm very much not a programmer, just someone with an interest in coding. Here's my attempt in python, which probably breaks an awful lot of conventions and rules: import re,random,argparse parser...

I'm very much not a programmer, just someone with an interest in coding. Here's my attempt in python, which probably breaks an awful lot of conventions and rules:

``````import re,random,argparse

parser = argparse.ArgumentParser(description="Roll dice with or without additional operations")
parser.add_argument("to_roll", metavar="to_roll", type=str, help="A string in the format 4d6+1")
args = parser.parse_args()

to_roll = args.to_roll
input = re.match(r'(\d*)d(\d+)(([+-/\*])(\d+))?', to_roll)

if input:
if input.group(1):
dice = int(input.group(1))
else:
dice = 1
faces = int(input.group(2))
if input.group(3):
mod_op = input.group(4)
mod_num = int(input.group(5))
else:
print("Enter calculation in the correct format ""4d6+1""")

def dice_roll():
roll_result = []
for d in range(dice):
roll = random.randint(1,faces)
roll_result.append(roll)

roll_total = 0
for d in roll_result:
roll_total+=d

if input.group(3):
if mod_op == "+":
roll_total+=mod_num
if mod_op == "-":
roll_total+-mod_num
if mod_op == "/":
roll_total//=mod_num
if mod_op == "*":
roll_total*=mod_num

print("Rolled:",*roll_result)
print("Total:",roll_total)

dice_roll()
``````