When you don't have a set of dice lying around, or when 20d6+2d4 just takes too long to add up:
#!/usr/bin/env python import logging import random import re import sys ROLL = re.compile(r'(\d*)d(\d+)') def roll(dice): """Randomly generate the result of a dice roll. >>> 1 <= roll('d6') <= 6 True >>> 4 <= roll('4d6') <= 24 True >>> 2 <= roll('1d10+1d4') <= 14 True >>> 2+1-1 <= roll('2d8+1d6-1') <= 16+6-1 True """ logging.debug("roll(%r)", dice) def _roll(m): x = m.group(1) and int(m.group(1)) or 1 y = int(m.group(2)) return '%d' % (x * random.randint(1, y)) postroll = ROLL.sub(_roll, dice) logging.debug("roll(%r) -> %r", dice, postroll) value = eval(postroll, {}, {}) logging.debug("eval(%r) -> %r", postroll, value) return value def argz(): """Define the arguments to this script.""" from optparse import OptionParser parser = OptionParser() parser.set_defaults(log_level=logging.WARNING) parser.add_option('-d', '--debug', action='store_const', const=logging.DEBUG, dest='log_level') parser.add_option('-v', '--verbose', action='store_const', const=logging.INFO, dest='log_level') parser.add_option('-q', '--quiet', action='store_const', const=logging.ERROR, dest='log_level') parser.add_option('--test', action='store_true', dest='test', default=False, help="Run the unit tests.") parser.add_option('--stat', action='store_true', dest='stat', default=False, help="Roll 4d6, drop the lowest (for ability scores).") (options, args) = parser.parse_args() logging.basicConfig(level=options.log_level) if options.stat: dice = [roll('d6') for x in xrange(4)] logging.info("rolled %r for ability", dice) dice = sorted(dice)[1:] logging.debug("kept %r", dice) stat = sum(dice) print "%d (%+01d)" % (stat, (stat - 10) / 2) sys.exit(0) if options.test or len(args) == 0: import doctest doctest.testmod() sys.exit(0) return (options, args) if __name__ == '__main__': (options, args) = argz() for dice in args: print roll(dice)
You can even roll stats!
$ for stat in STR DEX CON WIS INT CHA EXT > do ./roll.py --stat > done | sort -n | tail -n6 13 (+1) 14 (+2) 15 (+2) 16 (+3) 17 (+3) 17 (+3)