diceroller.py: Refactored Dice object into its own library under lib/
This commit is contained in:
131
lib/dice.py
Normal file
131
lib/dice.py
Normal file
@ -0,0 +1,131 @@
|
||||
# dice.py
|
||||
#
|
||||
# This module contains a class to represent various RPG-style dice expressions.
|
||||
# These can then be rolled and the results, both total and individual rolls, returned.
|
||||
#
|
||||
# This module assumes that the built-in python PRNG (Mersenne Twister) is acceptable.
|
||||
# Support for real entropy is an RFE.
|
||||
|
||||
import random
|
||||
import re
|
||||
|
||||
class Dice():
|
||||
# This builds dice out of a simple input format
|
||||
# All of the dark regex magic is contained to this function,
|
||||
# lest it corrupt innocent code
|
||||
@classmethod
|
||||
def from_str(cls, desc):
|
||||
if not re.match(r'^\d+d\d+', desc):
|
||||
raise Exception('Dice format invalid. See --help')
|
||||
|
||||
(n, s) = re.match(r'^(\d+)d(\d+)', desc).groups()
|
||||
num = int(n)
|
||||
sides = int(s)
|
||||
mod = 0
|
||||
drop_low = 0
|
||||
drop_high = 0
|
||||
reroll = 0
|
||||
reroll_times = 0
|
||||
|
||||
for m in re.findall(r'[lh+-]\d+', desc):
|
||||
if m[0] == '-':
|
||||
mod += int(m)
|
||||
elif m[0] == '+':
|
||||
mod += int(m[1:])
|
||||
elif m[0] == 'h':
|
||||
drop_high = int(m[1:])
|
||||
elif m[0] == 'l':
|
||||
drop_low = int(m[1:])
|
||||
|
||||
m = re.search(r'r(\d+)(x\d+)?', desc)
|
||||
if m:
|
||||
(r, rt) = m.groups()
|
||||
reroll = int(r)
|
||||
if rt:
|
||||
reroll_times = int(rt[1:])
|
||||
|
||||
return cls(num, sides, mod, drop_low, drop_high, reroll, reroll_times)
|
||||
|
||||
|
||||
def __init__(self, num, sides, mod, drop_low=0, drop_high=0, reroll=0, reroll_times=0):
|
||||
self.num = num
|
||||
self.sides = sides
|
||||
self.mod = mod
|
||||
self.drop_low = drop_low
|
||||
self.drop_high = drop_high
|
||||
self.reroll = reroll
|
||||
self.reroll_times = reroll_times
|
||||
self.times_rolled = 0
|
||||
|
||||
|
||||
def num_rolls(self):
|
||||
return self.times_rolled
|
||||
|
||||
|
||||
# Rolls the dice, returns a structure
|
||||
# containing the result plus metadata
|
||||
def roll(self, verbose=False):
|
||||
results = []
|
||||
rerolled = []
|
||||
dropped = []
|
||||
|
||||
# Roll the actual dice, handling rerolls
|
||||
for i in range(0, self.num):
|
||||
count = 0
|
||||
result = 0
|
||||
while result <= self.reroll or result == 0:
|
||||
if self.reroll_times and count >= self.reroll_times:
|
||||
break
|
||||
|
||||
result = random.randint(1, self.sides)
|
||||
count += 1
|
||||
if result <= self.reroll:
|
||||
rerolled.append(result)
|
||||
|
||||
results.append(result)
|
||||
|
||||
|
||||
# Drop any high or low, if specified
|
||||
if self.drop_low or self.drop_high:
|
||||
results.sort()
|
||||
|
||||
if self.drop_low:
|
||||
dropped.extend(results[:self.drop_low])
|
||||
results = results[self.drop_low:]
|
||||
|
||||
if self.drop_high:
|
||||
dropped.extend(results[-1 * self.drop_high:])
|
||||
results = results[:-1 * self.drop_high]
|
||||
|
||||
random.shuffle(results)
|
||||
|
||||
# Get the results
|
||||
total = sum(results) + self.mod
|
||||
|
||||
self.times_rolled += 1
|
||||
|
||||
return {'total': total, 'rolls': results, 'dropped': dropped, 'rerolled': rerolled}
|
||||
|
||||
|
||||
def __str__(self):
|
||||
reroll_info = ''
|
||||
drop_info = ''
|
||||
mod_info = ''
|
||||
|
||||
if self.reroll:
|
||||
reroll_info = ' [reroll {}'.format(self.reroll)
|
||||
if self.reroll_times:
|
||||
reroll_info = reroll_info + 'x{}'.format(self.reroll_times)
|
||||
reroll_info = reroll_info + ']'
|
||||
|
||||
if self.drop_low:
|
||||
drop_info = drop_info + ' [drop low {}]'.format(self.drop_low)
|
||||
if self.drop_high:
|
||||
drop_info = drop_info + ' [drop high {}]'.format(self.drop_high)
|
||||
|
||||
if self.mod > 0:
|
||||
mod_info = '+{}'.format(self.mod)
|
||||
elif self.mod < 0:
|
||||
mod_info = '-{}'.format(self.mod)
|
||||
|
||||
return "{num}d{sides}{mod}{drop}{reroll}".format(num=self.num, sides=self.sides, mod=mod_info, drop=drop_info, reroll=reroll_info)
|
Reference in New Issue
Block a user