Я пишу пользовательский синтаксический анализатор прокрутки игры в кости (хихиканье, если Вы должны) в Python. В основном я хочу использовать стандартную математическую оценку, но добавить 'd' оператор:
#xdy
sum = 0
for each in range(x):
sum += randInt(1, y)
return sum
Так, чтобы, например, 1d6+2d6+2d6-72+4d100 = (5) + (1+1) + (6+2)-72 + (5+39+38+59) = 84
Я использовал regex для замены всего 'd's суммой и затем использовал оценку, но мой regex развалился при контакте с круглыми скобками с обеих сторон. Существует ли более быстрый способ пойти об этом, чем реализация моего собственного рекурсивного парсинга? Возможно, добавляя оператор к оценке?
Править: Я, кажется, дал плохой пример, поскольку вышеупомянутый пример работает с моей текущей версией. То, что я ищу, является некоторым способом оценить, скажем, (5 + (6d6)) d (7-2* (1d4)).
"Развалился", я просто подразумевал, что мое текущее regex выражение перестало работать. Я был слишком неопределенен о своем отказе, извините для беспорядка. Вот мой текущий код:
def evalDice(roll_matchgroup):
roll_split = roll_matchgroup.group('roll').split('d')
print roll_split
roll_list = []
for die in range(int(roll_split[0])):
roll = random.randint(1,int(roll_split[1]))
roll_list.append(roll)
def EvalRoll(roll):
if not roll: return 0
rollPattern = re.compile('(?P<roll>\d*d\d+)')
roll_string = rollPattern.sub(evalDice, roll.lower())
для этого, "1d6+4d100" работает просто великолепно, но" (1d6+4) d100" или даже "1d6+4d (100)" сбои.
Python не позволяет вам писать новые операторы, и вы не можете заключать скобки с обычный язык. Вам нужно будет написать парсер рекурсивного спуска. Это должно быть довольно просто для вашего языка игры в кости.
В качестве альтернативы вы можете использовать существующий оператор Python и использовать инструменты синтаксического анализа Pythons для преобразования текста в AST.
Взгляните на библиотеку PyParsing . В частности, на странице examples есть пример , довольно близкий к тому, что вы хотите:
Анализатор и вычислитель броска кости для оценки строк, таких как «4d20 + 5.5 + 4d6.takeHighest (3) ".
Вы можете использовать функцию обратного вызова с re.sub
. Когда вы переходите по ссылке, ищите до абзаца, начинающегося с «Если repl - это функция ...»
import re
import random
def xdy(matchobj):
x,y=map(int,matchobj.groups())
s = 0
for each in range(x):
s += random.randint(1, y)
return str(s)
s='1d6+2d6+2d6-72+4d100'
t=re.sub('(\d+)d(\d+)',xdy,s)
print(t)
# 5+10+8-72+197
print(eval(t))
# 148
Здесь используется eval, что на самом деле довольно ужасно, но вот и все.
>>> x = '1d6+2d6+2d6-72+4d100'
>>> eval(re.sub(r'(\d+)d(\d+)',r'sum((random.randint(1,x) for x in \1 * [\2]))', x))
Небольшие заметки:
Это заменяет, скажем, 4d6
на sum ((random.randint (1, x) for x in 4 * [6]))
.
4 * [6]
дает список [6,6,6,6]
.
((random.randint (1, x) for x in [6,6,6,6]))
- генератор, эквивалентный пониманию списка; этот конкретный вернет четыре случайных числа от 1 до 6.
В моем плагине Supybot dice я анализирую выражение с помощью
r'(?P<sign>[+-])((?P<dice>\d*)d(?P<sides>\d+)|(?P<mod>\d+))'
, затем получаю общее количество каждой кости и общий модификатор, бросаю их и получаю общий результат (я хотел показать общее количество каждой кости ).