From c2280aaf1e52b7e10380119152ea8fcaad182c5e Mon Sep 17 00:00:00 2001 From: kad Date: Sun, 31 Oct 2010 11:08:58 -0600 Subject: [PATCH] Remove diceply.py --- modules/Dice.py | 218 ++++++++++++++++++++++++++++++++++++++- modules/diceply.py | 246 --------------------------------------------- 2 files changed, 216 insertions(+), 248 deletions(-) delete mode 100755 modules/diceply.py diff --git a/modules/Dice.py b/modules/Dice.py index 27d5ad2..e03927f 100644 --- a/modules/Dice.py +++ b/modules/Dice.py @@ -22,20 +22,234 @@ from extlib import irclib from Module import Module -import diceply +import ply.lex as lex import ply.yacc as yacc # Rolls dice, for RPGs mostly class Dice(Module): + tokens = ['NUMBER', 'TEXT'] + literals = ['#', '/', '+', '-', 'd', ';'] + + def build(self): + lex.lex(module=self) + yacc.yacc(module=self) + + t_TEXT = 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', ';'), + ('left', '+', '-'), + ('right', 'd'), + ('left', '#'), + ('left', '/') + ) + + output = "" + + # Takes the parsed dice string for a single roll (eg 3/4d20) and performs + # the actual roll. Returns a string representing the result + def roll_dice(self, keep, dice, size): + 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 = "["; + if len(b) != len(a): + # Bold is \002 + outstr += ",".join(str(i) for i in b) + "," + temp = sorted(a, reverse=True) + temp = temp[keep:] + bstr = ",".join(str(i) for i in temp) + outstr += bstr + else: + outstr += ",".join(str(i) for i in a) + outstr += "]" + + return (total, outstr) + + # 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) + def process_roll(self, trials, mods, comment): + output = "" + mode = 1 + repeat = 1 + if trials != None: + repeat = trials + for i in range(repeat): + 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 + 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 + output += "%d (%s)" % (total, curr_str) + if comment != None: + output = "%s: %s" % (comment.strip(), output) + return output + + # 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 + def p_roll_r(self, p): + 'roll : roll ";" roll' + global output + p[0] = p[1] + "; " + p[3] + output = p[0] + + # The basic roll string + def p_roll(self, p): + '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] + + # Trial is optional so have a rule without it + def p_roll_no_trials(self, p): + '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): + '''comment : TEXT + |''' + if len(p) == 2: + p[0] = p[1] + else: + p[0] = None + + def p_modifier(self, p): + '''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]] + + # Return the left side before the "d", and the number of faces + def p_die(self, p): + 'modifier : left NUMBER' + p[0] = (p[1], p[2]) + + def p_die_num(self, p): + 'modifier : NUMBER' + p[0] = p[1] + + # left is the number of dice we are rolling, and how many we are keeping + def p_left(self, p): + '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 + + # Provide the user with something (albeit not much) when the roll can't be parsed + def p_error(self, p): + global output + output = "Unable to parse roll" + + def get_result(self): + global output + return output + def do(self, connection, event, nick, userhost, replypath, what, admin_unlocked): whats = what.split(' ') if whats[0] == 'roll': dicestr = ' '.join(whats[1:]) + self.build() yacc.parse(dicestr) - reply = diceply.get_result() + reply = self.get_result() if reply is not "": return self.reply(connection, replypath, nick + ': ' + reply) if whats[0] == 'ctech': diff --git a/modules/diceply.py b/modules/diceply.py deleted file mode 100755 index 809a0c6..0000000 --- a/modules/diceply.py +++ /dev/null @@ -1,246 +0,0 @@ -# diceply.py -# Dice string grammar using PLY -# After initial run, parser.out should have the full grammar and the states -# and any shift/reduce or reduce/reduce conflicts. -# -# The current state of the grammar has some shift/reduce conflicts, because -# I don't know LR parsers well enough to prevent them. Also, currently spaces -# in the roll string aren't working if we want comments (for now) -# -# This script can be run standalone if you enable the tests at the bottom - -import sys -import random - -tokens = ['NUMBER', 'TEXT'] - -literals = ['#', '/', '+', '-', 'd', ';'] - -t_TEXT = r'\s+[^;]+' - -def t_NUMBER(t): - r'\d+' - t.value = int(t.value) - return t - -def t_error(t): - t.lexer.skip(1) - -import ply.lex as lex -lex.lex() - -precedence = ( - ('left', ';'), - ('left', '+', '-'), - ('right', 'd'), - ('left', '#'), - ('left', '/') -) - -output = "" - -# Takes the parsed dice string for a single roll (eg 3/4d20) and performs -# the actual roll. Returns a string representing the result -def roll_dice(keep, dice, size): - 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 = "["; - if len(b) != len(a): - # Bold is \002 - outstr += ",".join(str(i) for i in b) + "," - temp = sorted(a, reverse=True) - temp = temp[keep:] - bstr = ",".join(str(i) for i in temp) - outstr += bstr - else: - outstr += ",".join(str(i) for i in a) - outstr += "]" - - return (total, outstr) - -# 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) -def process_roll(trials, mods, comment): - output = "" - mode = 1 - repeat = 1 - if trials != None: - repeat = trials - for i in range(repeat): - 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 - res = 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 - output += "%d (%s)" % (total, curr_str) - if comment != None: - output = "%s: %s" % (comment.strip(), output) - return output - -# 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 -def p_roll_r(p): - 'roll : roll ";" roll' - global output - p[0] = p[1] + "; " + p[3] - output = p[0] - -# The basic roll string -def p_roll(p): - 'roll : trial modifier comment' - global output - mods = [] - if type(p[2]) == list: - mods = p[2] - else: - mods = [p[2]] - p[0] = process_roll(p[1], mods, p[3]) - output = p[0] - -# Trial is optional so have a rule without it -def p_roll_no_trials(p): - 'roll : modifier comment' - global output - mods = [] - if type(p[1]) == list: - mods = p[1] - else: - mods = [p[1]] - p[0] = process_roll(None, mods, p[2]) - output = p[0] - -def p_comment(p): - '''comment : TEXT - |''' - if len(p) == 2: - p[0] = p[1] - else: - p[0] = None - -def p_modifier(p): - '''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]] - -# Return the left side before the "d", and the number of faces -def p_die(p): - 'modifier : left NUMBER' - p[0] = (p[1], p[2]) - -def p_die_num(p): - 'modifier : NUMBER' - p[0] = p[1] - -# left is the number of dice we are rolling, and how many we are keeping -def p_left(p): - 'left : keep dice' - if p[1] == None: - p[0] = [None, p[2]] - else: - p[0] = [p[1], p[2]] - -def p_left1(p): - 'left : dice' - p[0] = [None, p[1]] - -def p_left_e(p): - 'left :' - p[0] = None - -def p_total(p): - 'trial : NUMBER "#"' - if len(p) > 1: - p[0] = p[1] - else: - p[0] = None - -def p_keep(p): - 'keep : NUMBER "/"' - if p[1] != None: - p[0] = p[1] - else: - p[0] = None - -def p_dice(p): - 'dice : NUMBER "d"' - p[0] = p[1] - -def p_dice2(p): - 'dice : "d"' - p[0] = 1 - -# Provide the user with something (albeit not much) when the roll can't be parsed -def p_error(p): - print "Unable to parse roll" - print yacc.token() - global output - output = "Unable to parse roll" - -def get_result(): - global output - return output - -import ply.yacc as yacc -yacc.yacc() - -# Testing stuff: - -# rolls = ( - # "5#3/4d20+1+2+d20;3/4d20;4d20", - # "d20+10 attack;d8+6 damage", - # "2#d20+10 twin strike!111;d10+9 damage", - # "d20+d20;d20+d20;d20+d20+d20", - # "error" -# ) - -# for roll in rolls: - # print "***** trying roll " + roll - # yacc.parse(roll, debug=0) - # print output