def roll_one(self, dice, adv: int = 0): result = SingleDiceGroup() result.rolled = [] # splits dice and comments split = re.match(r'^([^\[\]]*?)\s*(\[.*\])?\s*$', dice) dice = split.group(1).strip() annotation = split.group(2) result.annotation = annotation if annotation is not None else '' # Recognizes dice obj = re.findall('\d+', dice) obj = [int(x) for x in obj] numArgs = len(obj) ops = [] if numArgs == 1: if not dice.startswith('d'): raise errors.InvalidArgument('Please pass in the value of the dice.') numDice = 1 diceVal = obj[0] if adv is not 0 and diceVal == 20: numDice = 2 ops = ['k', 'h1'] if adv is 1 else ['k', 'l1'] elif numArgs == 2: numDice = obj[0] diceVal = obj[-1] if adv is not 0 and diceVal == 20: ops = ['k', 'h' + str(numDice)] if adv is 1 else ['k', 'l' + str(numDice)] numDice = numDice * 2 else: # split into xdy and operators numDice = obj[0] diceVal = obj[1] dice = re.split('(\d+d\d+)', dice)[-1] ops = VALID_OPERATORS_2.split(dice) ops = [a for a in ops if a is not None] # dice repair/modification if numDice > 300 or diceVal < 1: raise errors.InvalidArgument('Too many dice rolled.') result.max_value = diceVal result.num_dice = numDice result.operators = ops for _ in range(numDice): try: tempdice = SingleDice() tempdice.value = random.randint(1, diceVal) tempdice.rolls = [tempdice.value] tempdice.max_value = diceVal tempdice.kept = True result.rolled.append(tempdice) except: result.rolled.append(SingleDice()) if ops is not None: rerollList = [] reroll_once = [] keep = None to_explode = [] to_reroll_add = [] valid_operators = VALID_OPERATORS_ARRAY last_operator = None for index, op in enumerate(ops): if last_operator is not None and op in valid_operators and not op == last_operator: result.reroll(reroll_once, 1) reroll_once = [] result.reroll(rerollList) rerollList = [] result.keep(keep) keep = None result.reroll(to_reroll_add, 1, keep_rerolled=True, unique=True) to_reroll_add = [] result.reroll(to_explode, greedy=True, keep_rerolled=True) to_explode = [] if op == 'rr': rerollList += parse_selectors([list_get(index + 1, 0, ops)], result, greedy=True) if op == 'k': keep = [] if keep is None else keep keep += parse_selectors([list_get(index + 1, 0, ops)], result) if op == 'ro': reroll_once += parse_selectors([list_get(index + 1, 0, ops)], result) if op == 'mi': _min = list_get(index + 1, 0, ops) for r in result.rolled: if r.value < int(_min): r.update(int(_min)) if op == 'ma': _max = list_get(index + 1, 0, ops) for r in result.rolled: if r.value > int(_max): r.update(int(_max)) if op == 'ra': to_reroll_add += parse_selectors([list_get(index + 1, 0, ops)], result) if op == 'e': to_explode += parse_selectors([list_get(index + 1, 0, ops)], result, greedy=True) if op in valid_operators: last_operator = op result.reroll(reroll_once, 1) result.reroll(rerollList) result.keep(keep) result.reroll(to_reroll_add, 1, keep_rerolled=True, unique=True) result.reroll(to_explode, greedy=True, keep_rerolled=True) return result
def roll(self, rollStr, adv: int = 0, rollFor='', inline=False, double=False, show_blurbs=True, **kwargs): try: if '**' in rollStr: raise errors.InvalidArgument("Exponents are currently disabled.") self.parts = [] # split roll string into XdYoptsSel [comment] or Op # set remainder to comment # parse each, returning a SingleDiceResult dice_set = re.split('([-+*/().=])', rollStr) dice_set = [d for d in dice_set if not d in (None, '')] log.debug("Found dice set: " + str(dice_set)) for index, dice in enumerate(dice_set): match = DICE_PATTERN.match(dice) log.debug("Found dice group: " + str(match.groups())) # check if it's dice if match.group(1): roll = self.roll_one(dice.replace(match.group(5), ''), adv) self.parts.append(roll) # or a constant elif match.group(2): self.parts.append(Constant(value=int(match.group(2)), annotation=match.group(4))) # or an operator elif not match.group(5): self.parts.append(Operator(op=match.group(3), annotation=match.group(4))) if match.group(5): self.parts.append(Comment(match.group(5) + ''.join(dice_set[index + 1:]))) break # calculate total crit = self.get_crit() try: total = self.get_total() except SyntaxError: raise errors.InvalidArgument("No dice found to roll.") rolled = ' '.join(str(res) for res in self.parts if not isinstance(res, Comment)) if rollFor is '': rollFor = ''.join(str(c) for c in self.parts if isinstance(c, Comment)) # return final solution if not inline: # Builds end result while showing rolls reply = ' '.join( str(res) for res in self.parts if not isinstance(res, Comment)) + '\n**Total:** ' + str( floor(total)) skeletonReply = reply rollFor = rollFor if rollFor is not '' else 'Result' reply = '**{}:** '.format(rollFor) + reply if show_blurbs: if adv == 1: reply += '\n**Rolled with Advantage**' elif adv == -1: reply += '\n**Rolled with Disadvantage**' if crit == 1: critStr = "\n_**Critical Hit!**_ " reply += critStr elif crit == 2: critStr = "\n_**Critical Fail!**_ " reply += critStr else: # Builds end result while showing rolls reply = ' '.join(str(res) for res in self.parts if not isinstance(res, Comment)) + ' = `' + str( floor(total)) + '`' skeletonReply = reply rollFor = rollFor if rollFor is not '' else 'Result' reply = '**{}:** '.format(rollFor) + reply if show_blurbs: if adv == 1: reply += '\n**Rolled with Advantage**' elif adv == -1: reply += '\n**Rolled with Disadvantage**' if crit == 1: critStr = "\n_**Critical Hit!**_ " reply += critStr elif crit == 2: critStr = "\n_**Critical Fail!**_ " reply += critStr reply = re.sub(' +', ' ', reply) skeletonReply = re.sub(' +', ' ', str(skeletonReply)) return DiceResult(result=int(floor(total)), verbose_result=reply, crit=crit, rolled=rolled, skeleton=skeletonReply, raw_dice=self) except Exception as ex: if not isinstance(ex, (SyntaxError, KeyError, errors.AvraeException)): log.error('Error in roll() caused by roll {}:'.format(rollStr)) traceback.print_exc() return DiceResult(verbose_result="Invalid input: {}".format(ex))
def roll_one(self, dice, adv: int = 0): # splits dice and annotation split = re.match(r'^([^\[\]]*?)\s*(\[.*\])?\s*$', dice) dice = split.group(1).strip() annotation = split.group(2) # Recognizes dice obj = re.findall('\d+', dice) obj = [int(x) for x in obj] numArgs = len(obj) # prepare dice and operators ops = [] if numArgs == 1: if not dice.startswith('d'): raise errors.InvalidArgument( 'Please pass in the value of the dice.') numDice = 1 dice_size = obj[0] if adv is not 0 and dice_size == 20: numDice = 2 ops = ['k', 'h1'] if adv is 1 else ['k', 'l1'] elif numArgs == 2: numDice = obj[0] dice_size = obj[-1] if adv is not 0 and dice_size == 20: ops = ['k', 'h' + str(numDice) ] if adv is 1 else ['k', 'l' + str(numDice)] numDice = numDice * 2 else: # split into xdy and operators numDice = obj[0] dice_size = obj[1] dice = re.split('(\d+d\d+)', dice)[-1] ops = VALID_OPERATORS_2.split(dice) ops = [a for a in ops if a is not None] # ensure limits if numDice > 300 or dice_size < 1: raise errors.InvalidArgument('Too many dice rolled.') # prepare output result = SingleDiceGroup(num_dice=numDice, max_value=dice_size, annotation=annotation or '', operators=ops) # roll dice for _ in range(numDice): try: tempdice = SingleDice(value=random.randint(1, dice_size), max_value=dice_size) result.rolled.append(tempdice) except: # ? result.rolled.append(SingleDice()) # define operators def _op_rr(buf): self._rerolls += result.reroll(buf, greedy=True) def _op_k(buf): result.keep(buf) def _op_ro(buf): self._rerolls += result.reroll(buf, once=True) def _op_ra(buf): result.reroll(buf, once=True, keep_rerolled=True, unique=True) def _op_e(buf): self._rerolls += result.reroll(buf, greedy=True, keep_rerolled=True) # run operators if ops is not None: buffer = list() operation = None last_operator = None for index, op in enumerate(ops): if self._rerolls > MAX_REROLLS: raise OverflowError("Tried to reroll too many dice.") if operation is not None and op in VALID_OPERATORS_ARRAY and not op == last_operator: operation(buffer) buffer = list() operation = None if op == 'rr': buffer += parse_selectors([list_get(index + 1, 0, ops)], result, greedy=True) operation = _op_rr elif op == 'k': buffer += parse_selectors([list_get(index + 1, 0, ops)], result) operation = _op_k elif op == 'p': buffer += parse_selectors([list_get(index + 1, 0, ops)], result, inverse=True) operation = _op_k elif op == 'ro': buffer += parse_selectors([list_get(index + 1, 0, ops)], result) operation = _op_ro elif op == 'mi': _min = list_get(index + 1, 0, ops) for r in result.rolled: if r.value < int(_min): r.update(int(_min)) elif op == 'ma': _max = list_get(index + 1, 0, ops) for r in result.rolled: if r.value > int(_max): r.update(int(_max)) elif op == 'ra': buffer += parse_selectors([list_get(index + 1, 0, ops)], result) operation = _op_ra elif op == 'e': buffer += parse_selectors([list_get(index + 1, 0, ops)], result, greedy=True) operation = _op_e if op in VALID_OPERATORS_ARRAY: last_operator = op if self._rerolls > MAX_REROLLS: raise OverflowError("Tried to reroll too many dice.") if operation is not None: operation(buffer) return result