Rules
Write a function that accepts string as a parameter, returning evaluated value of expression in dice notation, including addition and multiplication.
To clear the things up, here comes EBNF definition of legal expressions:
roll ::= [positive integer], "d", positive integer
entity ::= roll | positive number
expression ::= entity { [, whitespace], "+"|"*"[, whitespace], entity }
Example inputs:
- "3d6 + 12"
- "4*d12 + 3"
- "d100"
Using eval functions, or similar, is not forbidden, but I encourage to solving without using these. Re-entrancy is welcome.
I cannot provide test-cases, as output should be random ;).
Format your answers' titles: language, n characters (important notes — no eval, etc.)
My ruby solution, 92 81 characters, using eval:
def f s
eval s.gsub(/(\d+)?d(\d+)/i){eval"a+=rand $2.to_i;"*a=($1||1).to_i}
end
Another ruby solution, not shorter (92 characters), but I find it interesting — it still uses eval but this time in quite creative way.
class Fixnum
def**b
eval"a+=rand b;"*a=self
end
end
def f s
eval s.gsub(/d/,'**')
end
Ruby, 87 characters, uses
eval
Here's my Ruby solution, partially based on the OP's. It's five characters shorter and only uses
eval
once.A readable version of the code:
J
With cobbal's help, squeeze everything into 93 characters.
Python 124 chars with eval, 154 without.
Just to show python doesn't have to be readable, here's a 124 character solution, with a similar eval-based approach to the original:
[Edit] And here's a 154 character one without eval:
Note: both will work for inputs like "2d6 + 1d3 + 5" but don't support more advanced variants like "2d3d6" or negative dice ("1d6-4" is OK, but "1d6-2d4" isn't) (You can shave off 2 characters to not support negative numbers at all in the second one instead)
python, 197 chars in obscured version.
Readable version: 369 chars. No eval, straight forward parsing.
compressed version: 258 chars, single character names, abused conditional expressions, shortcut in logical expression:
obscured version: 216 chars, using reduce, map heavily to avoid "def", "return".
Last version: 197 chars, folded in @Brain's comments, added testing runs.
Tests:
This solution can't handle whitespaces without adjacent digits. so "43d29d16d21*9+d7d9*91+2*d24*7" will work, but "43d29d16d21*9 + d7d9*91 + 2*d24*7" will not, due to the second space (between "+" and "d"). It can be corrected by first removing whitespaces from s, but this will make the code longer than 200 chars, so I'll keep the bug.
JavaScript solution, 340 chars when compressed (no eval, supports prefixed multiplicator and suffixed addition):
Test code:
Compressed version (pretty sure you can do shorter):
Python, 452 bytes in the compressed version
I'm not sure if this is cool, ugly, or plain stupid, but it was fun writing it.
What we do is as follows: We use regexes (which is usually not the right tool for this kind of thing) to convert the dice notation string into a list of commands in a small, stack-based language. This language has four commands:
mul
multiplies the top two numbers on the stack and pushes the resultadd
adds the top two numbers on the stack and pushes the resultroll
pops the dice size from the stack, then the count, rolls a size-sided dice count times and pushes the resultThis command list is then evaluated.
By the way (this was discussed in the question comments), this implementation will allow strings like
"2d3d6"
, understanding this as "roll a d3 twice, then roll a d6 as many times as the result of the two rolls."Also, although there is some error checking, it still expects a valid input. Passing "*4" for example will result in an infinite loop.
Here is the compressed version (not pretty):