|
|
@ -1,9 +1,9 @@
|
|
|
|
|
|
|
|
import math
|
|
|
|
import random
|
|
|
|
import random
|
|
|
|
|
|
|
|
|
|
|
|
import parsy
|
|
|
|
import parsy
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: functions
|
|
|
|
|
|
|
|
# TODO: nested groups
|
|
|
|
# TODO: nested groups
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -54,10 +54,13 @@ class Group(object):
|
|
|
|
traverse(node.right, subrolls)
|
|
|
|
traverse(node.right, subrolls)
|
|
|
|
except AttributeError:
|
|
|
|
except AttributeError:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
node.result
|
|
|
|
traverse(node.operand, subrolls)
|
|
|
|
subrolls.append(node)
|
|
|
|
|
|
|
|
except AttributeError:
|
|
|
|
except AttributeError:
|
|
|
|
return
|
|
|
|
try:
|
|
|
|
|
|
|
|
node.result
|
|
|
|
|
|
|
|
subrolls.append(node)
|
|
|
|
|
|
|
|
except AttributeError:
|
|
|
|
|
|
|
|
return
|
|
|
|
subrolls = []
|
|
|
|
subrolls = []
|
|
|
|
traverse(tree, subrolls)
|
|
|
|
traverse(tree, subrolls)
|
|
|
|
return subrolls
|
|
|
|
return subrolls
|
|
|
@ -66,13 +69,16 @@ class Group(object):
|
|
|
|
def exp(x):
|
|
|
|
def exp(x):
|
|
|
|
def copy(node):
|
|
|
|
def copy(node):
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
return Operation(node.op, node.func, copy(node.left), copy(node.right))
|
|
|
|
return Operation2(node.op, node.func, copy(node.left), copy(node.right))
|
|
|
|
except AttributeError:
|
|
|
|
except AttributeError:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
node.result
|
|
|
|
return Operation1(node.op, node.func, copy(node.operand))
|
|
|
|
return num(x)
|
|
|
|
|
|
|
|
except AttributeError:
|
|
|
|
except AttributeError:
|
|
|
|
return num(node)
|
|
|
|
try:
|
|
|
|
|
|
|
|
node.result
|
|
|
|
|
|
|
|
return num(x)
|
|
|
|
|
|
|
|
except AttributeError:
|
|
|
|
|
|
|
|
return num(node)
|
|
|
|
new_tree = copy(tree)
|
|
|
|
new_tree = copy(tree)
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
return new_tree.calc()
|
|
|
|
return new_tree.calc()
|
|
|
@ -81,7 +87,7 @@ class Group(object):
|
|
|
|
return exp
|
|
|
|
return exp
|
|
|
|
|
|
|
|
|
|
|
|
def _update_subrolls(self, subrolls):
|
|
|
|
def _update_subrolls(self, subrolls):
|
|
|
|
results = [(r, i, j, s) for i, s in enumerate(subrolls) for j, r in enumerate(s.result) if j in s.kept()]
|
|
|
|
results = [(r, i, j, s) for i, s in enumerate(subrolls) for j, r in enumerate(s.result) if j in s.kept(True)]
|
|
|
|
results = sorted(results, key=lambda x: (x[0], len(subrolls) - x[1], len(x[3].result) - x[2]))
|
|
|
|
results = sorted(results, key=lambda x: (x[0], len(subrolls) - x[1], len(x[3].result) - x[2]))
|
|
|
|
if self.keep:
|
|
|
|
if self.keep:
|
|
|
|
if self.keep[1]:
|
|
|
|
if self.keep[1]:
|
|
|
@ -153,7 +159,27 @@ class Group(object):
|
|
|
|
return sum(filtered)
|
|
|
|
return sum(filtered)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Operation(object):
|
|
|
|
class Operation1(object):
|
|
|
|
|
|
|
|
def __init__(self, op, func, operand):
|
|
|
|
|
|
|
|
self.op = op
|
|
|
|
|
|
|
|
self.func = func
|
|
|
|
|
|
|
|
self.operand = operand
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
|
|
|
return '<{0} {1}>'.format(self.op, repr(self.operand))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
|
|
|
return '{0}( {1} )'.format(self.op, str(self.operand))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def calc(self):
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
operand = self.operand.calc()
|
|
|
|
|
|
|
|
except AttributeError:
|
|
|
|
|
|
|
|
operand = num(self.operand)
|
|
|
|
|
|
|
|
return self.func(operand)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Operation2(object):
|
|
|
|
def __init__(self, op, func, left, right):
|
|
|
|
def __init__(self, op, func, left, right):
|
|
|
|
self.op = op
|
|
|
|
self.op = op
|
|
|
|
self.func = func
|
|
|
|
self.func = func
|
|
|
@ -211,8 +237,8 @@ class Roll(object):
|
|
|
|
result.append('~~*{0}*~~'.format(x))
|
|
|
|
result.append('~~*{0}*~~'.format(x))
|
|
|
|
return '**(** {0} **)**'.format(' + '.join(result))
|
|
|
|
return '**(** {0} **)**'.format(' + '.join(result))
|
|
|
|
|
|
|
|
|
|
|
|
def kept(self):
|
|
|
|
def kept(self, ignore_group=False):
|
|
|
|
if self.group_kept is not None:
|
|
|
|
if not ignore_group and self.group_kept is not None:
|
|
|
|
return self.group_kept
|
|
|
|
return self.group_kept
|
|
|
|
result = sorted(enumerate(self.result), key=lambda x: (x[1], len(self.result) - x[0]))
|
|
|
|
result = sorted(enumerate(self.result), key=lambda x: (x[1], len(self.result) - x[0]))
|
|
|
|
if self.keep:
|
|
|
|
if self.keep:
|
|
|
@ -225,7 +251,10 @@ class Roll(object):
|
|
|
|
result = result[:-round(self.drop[0])]
|
|
|
|
result = result[:-round(self.drop[0])]
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
result = result[round(self.drop[0]):]
|
|
|
|
result = result[round(self.drop[0]):]
|
|
|
|
return list(list(zip(*sorted(result)))[0])
|
|
|
|
try:
|
|
|
|
|
|
|
|
return list(list(zip(*sorted(result)))[0])
|
|
|
|
|
|
|
|
except IndexError:
|
|
|
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
|
|
def filtered(self):
|
|
|
|
def filtered(self):
|
|
|
|
kept = self.kept()
|
|
|
|
kept = self.kept()
|
|
|
@ -248,7 +277,8 @@ class Parser(object):
|
|
|
|
whitespace = parsy.regex(r'\s*')
|
|
|
|
whitespace = parsy.regex(r'\s*')
|
|
|
|
number = parsy.regex(r'(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?').map(float)
|
|
|
|
number = parsy.regex(r'(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?').map(float)
|
|
|
|
expression = (whitespace >> (
|
|
|
|
expression = (whitespace >> (
|
|
|
|
(parsy.regex(r'\*{2}|[()*/%+-]') | number | parsy.regex(r'\[.*?\]') |
|
|
|
|
(parsy.string('floor') | parsy.string('ceil') | parsy.string('round') | parsy.string('abs') |
|
|
|
|
|
|
|
|
parsy.regex(r'\*{2}|[()*/%+-]') | number | parsy.regex(r'\[.*?\]') |
|
|
|
|
parsy.regex(r'\!{2}|\!p|mt|ro|k[hl]|d[hl]|s[ad]|[{}dFfmkdrs!,<>=]')
|
|
|
|
parsy.regex(r'\!{2}|\!p|mt|ro|k[hl]|d[hl]|s[ad]|[{}dFfmkdrs!,<>=]')
|
|
|
|
) << whitespace)).many()
|
|
|
|
) << whitespace)).many()
|
|
|
|
return expression.parse(formula)
|
|
|
|
return expression.parse(formula)
|
|
|
@ -298,7 +328,7 @@ class Parser(object):
|
|
|
|
def group():
|
|
|
|
def group():
|
|
|
|
yield parsy.match_item('{')
|
|
|
|
yield parsy.match_item('{')
|
|
|
|
#result = yield group_simple
|
|
|
|
#result = yield group_simple
|
|
|
|
result = yield expression_additive
|
|
|
|
result = yield function | expression_additive
|
|
|
|
result = Group([result])
|
|
|
|
result = Group([result])
|
|
|
|
while True:
|
|
|
|
while True:
|
|
|
|
end = yield parsy.match_item('}') | parsy.success('')
|
|
|
|
end = yield parsy.match_item('}') | parsy.success('')
|
|
|
@ -306,10 +336,26 @@ class Parser(object):
|
|
|
|
break
|
|
|
|
break
|
|
|
|
yield parsy.match_item(',')
|
|
|
|
yield parsy.match_item(',')
|
|
|
|
#other = yield group_simple
|
|
|
|
#other = yield group_simple
|
|
|
|
other = yield expression_additive
|
|
|
|
other = yield function | expression_additive
|
|
|
|
result.items.append(other)
|
|
|
|
result.items.append(other)
|
|
|
|
return result
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@parsy.generate
|
|
|
|
|
|
|
|
def function():
|
|
|
|
|
|
|
|
func = yield parsy.test_item(lambda x: x in ['floor', 'ceil', 'round', 'abs'], 'floor|ceil|round|abs')
|
|
|
|
|
|
|
|
yield parsy.match_item('(')
|
|
|
|
|
|
|
|
operand = yield function | expression_additive
|
|
|
|
|
|
|
|
yield parsy.match_item(')')
|
|
|
|
|
|
|
|
if func == 'floor':
|
|
|
|
|
|
|
|
result = Operation1(func, lambda x: math.floor(x), operand)
|
|
|
|
|
|
|
|
elif func == 'ceil':
|
|
|
|
|
|
|
|
result = Operation1(func, lambda x: math.ceil(x), operand)
|
|
|
|
|
|
|
|
elif func == 'round':
|
|
|
|
|
|
|
|
result = Operation1(func, lambda x: round(x), operand)
|
|
|
|
|
|
|
|
elif func == 'abs':
|
|
|
|
|
|
|
|
result = Operation1(func, lambda x: abs(x), operand)
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
@parsy.generate
|
|
|
|
@parsy.generate
|
|
|
|
def expression_additive():
|
|
|
|
def expression_additive():
|
|
|
|
result = yield expression_multiplicative
|
|
|
|
result = yield expression_multiplicative
|
|
|
@ -320,9 +366,9 @@ class Parser(object):
|
|
|
|
break
|
|
|
|
break
|
|
|
|
operand = yield expression_multiplicative
|
|
|
|
operand = yield expression_multiplicative
|
|
|
|
if operation == '+':
|
|
|
|
if operation == '+':
|
|
|
|
result = Operation(operation, lambda x, y: x + y, result, operand)
|
|
|
|
result = Operation2(operation, lambda x, y: x + y, result, operand)
|
|
|
|
elif operation == '-':
|
|
|
|
elif operation == '-':
|
|
|
|
result = Operation(operation, lambda x, y: x - y, result, operand)
|
|
|
|
result = Operation2(operation, lambda x, y: x - y, result, operand)
|
|
|
|
return result
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
@parsy.generate
|
|
|
|
@parsy.generate
|
|
|
@ -335,11 +381,11 @@ class Parser(object):
|
|
|
|
break
|
|
|
|
break
|
|
|
|
operand = yield expression_exponential
|
|
|
|
operand = yield expression_exponential
|
|
|
|
if operation == '*':
|
|
|
|
if operation == '*':
|
|
|
|
result = Operation(operation, lambda x, y: x * y, result, operand)
|
|
|
|
result = Operation2(operation, lambda x, y: x * y, result, operand)
|
|
|
|
elif operation == '/':
|
|
|
|
elif operation == '/':
|
|
|
|
result = Operation(operation, lambda x, y: x / y, result, operand)
|
|
|
|
result = Operation2(operation, lambda x, y: x / y, result, operand)
|
|
|
|
elif operation == '%':
|
|
|
|
elif operation == '%':
|
|
|
|
result = Operation(operation, lambda x, y: x % y, result, operand)
|
|
|
|
result = Operation2(operation, lambda x, y: x % y, result, operand)
|
|
|
|
return result
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
@parsy.generate
|
|
|
|
@parsy.generate
|
|
|
@ -352,7 +398,7 @@ class Parser(object):
|
|
|
|
break
|
|
|
|
break
|
|
|
|
operand = yield expression_simple
|
|
|
|
operand = yield expression_simple
|
|
|
|
if operation == '**':
|
|
|
|
if operation == '**':
|
|
|
|
result = Operation(operation, lambda x, y: x ** y, result, operand)
|
|
|
|
result = Operation2(operation, lambda x, y: x ** y, result, operand)
|
|
|
|
return result
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
@parsy.generate
|
|
|
|
@parsy.generate
|
|
|
@ -551,8 +597,11 @@ class Parser(object):
|
|
|
|
count = yield computed_simple
|
|
|
|
count = yield computed_simple
|
|
|
|
yield parsy.match_item('d')
|
|
|
|
yield parsy.match_item('d')
|
|
|
|
sides = yield computed_simple | parsy.match_item('F')
|
|
|
|
sides = yield computed_simple | parsy.match_item('F')
|
|
|
|
dice = [-1, 0, 1] if sides == 'F' else [x + 1 for x in range(round(sides))]
|
|
|
|
dice = [-1, -1, 0, 0, 1, 1] if sides == 'F' else [x + 1 for x in range(round(sides))]
|
|
|
|
result = [random.choice(dice) for _ in range(round(count))]
|
|
|
|
try:
|
|
|
|
|
|
|
|
result = [random.choice(dice) for _ in range(round(count))]
|
|
|
|
|
|
|
|
except IndexError:
|
|
|
|
|
|
|
|
raise RuntimeError('Dice has to have at least one side!')
|
|
|
|
return result, dice
|
|
|
|
return result, dice
|
|
|
|
|
|
|
|
|
|
|
|
@parsy.generate
|
|
|
|
@parsy.generate
|
|
|
@ -607,8 +656,8 @@ class Parser(object):
|
|
|
|
return num(value if sign == '+' else -value)
|
|
|
|
return num(value if sign == '+' else -value)
|
|
|
|
|
|
|
|
|
|
|
|
computed_simple = (parsy.match_item('(') >> computed_additive << parsy.match_item(')')) | number
|
|
|
|
computed_simple = (parsy.match_item('(') >> computed_additive << parsy.match_item(')')) | number
|
|
|
|
expression_simple = roll | (parsy.match_item('(') >> expression_additive << parsy.match_item(')')) | number
|
|
|
|
expression_simple = roll | function | (parsy.match_item('(') >> expression_additive << parsy.match_item(')')) | number
|
|
|
|
group_simple = group_failures | group_successes | expression_additive
|
|
|
|
group_simple = group_failures | group_successes | function | expression_additive
|
|
|
|
return group_simple.parse(tokens)
|
|
|
|
return group_simple.parse(tokens)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|