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
|
||||
|
||||
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':
|
||||
rollitrs = re.split(';\s*', ' '.join(whats[1:]))
|
||||
reply = ""
|
||||
for count, roll in enumerate(rollitrs):
|
||||
pattern = '^(?:(\d+)#)?(?:(\d+)/)?(\d+)?d(\d+)(?:(\+|\-)(\d+))?(?:\s+(.*))?'
|
||||
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 += "; "
|
||||
dicestr = ' '.join(whats[1:])
|
||||
self.build()
|
||||
yacc.parse(dicestr)
|
||||
reply = self.get_result()
|
||||
if reply is not "":
|
||||
return self.reply(connection, replypath, nick + ': ' + reply)
|
||||
if whats[0] == 'ctech':
|
||||
|
Loading…
Reference in New Issue
Block a user