Merge branch 'kad'
This commit is contained in:
		
						commit
						0bcefc49b5
					
				
							
								
								
									
										283
									
								
								modules/Dice.py
									
									
									
									
									
								
							
							
						
						
									
										283
									
								
								modules/Dice.py
									
									
									
									
									
								
							@ -22,77 +22,234 @@ from extlib import irclib
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from Module import Module
 | 
					from Module import Module
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ply.lex as lex
 | 
				
			||||||
 | 
					import ply.yacc as yacc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Rolls dice, for RPGs mostly
 | 
					# Rolls dice, for RPGs mostly
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Dice(Module):
 | 
					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):
 | 
					    def do(self, connection, event, nick, userhost, replypath, what, admin_unlocked):
 | 
				
			||||||
        whats = what.split(' ')
 | 
					        whats = what.split(' ')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if whats[0] == 'roll':
 | 
					        if whats[0] == 'roll':
 | 
				
			||||||
            rollitrs = re.split(';\s*', ' '.join(whats[1:]))
 | 
					            dicestr = ' '.join(whats[1:])
 | 
				
			||||||
            reply = ""
 | 
					            self.build()
 | 
				
			||||||
            for count, roll in enumerate(rollitrs):
 | 
					            yacc.parse(dicestr)
 | 
				
			||||||
                pattern = '^(?:(\d+)#)?(?:(\d+)/)?(\d+)?d(\d+)(?:(\+|\-)(\d+))?(?:\s+(.*))?'
 | 
					            reply = self.get_result()
 | 
				
			||||||
                regex = re.compile(pattern)
 | 
					 | 
				
			||||||
                matches = regex.search(roll)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if matches is not None:
 | 
					 | 
				
			||||||
                    # set variables including defaults for unspecified stuff
 | 
					 | 
				
			||||||
                    faces = int(matches.group(4))
 | 
					 | 
				
			||||||
                    comment = matches.group(7)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if matches.group(1) is None:
 | 
					 | 
				
			||||||
                        times = 1
 | 
					 | 
				
			||||||
                    else:
 | 
					 | 
				
			||||||
                        times = int(matches.group(1))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if matches.group(3) is None:
 | 
					 | 
				
			||||||
                        dice = 1
 | 
					 | 
				
			||||||
                    else:
 | 
					 | 
				
			||||||
                        dice = int(matches.group(3))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if matches.group(2) is None:
 | 
					 | 
				
			||||||
                        top = dice
 | 
					 | 
				
			||||||
                    else:
 | 
					 | 
				
			||||||
                        top = int(matches.group(2))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if matches.group(5) is None or matches.group(6) is None:
 | 
					 | 
				
			||||||
                        modifier = 0
 | 
					 | 
				
			||||||
                    else:
 | 
					 | 
				
			||||||
                        if str(matches.group(5)) == '-':
 | 
					 | 
				
			||||||
                            modifier = -1 * int(matches.group(6))
 | 
					 | 
				
			||||||
                        else:
 | 
					 | 
				
			||||||
                            modifier = int(matches.group(6))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    result = roll + ': '
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    for t in range(times):
 | 
					 | 
				
			||||||
                        ressubstr = ""
 | 
					 | 
				
			||||||
                        rolls = []
 | 
					 | 
				
			||||||
                        for d in range(dice):
 | 
					 | 
				
			||||||
                            rolls.append(str(random.randint(1, faces)))
 | 
					 | 
				
			||||||
                        rolls.sort()
 | 
					 | 
				
			||||||
                        rolls.reverse()
 | 
					 | 
				
			||||||
                        ressubstr = ','.join(rolls[0:top])
 | 
					 | 
				
			||||||
                        sum = 0
 | 
					 | 
				
			||||||
                        for r in rolls[0:top]:
 | 
					 | 
				
			||||||
                            sum += int(r)
 | 
					 | 
				
			||||||
                        sumplus = sum + modifier
 | 
					 | 
				
			||||||
                        result += str(sumplus) + ' [' + ressubstr
 | 
					 | 
				
			||||||
                        if modifier != 0:
 | 
					 | 
				
			||||||
                            if modifier > 0:
 | 
					 | 
				
			||||||
                                result += ' + ' + str(modifier)
 | 
					 | 
				
			||||||
                            else:
 | 
					 | 
				
			||||||
                                result += ' - ' + str(-1 * modifier)
 | 
					 | 
				
			||||||
                        result += ']'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        if t != times-1:
 | 
					 | 
				
			||||||
                            result += ', '
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    reply += result
 | 
					 | 
				
			||||||
                    if count is not len(rollitrs)-1:
 | 
					 | 
				
			||||||
                        reply += "; "
 | 
					 | 
				
			||||||
            if reply is not "":
 | 
					            if reply is not "":
 | 
				
			||||||
                return self.reply(connection, replypath, nick + ': ' + reply)
 | 
					                return self.reply(connection, replypath, nick + ': ' + reply)
 | 
				
			||||||
        if whats[0] == 'ctech':
 | 
					        if whats[0] == 'ctech':
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user