diff --git a/ircbot/modules/Dice.py b/ircbot/modules/Dice.py deleted file mode 100644 index 987b575..0000000 --- a/ircbot/modules/Dice.py +++ /dev/null @@ -1,400 +0,0 @@ -""" -Dice - roll dice when asked, intended for RPGs -Copyright (C) 2010 Brian S. Stephan - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -import math -import re -import random - -import ply.lex as lex -import ply.yacc as yacc - -from Module import Module - -class Dice(Module): - - """Roll simple or complex dice strings.""" - - tokens = ['NUMBER', 'TEXT', 'ROLLSEP'] - literals = ['#', '/', '+', '-', 'd'] - - def __init__(self, irc, config): - """Set up XML-RPC methods.""" - - Module.__init__(self, irc, config) - - irc.xmlrpc_register_function(self.do_roll, "dice_roll") - - def build(self): - lex.lex(module=self) - yacc.yacc(module=self) - - t_TEXT = r'\s+[^;]+' - t_ROLLSEP = r';\s*' - - def t_NUMBER(self, t): - r'\d+' - t.value = int(t.value) - return t - - def t_error(self, t): - t.lexer.skip(1) - - precedence = ( - ('left', 'ROLLSEP'), - ('left', '+', '-'), - ('right', 'd'), - ('left', '#'), - ('left', '/') - ) - - output = "" - - def roll_dice(self, keep, dice, size): - """Takes the parsed dice string for a single roll (eg 3/4d20) and performs - the actual roll. Returns a string representing the result. - """ - - a = range(dice) - for i in range(dice): - a[i] = random.randint(1, size) - if keep != dice: - b = sorted(a, reverse=True) - b = b[0:keep] - else: - b = a - total = sum(b) - outstr = "[" + ",".join(str(i) for i in a) + "]" - - return (total, outstr) - - def process_roll(self, trials, mods, comment): - """Processes rolls coming from the parser. - - This generates the inputs for the roll_dice() command, and returns - the full string representing the whole current dice string (the part - up to a semicolon or end of line). - """ - - output = "" - repeat = 1 - if trials != None: - repeat = trials - for i in range(repeat): - mode = 1 - total = 0 - curr_str = "" - if i > 0: - output += ", " - for m in mods: - keep = 0 - dice = 1 - res = 0 - # if m is a tuple, then it is a die roll - # m[0] = (keep, num dice) - # m[1] = num faces on the die - if type(m) == tuple: - if m[0] != None: - if m[0][0] != None: - keep = m[0][0] - dice = m[0][1] - size = m[1] - if keep > dice or keep == 0: - keep = dice - if size < 1: - output = "# of sides for die is incorrect: %d" % size - return output - if dice < 1: - output = "# of dice is incorrect: %d" % dice - return output - res = self.roll_dice(keep, dice, size) - curr_str += "%d%s" % (res[0], res[1]) - res = res[0] - elif m == "+": - mode = 1 - curr_str += "+" - elif m == "-": - mode = -1 - curr_str += "-" - else: - res = m - curr_str += str(m) - total += mode * res - if repeat == 1: - if comment != None: - output = "%d %s (%s)" % (total, comment.strip(), curr_str) - else: - output = "%d (%s)" % (total, curr_str) - else: - output += "%d (%s)" % (total, curr_str) - if i == repeat - 1: - if comment != None: - output += " (%s)" % (comment.strip()) - return output - - def p_roll_r(self, p): - # Chain rolls together. - - # General idea I had when creating this grammar: A roll string is a chain - # of modifiers, which may be repeated for a certain number of trials. It can - # have a comment that describes the roll - # Multiple roll strings can be chained with semicolon - - 'roll : roll ROLLSEP roll' - global output - p[0] = p[1] + "; " + p[3] - output = p[0] - - def p_roll(self, p): - # Parse a basic roll string. - - 'roll : trial modifier comment' - global output - mods = [] - if type(p[2]) == list: - mods = p[2] - else: - mods = [p[2]] - p[0] = self.process_roll(p[1], mods, p[3]) - output = p[0] - - def p_roll_no_trials(self, p): - # Parse a roll string without trials. - - 'roll : modifier comment' - global output - mods = [] - if type(p[1]) == list: - mods = p[1] - else: - mods = [p[1]] - p[0] = self.process_roll(None, mods, p[2]) - output = p[0] - - def p_comment(self, p): - # Parse a comment. - - '''comment : TEXT - |''' - if len(p) == 2: - p[0] = p[1] - else: - p[0] = None - - def p_modifier(self, p): - # Parse a modifier on a roll string. - - '''modifier : modifier "+" modifier - | modifier "-" modifier''' - # Use append to prevent nested lists (makes dealing with this easier) - if type(p[1]) == list: - p[1].append(p[2]) - p[1].append(p[3]) - p[0] = p[1] - elif type(p[3]) == list: - p[3].insert(0, p[2]) - p[3].insert(0, p[1]) - p[0] = p[3] - else: - p[0] = [p[1], p[2], p[3]] - - def p_die(self, p): - # Return the left side before the "d", and the number of faces. - - 'modifier : left NUMBER' - p[0] = (p[1], p[2]) - - def p_die_num(self, p): - 'modifier : NUMBER' - p[0] = p[1] - - def p_left(self, p): - # Parse the number of dice we are rolling, and how many we are keeping. - 'left : keep dice' - if p[1] == None: - p[0] = [None, p[2]] - else: - p[0] = [p[1], p[2]] - - def p_left_all(self, p): - 'left : dice' - p[0] = [None, p[1]] - - def p_left_e(self, p): - 'left :' - p[0] = None - - def p_total(self, p): - 'trial : NUMBER "#"' - if len(p) > 1: - p[0] = p[1] - else: - p[0] = None - - def p_keep(self, p): - 'keep : NUMBER "/"' - if p[1] != None: - p[0] = p[1] - else: - p[0] = None - - def p_dice(self, p): - 'dice : NUMBER "d"' - p[0] = p[1] - - def p_dice_one(self, p): - 'dice : "d"' - p[0] = 1 - - def p_error(self, p): - # Provide the user with something (albeit not much) when the roll can't be parsed. - global output - output = "Unable to parse roll" - - def get_result(self): - global output - return output - - def do_roll(self, dicestr): - """ - Roll some dice and get the result (with broken out rolls). - - Keyword arguments: - dicestr - format: - N#X/YdS+M label - N#: do the following roll N times (optional) - X/: take the top X rolls of the Y times rolled (optional) - Y : roll the die specified Y times (optional, defaults to 1) - dS: roll a S-sided die - +M: add M to the result (-M for subtraction) (optional) - """ - self.build() - yacc.parse(dicestr) - return self.get_result() - - def do(self, connection, event, nick, userhost, what, admin_unlocked): - match = re.search('^!roll\s+(.*)$', what) - if match: - dicestr = match.group(1) - reply = nick + ": " + self.do_roll(dicestr) - # irc format it - return self.irc.reply(event, - re.sub(r"(\d+)(.*?\s+)(\(.*?\))", r"\1\214\3", reply)) - - match = re.search('^!ctech\s+(.*)$', what) - if match: - rollitrs = re.split(';\s*', match.group(1)) - reply = "" - for count, roll in enumerate(rollitrs): - pattern = '^(\d+)d(?:(\+|\-)(\d+))?(?:\s+(.*))?' - regex = re.compile(pattern) - matches = regex.search(roll) - if matches is not None: - dice = int(matches.group(1)) - modifier = 0 - - if matches.group(2) is not None and matches.group(3) is not None: - if str(matches.group(2)) == '-': - modifier = -1 * int(matches.group(3)) - else: - modifier = int(matches.group(3)) - - result = roll + ': ' - - rolls = [] - for d in range(dice): - rolls.append(random.randint(1, 10)) - rolls.sort() - rolls.reverse() - - # highest single die method - method1 = rolls[0] - - # highest set method - method2 = 0 - rolling_sum = 0 - for i, r in enumerate(rolls): - # if next roll is same as current, sum and continue, else see if sum is best so far - if i+1 < len(rolls) and rolls[i+1] == r: - if rolling_sum == 0: - rolling_sum = r - rolling_sum += r - else: - if rolling_sum > method2: - method2 = rolling_sum - rolling_sum = 0 - # check for set in progress (e.g. lots of 1s) - if rolling_sum > method2: - method2 = rolling_sum - - # straight method - method3 = 0 - rolling_sum = 0 - count = 0 - for i, r in enumerate(rolls): - # if next roll is one less as current, sum and continue, else check len and see if sum is best so far - if i+1 < len(rolls) and rolls[i+1] == r-1: - if rolling_sum == 0: - rolling_sum = r - count += 1 - rolling_sum += r-1 - count += 1 - else: - if count >= 3 and rolling_sum > method3: - method3 = rolling_sum - rolling_sum = 0 - # check for straight in progress (e.g. straight ending in 1) - if count >= 3 and rolling_sum > method3: - method3 = rolling_sum - - # get best roll - best = max([method1, method2, method3]) - - # check for critical failure - botch = False - ones = 0 - for r in rolls: - if r == 1: - ones += 1 - if ones >= math.ceil(float(len(rolls))/2): - botch = True - - if botch: - result += 'BOTCH' - else: - result += str(best + modifier) - rollres = '' - for i,r in enumerate(rolls): - rollres += str(r) - if i is not len(rolls)-1: - rollres += ',' - result += ' [' + rollres - if modifier != 0: - if modifier > 0: - result += ' +' + str(modifier) - else: - result += ' -' + str(modifier * -1) - result += ']' - - reply += result - if count is not len(rollitrs)-1: - reply += "; " - if reply is not "": - return self.irc.reply(event, nick + ': ' + reply) - -# vi:tabstop=4:expandtab:autoindent -# kate: indent-mode python;indent-width 4;replace-tabs on;