# Roll Dungeons and Dragons dice

I want to play Dungeons and Dragons, but I don't have any dice! Your challenge is to roll some D&D dice.

The input format specification in Backus-Naur form is:

``````<valid-input>  ::= <opt-integer> "d" <integer> <opt-modifier>
<opt-integer>  ::= | <integer>
<opt-modifier> ::= | "+" <integer>
<integer>      ::= "0" | "1" | "2" | "3" | "4" | "5" |
"6" | "7" | "8" | "9" | <integer> <integer>
``````

The optional integer before the `d` is the number of dice to roll; it must be at least `1`, and defaults to `1` if not supplied.

The required integer immediately after the `d` is the number of sides each die has; it must be at least `1`. The sides of each die are distinct consecutive positive integers starting at `1`.

The optional modifier may be `+0`, and it defaults to `+0` if not specified.

For example, for the input `2d10+5`, you generate two random numbers from 1 to 10 inclusive, add them together, and add 5. Then you will output the result.

If you receive invalid input, such as `2d`, `d20+`, `0d4`, `2d5+1+2`, `2+2`, or anything else that doesn't fit this format, you must output "`Invalid input`". Otherwise, you must output only a single random integer, weighted according to the input. For example, `3d6` should produce more `10`s than `4`s.

### Test cases

``````Input      Minimum possible output    Maximum possible output
d1         1                          1
d6         1                          6
d1+3       4                          4
d20+3      4                          23
2d1        2                          2
2d6+2      4                          14
d01        1                          1
d01+0      1                          1
01d01+01   2                          2
3d20+10    13                         70

d          Invalid input
d0         Invalid input
d+0        Invalid input
d0+0       Invalid input
0d1        Invalid input
0d1+1      Invalid input
d1+        Invalid input
1d         Invalid input
1d1+       Invalid input
1d+1       Invalid input
2d+2d      Invalid input
d2+d2      Invalid input
d2+2+2     Invalid input
d2-1       Invalid input
-d2        Invalid input
-2d2       Invalid input
4*3        Invalid input
4*d2       Invalid input
``````

This is , so the shortest code in bytes will win!

-
Is `02d05+073` a valid input? – MT0 Apr 7 '14 at 7:23
The hard part about this question is validating the input, but the paragraph which describes the validation rules is self-contradictory. It describes `n` and `p` as optional, but input which choose not to include them (`d20+`) as invalid. – Peter Taylor Apr 7 '14 at 9:13
@PeterTaylor: I think the `+` sign should only be added if the modifier `p` is provided. – ProgramFOX Apr 7 '14 at 9:39
@MT0 Yes; `02` is a number >= 1. – Doorknob Apr 7 '14 at 12:11
@Doorknob, Well, because d13 and d17 aren't dice used in D&D. D&D uses d4, d6, d8, d10, d12, and d20. Also, there are certainly cases where a roll would include different types of dice (eg, `1d4+1d6` for a Rogue sneak attacking with a dagger) or having a negative `p` (eg, `1d20-1` for a skill check with no ranks/training and a negative ability modifier). – Brian S Apr 7 '14 at 15:48

# Perl, 109959396 89 bytes

``````s/^d/1d/;/^(\d+)d(\d+)(\+\d+)?\$/;\$d+=1+rand\$2|0for
1..\$1;\$_=\$1*\$2?\$d+\$3:'Invalid input'
``````

Requires the `-p` switch, which accounts for two of the bytes. Try it online on Ideone.

### How it works

• Because of the `-p` switch, a line is read from STDIN and stored in `\$_`.

• The command `s/^d/1d/` prepends a 1 to `\$_` if it begins with a d, i. e., if the number of dice has not been specified.

• The regular expression `/^(\d+)d(\d+)(\+\d+)?/` checks if the line consists of a number, a literal d, another number and, optionally, a third number preceded by a + sign.

If there is a match, the numbers will be saved in `\$1`, `\$2` and `\$3`.

In this case, the input will be valid if and only if `\$1` and `\$2` are both positive.

• `\$d += 1 + rand \$2 | 0` adds a pseudo-randomly chosen integer from 1 to the specified number of sides to `\$d` (initially treated as zero).

• `for 1 .. \$1` does the above once for every integer between 1 and the number of dice.

• The command `\$_ = \$1 * \$2 ? \$d + \$3 : 'Invalid input'` does the following:

• If `\$1 * \$2` is zero, it sets `\$_` to Invalid input.

• Otherwise, the input is valid and it sets `\$_` to the sum of the dice rolls and the modifier.

• Because of the `-p` switch, Perl prints the contents of `\$_`.

• Since there a no further input lines, the script exits.

-
I believe that, in general, extra command line parameters are considered to be worth a byte each but the hyphen is free. In this case, `-p` would only cost you one, making this a 108 byte solution. – undergroundmonorail Apr 7 '14 at 5:59
Can be made 96 chars, `/^([1-9]\d*)?d([1-9]\d*)(\+\d+)?\$/||die"Invalid input\$/";\$a+=1+int rand\$2for(1..\$1||1);\$_=\$a+\$3` – Hasturkun Apr 7 '14 at 9:02
@undergroundmonorail: I've seen people counting a single command-line switch as one, two and even three (counting the whitespace) bytes. I'd rather count it as one, but two bytes seems fair to me. – Dennis Apr 7 '14 at 13:41
@Vynce I assume you haven't either. I use `|0` to cast to int, since `rand` returns a pseudo-randomly chosen float. – Dennis Dec 7 '15 at 20:23
@Vynce I've added a permalink to the question (ideone.com/gLJfhO). `-e` would be problematic here, unless you replace the single quotes with double quotes. – Dennis Dec 7 '15 at 20:28

# Fortran: 145

``````character(1)a;read(*,*)s,a,j,a,k;n=0;if(k<0.or.a=="-")then;print*,"error k<0";stop;endif;do l=1,int(s);n=n+int(s*rand(0)+1);enddo;print*,n+k;end;
``````

Abuses implicit typing (`i-n` are all integers, everything else a real). Minor caveat: input must be space separated, so `2d10+5` must be entered as `2 d 10 + 5`, otherwise you'll get an `input conversion error`.

-

## Ruby, 116

Alternative Ruby version. I was trying to find a way to do it without the regular expressions, but the validation you have to do is a lot harder without them.

``````gets=~/^(\d+)?d(\d+)(\+\d+)?\$/
a=\$1||?1
puts\$~&&a>?0?eval("r=#{\$3||0};#{a}.times{r+=rand(#\$2)+1};r"):'Invalid input'
``````

This one is 112, using Dennis' clever Perl algorithm:

``````\$p='(\d*[1-9]\d*)'
puts~/^#\$p?d#\$p(\+\d+)?\$/?eval("r=#{\$3||0};#{\$1||1}.times{r+=rand(#\$2)+1};r"):'Invalid input'
``````
-
@m.buettner Thanks! I don't know why I thought it had to be >0. – Chron Apr 7 '14 at 22:45

# Javascipt, 158

``````m=prompt().match(/^([1-9]\d*)?d([1-9]\d*)(\+\d+)?\$/);if(!m)alert("Invalid input");else{for(s=+m[3]|0,i=0;i<(+m[1]||1);i++)s+=Math.random()*+m[2]+1|0;alert(s)}
``````

Can't golf better than this. It's time to get back to work.

-
`s="Invalid input";if(m=prompt().match(/^([1-9]\d*)?d([1-9]\d*)(\+\d+)?\$/))for(s=m[3]|0,i=0;‌​i<(m[1]||1);i++)s+=Math.random()*m[2]+1|0;alert(s)` has only 137 bytes. – Dennis Apr 7 '14 at 14:28
According to the comments on the question, this is an incorrect answer because it rejects the input `02d05+073`. – Peter Taylor Apr 7 '14 at 15:57

## GolfScript (120 106 bytes)

``````.100?!1`*\+.43?)!'+0'*+.10,'d+':^*-!*.10,''*-^=*^1/{/n*}/~].,3=*3,or:x~;*{x~\{rand)+}+@*}'Invalid input'if
``````

This is not only shorter than the first version, but also more elegant. The part which actually does the die rolling is

``````\{rand)+}+@*
``````

The rest is mainly input validation, and a few characters for parsing.

``````# Start by converting valid inputs into valid inputs with all optional bits.
# Prepend a '1' if the string starts with 'd'.
.100?!1`*\+
# Append '+0' if there's no '+' in the string.
.43?)!'+0'*+
# Now we start knocking out the invalid inputs.
# If it contains a character other than [0-9d+], replace the string with ''.
.10,'d+':^*-!*
# If it doesn't contain exactly one 'd', exactly one '+', and the 'd' before the '+',
# replace the string with ''.
.10,''*-^=*
# Now we either have a valid string, an empty string, or a string which is almost valid
# but has some empty substrings which should be integers, or a forbidden 0 integer value.
# Replace the 'd' and '+' with newlines, eval the result, and gather into an array.
^1/{/n*}/~]
# If we had any empty parts, we'll have fewer than 3 items on the stack.
# In that case, replace with integer values which will fail the final validation step.
.,3=*3,or
# Final validation: number of dice * number of sides per die != 0.
:x~;*
# If we pass, do the actual die rolling. Otherwise give the error message.
{x~\{rand)+}+@*}'Invalid input'if
``````

Online demo with test framework

-
I'm wondering why you don't use `n./`? Maybe also `10,n*` for one character less. – Howard Apr 7 '14 at 17:35
@Howard, to the first, because it was a last minute hack to pass some test cases and I didn't think about golfing it. To the second, that would make it accept some invalid input. – Peter Taylor Apr 7 '14 at 17:55

# J - 130 (45?) char

This challenge seems to be a little biased towards regular expressions, especially with having to differentiate invalid input. J has a POSIX regex library, so it's not that bad, but it's not integrated as it is with Perl, so J fares no better than other languages.

``````+/@,`(1+?@#~)/`('Invalid input'"_)@.(0 e.\$)0 1 1>.".>|.}.((,'?d','(\+[0-9]+)?\$',~}.)'^([0-9]*[1-9][0-9]*)')(rxmatch rxfrom])1!:1]1
``````

If you're just implementing the logic for valid expressions, like the Python/PHP solutions appear to, it's the more reasonable 45 chars:

``````+/,(1+[:?@#/1>.".;._2@,&'d')`".;._1'+',1!:1]1
``````

Notable bits:

• `1!:1]1` is the input, and `(rxmatch rxfrom])` is the logic that returns the subexpression matches.

• Whether or not the input was legal is handled by the regex matching, so we can set the defaults for n and p with `0 1 1>.`. It looks backwards (n is 1 by default and p is 0) because we had to reverse (`|.`) the list earlier, so that the logic at the end executes in the right order.

• `@.` is the Agenda conjunction, essentially a J-ish switch statement. If the matches are empty (if 0 is an e.lement of the \$hape: `0 e.\$`), we emit the error message, else we go through with rolling the dice: `#~` to set out the dice, `1+?` to roll, and `+/@,` to add the modifier p and sum.

-
Does that work for `01d01+01`? – Cees Timmerman Apr 8 '14 at 12:24
@CeesTimmerman My bad. It does now. – algorithmshark Apr 8 '14 at 18:40

## TinyMUSH, 239

``````@dig/t +
@op d=+
@lo d=d
@fail d=Invalid input
@cr .
@set .=com
&d .=\$*:\ifelse(regmatch(%0,^(\\\\d+)?d(\\\\d+)(\\\\+\\\\d+)?\$,0 1 2 3),ifzero(and(or(not(strlen(%q1)),%q1),%q2),Invalid input,add(die(usetrue(%q1,1),%q2),%q3)),Invalid input)
``````

The first four lines deal with the fact that "d" is an alias for the universal "down" exit with a built-in failure message when it doesn't exist; exits are scanned before user-defined commands. The remaining lines create an object with a user-defined command making use of the built-in die() function.

-

# PHP, 129

``````<?eval(preg_filter(~Ð¡×£›ÔÖÀ›×£›Ö×£Ô£›ÔÖÀÛÐ,~ÛžÂÝÛÎÝÀÅÎÄ™×ÄÛ–ÔÔÃÛžÄÛŒÔÂž‘›×ÎÓÛÍÖÖÄšœ—ÛŒÛÌÄ,\$_GET[0])?:~šœ—Ý¶‘‰ž“–›ß–‘Š‹ÝÄ);
``````

Uses a regex to create a expression which PHP then evaluates. Input is fed in via url: ?0=argument. Make sure you urlencode the + to %2b. Here's what it looks like in a more readable form:

``````eval(preg_filter('/^(\\d)?d(\\d)(\\+\\d)?\$/','\$a="\$1"?:1;for(;\$i++<\$a;\$s+=rand(1,\$2));echo\$s\$3;',\$_GET[0])?:'echo"Invalid input";');
``````

Bitwise inverting the strings using `~` not only saves a character because you don't need quotes (PHP assumes they are strings) but also saves characters because you don't have to escape the backslashes in the regular expression.

The `?:` operator is a special form of the ternary operator. `\$foo = \$a ? \$a : \$b` is the same as `\$foo = \$a ?: \$b`.

-

# Java, 378

Just wanted to try a solution with Java far from the best solution. But hey: Java isn't a golfing language in any case!

It gets the input from command line. First parameter `args[0]` is the input value.

``````class A{public static void main(String[]s){System.out.print(s[0].matches(
"(0+\\d+|[1-9]\\d*|)d(0+\\d+|[1-9]\\d*)(\\+\\d+)?")?z(s[0]):"Invalid input");}static int
z(String s){String[]a=s.split("d");String[]b=a[1].split("\\+");int c=a[0].isEmpty()?1:Byte.
decode(a[0]);int d=b.length<2?0:Byte.decode(b[1]);while(c-->0)d+=new java.util.Random().
nextInt(Byte.decode(b[0]))+1;return d;}}
``````

Did you know, that `decode` is shorter than `valueOf`?

-

# Python 3, 185 bytes

``````import random,re
try:a,b,c=re.findall("^(\d*)d(\d+)(\+\d+)?\$",input())[0];t=int(c or 0)+(sum(random.randint(1,int(b))for i in range(int(a or 1))) or q)
except:t="Invalid input"
print(t)
``````

Passes all tests.

-
I misunderstood the BNF, though. This page helps. – Cees Timmerman Apr 8 '14 at 10:37
For the benefit of anyone else who wonders why the regex is anchored at one end but not the other: Python's `re.match` implicitly anchors at the start but not at the end. I'm not aware of any other regex library which does that. – Peter Taylor Apr 8 '14 at 10:56
There's a small saving by initialising `t=int(c or 0)`; and it might be possible to combine your answer with the existing Python one (which uses less whitespace) to save a couple more. – Peter Taylor Apr 8 '14 at 11:03

## JavaScript 134

``````m=prompt().match(/^((?!0)\d*)d((?!0)\d+)(\+\d+)?\$/);alert(m?eval('for(o=m[3]|0,i=m[1]||1;i--;)o+=m[2]*Math.random()+1|0'):'Invalid input')
``````
-
This is so similar to Snack's answer – ace Apr 7 '14 at 10:09
Well there are similarities, this is the same language/algorithm... But I thought there is enough differences in my code (and the regex) to post a different answer. – Mig Apr 7 '14 at 11:33
According to the comments on the question, this is an incorrect answer because it rejects the input `02d05+073`. – Peter Taylor Apr 7 '14 at 15:58

# Ruby, 167 147

``````/^(\d+)?d(\d+)(\+\d+)?\$/.match gets
abort'Invalid input'if !\$~||\$1==?0||!\$2||\$2==?0
p eval(([0]*(\$1||1).to_i).map{rand(\$2.to_i)+1}*?+)+(\$3||0).to_i
``````

Uses a regexp to do all the work. Since I'm using `\d+`, the only things I need to check for invalid input are that there was a match, that neither `n` nor `m` were `0`, and that there was an `m`. If any of those are found, it aborts with a message (`'Invalid input'`). Then it just prints the result, since it would have aborted by now if the input was invalid.

The result-printing isn't that interesting, but...

``````([0]*(\$1||1).to_i)    # create an array of n elements (1 if there is no n)
.map{rand(\$2.to_i)+1} # fill it up with random numbers, where the number x is 1 < x < m+1
.inject(:+)           # add them all up
+(\$3||0).to_i         # and finally add the modifier (0 if there is none)
``````

I later changed `.inject(:+)` to `eval(...*?+)`, but the idea is the same.

-

# Python3, 204B

Mine beats the existing Python answer by adding in the required error handling and reading `d20` as `1d20` rather than `0d20` :)

``````import random,re
try:a,b,c=re.findall('^([1-9]\d*)?d(\d+)(\+\d+)?\$',input())[0];I=int;R=sum(random.randrange(I(b))+1for x in[0]*(1if a==''else I(a)))+(0if c==''else I(c))
except:R='Invalid input'
print(R)
``````

edited to fix 2 typos: `I(x) => I(c)`, `Invalid Input => Invalid input`

edited to fix regex: `\+?(\d*) => (\+\d+)?`

-
According to the clarified question, this is an incorrect answer because it accepts the input `3d20+`. – Peter Taylor Apr 7 '14 at 16:00
Good point! #filler – alexander-brett Apr 7 '14 at 16:10
And not `01d01+01`. – Cees Timmerman Apr 8 '14 at 12:13