# 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, 2014 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. Apr 7, 2014 at 9:13
• @PeterTaylor: I think the + sign should only be added if the modifier p is provided. Apr 7, 2014 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). Apr 7, 2014 at 15:48
• You're going to play dnd without the usecase of 2d8 + 1d6 + 4? You're gonna have a bad time Apr 7, 2014 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. Apr 7, 2014 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 Apr 7, 2014 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. Apr 7, 2014 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. Dec 7, 2015 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. Dec 7, 2015 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.

• The basic logic is cool, but this does not handle d 1 correctly (nor several other inputs), nor is the output random. Also no need for ; at the end. Examples Nov 7, 2022 at 16:36

## 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. Apr 7, 2014 at 22:45

## 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. Apr 7, 2014 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. Apr 7, 2014 at 17:55

# 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?

# 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? Apr 8, 2014 at 12:24
• @CeesTimmerman My bad. It does now. Apr 8, 2014 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.

# 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. # Python 3, 184 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. If zero dice were allowed, it would be 6 bytes shorter by leaving out (or q).

• I misunderstood the BNF, though. This page helps. Apr 8, 2014 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. Apr 8, 2014 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. Apr 8, 2014 at 11:03

# VyxalV, 89 bytes

h\d=[1p|]⁽±Ḋ→a⟨←aL5=|←a⌊±T⟨0|2|4⟩=|←a1i\d=|←a3i\+=⟩A[←a0i⌊ʁ(←a2i⌊℅⅛)¾∑←a4i⌊+,|«¢q⁰∷Ṗ-←¥«,


Try it Online!

Ties Perl

h\d=[1p|]         # Checks if the first character is a d, and prepends a 1
⁽±Ḋ→a     # Splits the string on non-numeric values, so consecutive digits stay grouped, then casts the list to variable "a"

⟨←aL5=|           # is it Length 5 (3 numbers, the "+" and the "d")
←a⌊±T⟨0|2|4⟩=|  # convert the numbers-as-strings to numbers and check if the numbers are in positions 1 3 and 5 (0 indexed)
←a1i\d=|     # is the second item in the list  "d"
←a3i\+=⟩    # is the fourth item "+"
A     # are all of these checks true?

[←a0i⌊ʁ               # If all the checks are true, create a range from 0-a[0]
(←a2i⌊℅⅛)       #  for i in said range, generate a random number 0-a[2] and push to global array
¾∑←a4i⌊+,  # push global array, sum the values, and add a[4]

|«¢q⁰∷Ṗ-←¥«, # If any of the checks are false, print "invalid input"
$$$$


# Zsh, 205 223 bytes

set -e;e=Invalid\ input;trap '<<<$e' ERR echo "^0d\n+$\nd+\n+d\n+.+\n[-|*]">p;[[ grep -cfp<<<$1>0 ]]&&<<<$e&&exit
q=(${(s: :)${1//(#s)d/1d}//[^0-9]/ });for i ({1..$q[1]})((s+=RANDOM%q[2]+1)) <<<$[s+q[3]]


Try it Online (with all test cases)

Writing the validation code was a chore. Without this guff, the solution would be a svelte 89 bytes:

q=(${(s: :)${1//(#s)d/1d}//[^0-9]/ });for i ({1..$q[1]})((s+=RANDOM%q[2]+1)) <<<$[s+q[3]]


# 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+. Apr 7, 2014 at 16:00 • Good point! #filler Apr 7, 2014 at 16:10 • And not 01d01+01. Apr 8, 2014 at 12:13 # Go, unseeded, 366 bytes import(."regexp";."strconv";."math/rand") func f(s string)int{k:=MustCompile(^(\d*)d(\d+)(\+\d+)?$).FindAllStringSubmatch(s,-1)
if len(k)<1||k[0][2]==""{return-1}
r:=k[0]
var n,l,f,m,t int
l=len(r)
n,_=Atoi(r[1])
if n<1&&r[1]==""{n=1}else if n<1{return-1}
if l>2{f,_=Atoi(r[2])}
if f<1{return-1}
if l>3{m,_=Atoi(r[3])}
for i:=0;i<n;i++{t+=Intn(f)+1}
t+=m
return t}


Attempt This Online!

# Go, seeded, 393 bytes

import(."regexp";."strconv";."math/rand";."time")
func f(s string)int{k:=MustCompile(^(\d*)d(\d+)(\+\d+)?\$).FindAllStringSubmatch(s,-1)
if len(k)<1||k[0][2]==""{return-1}
r:=k[0]
var n,l,f,m,t int
l=len(r)
n,_=Atoi(r[1])
if n<1&&r[1]==""{n=1}else if n<1{return-1}
if l>2{f,_=Atoi(r[2])}
if f<1{return-1}
if l>3{m,_=Atoi(r[3])}
Seed(Now().Unix())
for i:=0;i<n;i++{t+=Intn(f)+1}
t+=m
return t}


Attempt This Online!