Last active
June 22, 2020 01:58
-
-
Save jsocol/74c3f3e38d37c7dcefc847389564854a to your computer and use it in GitHub Desktop.
Revisions
-
jsocol revised this gist
Jun 22, 2020 . 1 changed file with 39 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,4 @@ import argparse import random @@ -82,4 +83,41 @@ def __str__(self): d10 = D(10) d12 = D(12) d20 = D(20) d100 = D(100) def _get_cli_options(): parser = argparse.ArgumentParser() parser.add_argument('-c', '--count', type=int, default=1) parser.add_argument('-s', '--size', type=int, default=20) parser.add_argument('-r', '--roll', default=None) # parser.add_argument('--adv', action='store_true', default=False) # parser.add_argument('--dis', action='store_true', default=False) return parser.parse_args() def _main(): options = _get_cli_options() if options.roll is not None: count, _, size = options.roll.partition('d') count = int(count) size = int(size) else: count = options.count size = options.size die = D(size) rolls = [die.roll() for _ in range(count)] total = sum(rolls) print('rolling {}d{}'.format(count, size)) print('{}'.format(rolls)) print('total: {}'.format(total)) __main__ = _main if __name__ == '__main__': _main() -
jsocol revised this gist
May 19, 2020 . 1 changed file with 8 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -98,7 +98,8 @@ def calc_stats(totals, iters): } def rolltest(roller, iters=10000, precision=3, graph='normal', zoom=1, max_cols=None): print('Method: %s\nDesc: %s' % (roller.__name__, roller.__doc__)) print('Iterations: %d, Precision: %d' % (iters, precision)) @@ -111,8 +112,9 @@ def rolltest(roller, iters=10000, precision=3, graph='normal', zoom=1): print('Average: {mean:0.2f}; StDev: {stdev:0.2f}'.format(**stats)) output_fmt = '{0:3d} [{1:5.1f}%]: {2}' if max_cols is None: prefix_length = len(output_fmt.format(18, 15.1, '')) max_cols = os.get_terminal_size().columns - prefix_length if graph == 'normal': for k in sorted(totals.keys()): @@ -149,6 +151,7 @@ def make_parser(): parser.add_argument('-g', '--graph', default='normal', choices=['normal', 'atleast', 'atmost']) parser.add_argument('-z', '--zoom', default=1, type=int) parser.add_argument('-m', '--max-cols', default=None, type=int) return parser @@ -184,7 +187,8 @@ def main(): for test in to_run: rolltest(test, iters=options.iters, precision=options.precision, graph=options.graph, zoom=options.zoom, max_cols=options.max_cols) if __name__ == '__main__': -
jsocol revised this gist
May 19, 2020 . 1 changed file with 35 additions and 58 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,58 +1,35 @@ $ python rolltest.py --help usage: rolltest.py [-h] [-p PRECISION] [-i ITERS] [-t {3d6,4d6d1,adv,d20,disadv,noam,4d6ro1d1}] [-l LAMB] [-r RAW] [--imp IMP] [-g {normal,atleast,atmost}] [-z ZOOM] optional arguments: -h, --help show this help message and exit -p PRECISION, --precision PRECISION -i ITERS, --iters ITERS -t {3d6,4d6d1,adv,d20,disadv,noam,4d6ro1d1}, --test {3d6,4d6d1,adv,d20,disadv,noam,4d6ro1d1} -l LAMB, --lambda LAMB -r RAW, --raw RAW --imp IMP A dotted path to a roller, e.g. --imp my.module.roller, where roller is a callable -g {normal,atleast,atmost}, --graph {normal,atleast,atmost} -z ZOOM, --zoom ZOOM $ python rolltest.py --test 4d6ro1d1 --zoom 2 --precision 4 Method: r4d6ro1dl1 Desc: 4d6, reroll 1s once, drop lowest Iterations: 10000, Precision: 4 Average: 13.29; StDev: 2.42 6 [ 0.2%]: * 7 [ 0.9%]: *** 8 [ 1.8%]: ******* 9 [ 3.7%]: *************** 10 [ 6.8%]: **************************** 11 [ 10.2%]: ****************************************** 12 [ 12.8%]: ***************************************************** 13 [ 14.6%]: ************************************************************ 14 [ 15.4%]: *************************************************************** 15 [ 14.2%]: ********************************************************** 16 [ 10.3%]: ****************************************** 17 [ 6.6%]: *************************** 18 [ 2.5%]: ********** -
jsocol revised this gist
May 19, 2020 . 1 changed file with 46 additions and 9 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -6,6 +6,7 @@ """ import argparse import importlib import os import sys from collections import defaultdict @@ -57,17 +58,47 @@ def oned20(): return 1 * d20 def r4d6ro1dl1(): """4d6, reroll 1s once, drop lowest""" rolls = [] for _ in range(4): # 4d6 roll = d6.roll() if roll == 1: # Reroll 1s once roll = d6.roll() rolls.append(roll) rolls = list(sorted(rolls, reverse=True)) return sum(rolls[0:3]) TESTS = { '3d6': threed6, '4d6d1': fourdrop1, 'adv': adv, 'd20': oned20, 'disadv': disadv, 'noam': noam, '4d6ro1d1': r4d6ro1dl1, } def calc_stats(totals, iters): roll_total = sum(k * v for k, v in totals.items()) mean = roll_total / iters variance = 0.0 for score, count in totals.items(): variance += ((score - mean) ** 2) * count variance /= (iters - 1) stdev = variance ** 0.5 return { 'mean': mean, 'stdev': stdev, 'variance': variance, } def rolltest(roller, iters=10000, precision=3, graph='normal', zoom=1): print('Method: %s\nDesc: %s' % (roller.__name__, roller.__doc__)) print('Iterations: %d, Precision: %d' % (iters, precision)) @@ -76,24 +107,29 @@ def rolltest(roller, iters=10000, precision=3, graph='normal'): score = roller() totals[score] += 1 stats = calc_stats(totals, iters) print('Average: {mean:0.2f}; StDev: {stdev:0.2f}'.format(**stats)) output_fmt = '{0:3d} [{1:5.1f}%]: {2}' prefix_length = len(output_fmt.format(18, 15.1, '')) max_cols = os.get_terminal_size().columns - prefix_length if graph == 'normal': for k in sorted(totals.keys()): frac = round(totals[k] / iters, precision) cols = int(frac * max_cols * zoom) print(output_fmt.format(k, frac * 100., '*' * cols)) elif graph == 'atmost': cumulative = 0.0 for k in sorted(totals.keys()): cumulative += round(totals[k] / iters, precision) cols = int(cumulative * max_cols) print(output_fmt.format(k, cumulative * 100., '*' * cols)) elif graph == 'atleast': cumulative = 1.0 for k in sorted(totals.keys()): cols = int(cumulative * max_cols) print(output_fmt.format(k, cumulative * 100., '*' * cols)) cumulative -= round(totals[k] / iters, precision) else: print('Unsupported graph mode: %s' % graph) @@ -112,6 +148,7 @@ def make_parser(): 'roller is a callable')) parser.add_argument('-g', '--graph', default='normal', choices=['normal', 'atleast', 'atmost']) parser.add_argument('-z', '--zoom', default=1, type=int) return parser @@ -147,8 +184,8 @@ def main(): for test in to_run: rolltest(test, iters=options.iters, precision=options.precision, graph=options.graph, zoom=options.zoom) if __name__ == '__main__': main() -
jsocol revised this gist
Apr 25, 2020 . 1 changed file with 11 additions and 8 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -53,7 +53,7 @@ def fourdrop1(): def oned20(): """1d20 to embrace chaos""" return 1 * d20 @@ -78,22 +78,25 @@ def rolltest(roller, iters=10000, precision=3, graph='normal'): if graph == 'normal': for k in sorted(totals.keys()): frac = round(totals[k] / iters, precision) cols = int(frac * 10 ** precision) print('%3d [%5.1f%%]: %s' % (k, frac * 100., '*' * cols)) elif graph == 'atmost': max_cols = 100 cumulative = 0.0 for k in sorted(totals.keys()): cumulative += round(totals[k] / iters, precision) cols = int(cumulative * max_cols) print('%3d [%5.1f%%]: %s' % (k, cumulative * 100., '*' * cols)) elif graph == 'atleast': max_cols = 100 cumulative = 1.0 for k in sorted(totals.keys()): cols = int(cumulative * max_cols) print('%3d [%5.1f%%]: %s' % (k, cumulative * 100., '*' * cols)) cumulative -= round(totals[k] / iters, precision) else: print('Unsupported graph mode: %s' % graph) def make_parser(): @@ -148,4 +151,4 @@ def main(): if __name__ == '__main__': main() -
jsocol revised this gist
Apr 25, 2020 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -53,7 +53,7 @@ def fourdrop1(): def oned20(): """1d20 because you live dangerously""" return 1 * d20 -
jsocol revised this gist
Apr 25, 2020 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -81,14 +81,14 @@ def rolltest(roller, iters=10000, precision=3, graph='normal'): cols = int(round(totals[k] / iters, precision) * 10 ** precision) print('%2d: %s' % (k, '*' * cols)) elif graph == 'atleast' or graph == 'cdf': max_cols = 100 cumulative = 0.0 for k in sorted(totals.keys()): cumulative += round(totals[k] / iters, precision) cols = int(cumulative * max_cols) print('%2d: %s' % (k, '*' * cols)) elif graph == 'atmost': max_cols = 100 cumulative = 1.0 for k in sorted(totals.keys()): cols = int(cumulative * max_cols) -
jsocol revised this gist
Apr 25, 2020 . 1 changed file with 23 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -67,7 +67,7 @@ def oned20(): } def rolltest(roller, iters=10000, precision=3, graph='normal'): print('Method: %s\nDesc: %s' % (roller.__name__, roller.__doc__)) print('Iterations: %d, Precision: %d' % (iters, precision)) @@ -76,9 +76,24 @@ def rolltest(roller, iters=10000, precision=3): score = roller() totals[score] += 1 if graph == 'normal': for k in sorted(totals.keys()): cols = int(round(totals[k] / iters, precision) * 10 ** precision) print('%2d: %s' % (k, '*' * cols)) elif graph == 'atleast' or graph == 'cdf': max_cols = 20 * precision cumulative = 0.0 for k in sorted(totals.keys()): cumulative += round(totals[k] / iters, precision) cols = int(cumulative * max_cols) print('%2d: %s' % (k, '*' * cols)) elif graph == 'atmost': max_cols = 20 * precision cumulative = 1.0 for k in sorted(totals.keys()): cols = int(cumulative * max_cols) print('%2d: %s' % (k, '*' * cols)) cumulative -= round(totals[k] / iters, precision) def make_parser(): @@ -92,6 +107,8 @@ def make_parser(): parser.add_argument('--imp', help=('A dotted path to a roller, e.g. ' '--imp my.module.roller, where ' 'roller is a callable')) parser.add_argument('-g', '--graph', default='normal', choices=['normal', 'atleast', 'atmost']) return parser @@ -126,7 +143,8 @@ def main(): to_run.append(roller) for test in to_run: rolltest(test, iters=options.iters, precision=options.precision, graph=options.graph) if __name__ == '__main__': -
jsocol revised this gist
Apr 25, 2020 . 1 changed file with 58 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,58 @@ $ python rolltest.py -t 3d6 -t 4d6d1 -r 'mk_roller(d6, d6, d6, d6, d4, drop=2)' -i100000 -p2 Method: threed6 Desc: 3d6 Iterations: 100000, Precision: 2 3: 4: * 5: *** 6: ***** 7: ******* 8: ********** 9: ************ 10: ************* 11: ************* 12: ************ 13: ********** 14: ******* 15: ***** 16: *** 17: * 18: Method: fourdrop1 Desc: 4d6 drop 1 Iterations: 100000, Precision: 2 3: 4: 5: * 6: ** 7: *** 8: ***** 9: ******* 10: ********* 11: *********** 12: ************* 13: ************* 14: ************ 15: ********** 16: ******* 17: **** 18: ** Method: ['d6', 'd6', 'd6', 'd6', 'd4'] drop 2 Desc: user input: mk_roller(d6, d6, d6, d6, d4, drop=2) Iterations: 100000, Precision: 2 3: 4: 5: 6: * 7: ** 8: *** 9: ***** 10: ******** 11: *********** 12: ************** 13: *************** 14: *************** 15: ************ 16: ******** 17: **** 18: ** -
jsocol created this gist
Apr 25, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,85 @@ import random __all__ = ['d4', 'd6', 'd8', 'd10', 'd12', 'd20', 'd100'] class D(): """A die. Simulates an n-sided die, e.g. to create a d6, pass 6: >>> d4, d6, d8, d10, d12, d20 = D(4), D(6), D(8), D(10), D(12), D(20) To roll, you can call d6.roll() (or just d6()): >>> d6.roll() <<< 4 >>> d6() <<< 2 Or even just: >>> d6 <<< 3 (NB: I'm sure I owe someone an apology for that.) Need to roll with (dis) advantage? >>> d20, d20 <<< (20, 13) But the best way to use these is to do math with dice. For example, if you need to roll 3d8 + 4: >>> 3 * d8 + 4 <<< 22 Or add dice: >>> d8 + d4 <<< 9 Remember these are random rolls, so the examples above are not guaranteed. """ def __init__(self, sides): self.sides = sides def roll(self): """Roll the dice!""" return random.randint(1, self.sides) def __call__(self): return self.roll() def __mul__(self, n): """Roll n (an integer) number of dice.""" return sum([self.roll() for _ in range(n)]) __rmul__ = __mul__ def __add__(self, n): """Add an integer or another die.""" if isinstance(n, D): n = n.roll() return n + self.roll() __radd__ = __add__ def __repr__(self): return str(self.roll()) def __str__(self): return 'd%d' % self.sides d4 = D(4) d6 = D(6) d8 = D(8) d10 = D(10) d12 = D(12) d20 = D(20) d100 = D(100) This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,133 @@ # coding: utf-8 """A monte carlo dice simulator Given a method for rolling a set of dice, generate a histogram of results by actually trying it a bunch. """ import argparse import importlib import sys from collections import defaultdict from dice import d4, d6, d8, d10, d12, d20, d100, D # noqa: F401 def mk_roller(*dice, drop=0): """Create a roller from a set of dice and a number to drop If drop >= 0, drop the lowest rolls. If drop < 0, drop the highest. """ def roller(): rolls = [d.roll() for d in dice] keep = len(dice) - abs(drop) rolls = list(sorted(rolls, reverse=drop >= 0))[0:keep] return sum(rolls) roller.__name__ = [str(d) for d in dice].__str__() + ' drop %d' % drop return roller adv = mk_roller(d20, d20, drop=1) adv.__doc__ = 'advantage' disadv = mk_roller(d20, d20, drop=-1) disadv.__doc__ = 'disadvantage' def noam(): """4d6 + 1d4 drop 2""" rolls = [d6.roll(), d6.roll(), d6.roll(), d6.roll(), d4.roll()] rolls = list(sorted(rolls, reverse=True)) rolls = rolls[0:3] return sum(rolls) def threed6(): """3d6""" return 3 * d6 def fourdrop1(): """4d6 drop 1""" rolls = sorted([d6.roll() for _ in range(4)]) top3 = list(reversed(rolls))[0:3] return sum(top3) def oned20(): """1d20 like a psychopath""" return 1 * d20 TESTS = { '3d6': threed6, '4d6d1': fourdrop1, 'adv': adv, 'd20': oned20, 'disadv': disadv, 'noam': noam, } def rolltest(roller, iters=10000, precision=3): print('Method: %s\nDesc: %s' % (roller.__name__, roller.__doc__)) print('Iterations: %d, Precision: %d' % (iters, precision)) totals = defaultdict(int) for _ in range(iters): score = roller() totals[score] += 1 for k in sorted(totals.keys()): cols = int(round(totals[k] / iters, precision) * 10 ** precision) print('%2d: %s' % (k, '*' * cols)) def make_parser(): parser = argparse.ArgumentParser() parser.add_argument('-p', '--precision', default=3, type=int) parser.add_argument('-i', '--iters', default=10000, type=int) parser.add_argument('-t', '--test', dest='tests', choices=TESTS.keys(), action='append', default=[]) parser.add_argument('-l', '--lambda', dest='lamb') parser.add_argument('-r', '--raw') parser.add_argument('--imp', help=('A dotted path to a roller, e.g. ' '--imp my.module.roller, where ' 'roller is a callable')) return parser def get_opts(argv=None): parser = make_parser() if argv is None: argv = sys.argv[1:] return parser.parse_args(argv) def main(): options = get_opts() to_run = [] for test in options.tests: to_run.append(TESTS[test]) if options.lamb: roller = eval('lambda: %s' % options.lamb) roller.__doc__ = 'user lambda: %s' % options.lamb to_run.append(roller) if options.raw: roller = eval(options.raw) roller.__doc__ = 'user input: %s' % options.raw to_run.append(roller) if options.imp: path, _, method = options.imp.rpartition('.') module = importlib.import_module(path) roller = getattr(module, method) to_run.append(roller) for test in to_run: rolltest(test, iters=options.iters, precision=options.precision) if __name__ == '__main__': main()