# 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 10s than 4s.

### 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
• @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
• You're going to play dnd without the usecase of 2d8 + 1d6 + 4? You're gonna have a bad time – corsiKa Apr 7 '14 at 20:19

# 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. – Paul Prestidge 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|0,i=0;i<(+m||1);i++)s+=Math.random()*+m+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|0,i=0;i<(m||1);i++)s+=Math.random()*m+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)?:~šœ—Ý¶‘‰ž“–›ß–‘Š‹ÝÄ);  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)?:'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 is the input value.

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


Did you know, that decode is shorter than valueOf?

# Python 3, 184 bytes

import random,re
try:a,b,c=re.findall("^(\d*)d(\d+)(\+\d+)?$",input());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. If zero dice were allowed, it would be 6 bytes shorter by leaving out (or q). • 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|0,i=m||1;i--;)o+=m*Math.random()+1|0'):'Invalid input')

• This is so similar to Snack's answer – user12205 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. – Michael M. 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((*($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...

(*($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());I=int;R=sum(random.randrange(I(b))+1for x in*(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