Python , 452 байта в сжатой версии
Я не уверен, что это круто, безобразно или просто глупо, но было весело писать это.
Что мы делаем следующим образом: мы используем регулярные выражения (которые обычно не подходят для такого рода вещей), чтобы преобразовать строку обозначений кубиков в список команд на небольшом, основанном на стеке языке. Этот язык имеет четыре команды:
mul
умножает два верхних числа в стеке и выводит результат
add
добавляет два старших числа в стек и выводит результат
roll
извлекает размер кубика из стека, затем подсчитывает, бросает количество раз на стороне размера кости и выталкивает результат
- число просто помещается в стек
Затем этот список команд оценивается.
import re, random
def dice_eval(s):
s = s.replace(" ","")
s = re.sub(r"(\d+|[d+*])",r"\1 ",s) #seperate tokens by spaces
s = re.sub(r"(^|[+*] )d",r"\g<1>1 d",s) #e.g. change d 6 to 1 d 6
while "*" in s:
s = re.sub(r"([^+]+) \* ([^+]+)",r"\1 \2mul ",s,1)
while "+" in s:
s = re.sub(r"(.+) \+ (.+)",r"\1 \2add ",s,1)
s = re.sub(r"d (\d+) ",r"\1 roll ",s)
stack = []
for token in s.split():
if token == "mul":
stack.append(stack.pop() * stack.pop())
elif token == "add":
stack.append(stack.pop() + stack.pop())
elif token == "roll":
v = 0
dice = stack.pop()
for i in xrange(stack.pop()):
v += random.randint(1,dice)
stack.append(v)
elif token.isdigit():
stack.append(int(token))
else:
raise ValueError
assert len(stack) == 1
return stack.pop()
print dice_eval("2*d12+3d20*3+d6")
Кстати (это обсуждалось в комментариях к вопросу), эта реализация позволит использовать строки типа "2d3d6"
, понимая это как "дважды бросить d3, а затем бросить d6 столько раз, сколько получено в результате двух бросков. «
Кроме того, хотя есть некоторая проверка ошибок, она все еще ожидает допустимого ввода. Например, пропуск "* 4" приведет к бесконечному циклу.
Вот сжатая версия (не очень):
import re, random
r=re.sub
def e(s):
s=r(" ","",s)
s=r(r"(\d+|[d+*])",r"\1 ",s)
s=r(r"(^|[+*] )d",r"\g<1>1 d",s)
while"*"in s:s=r(r"([^+]+) \* ([^+]+)",r"\1 \2M ",s)
while"+"in s:s=r(r"(.+) \+ (.+)",r"\1 \2A ",s)
s=r(r"d (\d+)",r"\1 R",s)
t=[]
a=t.append
p=t.pop
for k in s.split():
if k=="M":a(p()*p())
elif k=="A":a(p()+p())
elif k=="R":
v=0
d=p()
for i in [[]]*p():v+=random.randint(1,d)
a(v)
else:a(int(k))
return p()