88 lines
2.8 KiB
Python
88 lines
2.8 KiB
Python
"""
|
|
dice.py: written by Scaevolus 2008, updated 2009
|
|
simulates dicerolls
|
|
"""
|
|
import re
|
|
import random
|
|
|
|
from util import hook
|
|
|
|
|
|
whitespace_re = re.compile(r'\s+')
|
|
valid_diceroll = r'^([+-]?(?:\d+|\d*d(?:\d+|F))(?:[+-](?:\d+|\d*d(?:\d+|F)))*)( .+)?$'
|
|
valid_diceroll_re = re.compile(valid_diceroll, re.I)
|
|
sign_re = re.compile(r'[+-]?(?:\d*d)?(?:\d+|F)', re.I)
|
|
split_re = re.compile(r'([\d+-]*)d?(F|\d*)', re.I)
|
|
|
|
|
|
def nrolls(count, n):
|
|
"roll an n-sided die count times"
|
|
if n == "F":
|
|
return [random.randint(-1,1) for x in xrange(count)]
|
|
if n < 2: # it's a coin
|
|
if count < 5000:
|
|
return [random.randint(0, 1) for x in xrange(count)]
|
|
else: # fake it
|
|
return [int(random.normalvariate(.5*count, (.75*count)**.5))]
|
|
else:
|
|
if count < 5000:
|
|
return [random.randint(1, n) for x in xrange(count)]
|
|
else: # fake it
|
|
return [int(random.normalvariate(.5*(1+n)*count,
|
|
(((n+1)*(2*n+1)/6.-(.5*(1+n))**2)*count)**.5))]
|
|
|
|
|
|
@hook.command('roll')
|
|
@hook.regex(valid_diceroll, re.I)
|
|
@hook.command
|
|
def dice(inp):
|
|
".dice <diceroll> -- simulates dicerolls, e.g. .dice 2d20-d5+4 roll 2 " \
|
|
"D20s, subtract 1D5, add 4"
|
|
desc = None
|
|
try: # if inp is a re.match object...
|
|
(inp, desc) = inp.groups()
|
|
except AttributeError:
|
|
pass # we got called via hook.command, inp is already the roll
|
|
if desc == None: (inp, desc) = valid_diceroll_re.match(inp).groups()
|
|
|
|
spec = whitespace_re.sub('', inp)
|
|
if not valid_diceroll_re.match(spec):
|
|
return "Invalid diceroll"
|
|
groups = sign_re.findall(spec)
|
|
|
|
total = 0
|
|
rolls = []
|
|
|
|
for roll in groups:
|
|
count, side = split_re.match(roll).groups()
|
|
count = int(count) if count not in " +-" else 1
|
|
if side.upper() == "F": # fudge dice are basically 1d3-2
|
|
for fudge in nrolls(count, "F"):
|
|
if fudge == 1:
|
|
rolls.append("\x033+\x0F")
|
|
elif fudge == -1:
|
|
rolls.append("\x034-\x0F")
|
|
else:
|
|
rolls.append("0")
|
|
total += fudge
|
|
elif side == "":
|
|
total += count
|
|
else:
|
|
side = int(side)
|
|
try:
|
|
if count > 0:
|
|
dice = nrolls(count, side)
|
|
rolls += map(str, dice)
|
|
total += sum(dice)
|
|
else:
|
|
dice = nrolls(-count, side)
|
|
rolls += [str(-x) for x in dice]
|
|
total -= sum(dice)
|
|
except OverflowError:
|
|
return "Thanks for overflowing a float, jerk >:["
|
|
|
|
if desc:
|
|
return "%s: %d (%s=%s)" % (desc.strip(), total, inp, ", ".join(rolls))
|
|
else:
|
|
return "%d (%s=%s)" % (total, inp, ", ".join(rolls))
|