dice.py
'''\
Dice rolls and odds.
D odds_% winning_rolls possible_rolls
--------------------------------------------
1 16.667 1 6
2 30.556 11 36
3 42.130 91 216
4 51.775 671 1296
5 59.812 4651 7776
6 66.510 31031 46656
7 72.092 201811 279936
8 76.743 1288991 1679616
9 80.619 8124571 10077696
10 83.849 50700551 60466176
11 86.541 313968931 362797056
12 88.784 1932641711 2176782336
13 90.654 11839990891 13060694016
14 92.211 72260648471 78364164096
15 93.509 439667406451 470184984576
16 94.591 2668522016831 2821109907456
17 95.493 16163719991611 16926659444736
18 96.244 97745259402791 101559956668416
19 96.870 590286253682371 609359740010496
20 97.392 3560791008422351 3656158440062976
commands:
main - Main function (normal use)
demo - Demo function (show off and test app)
test - Test function (development)
usage:
module.dispatch({command}, args={arguments}) -- calling module from code
$PYTHON module_name {command} {arguments} -- executing module externally
Developer@Sonnack.com
April 2016
'''
from __future__ import print_function
from sys import argv, exc_info
from traceback import extract_tb
from os import path
from random import random
from logger import logger, info, debug, trace
Log = logger('dice')
BasePath = r'C:\CJS\prj\Python\ws'
BASE = 6
def generate_all_possible_rolls (dice=6):
rs = []
r = [1] * dice
while r:
rs.append(tuple(r))
r = generate_next_roll(r)
return rs
def generate_next_roll (roll):
ix = len(roll) - 1
while 0 <= ix:
roll[ix] += 1
if BASE < roll[ix]:
roll[ix] = 1
ix -= 1
else:
break
return None if ix < 0 else roll
def roll_six_stats (roll, dice=6):
'''Returns rolls stats on a roll of six dice. {roll: list of six dice}'''
zs = map(lambda x: x+1, range(dice))
stats = {'roll':roll, 'zs':zs}
stats['1s'] = sum(map(lambda x: 1 if x==1 else 0, roll))
stats['5s'] = sum(map(lambda x: 1 if x==5 else 0, roll))
stats['str8'] = (0 == sum(map(lambda x,y: abs(x-y), sorted(roll), zs)))
of_a_kind = [0] * BASE
for d in roll:
of_a_kind[d-1] += 1
stats['oak'] = of_a_kind
stats['2oak'] = sum(map(lambda x: 1 if x==2 else 0, of_a_kind))
stats['3oak'] = sum(map(lambda x: 1 if x==3 else 0, of_a_kind))
stats['4oak'] = sum(map(lambda x: 1 if x==4 else 0, of_a_kind))
stats['5oak'] = sum(map(lambda x: 1 if x==5 else 0, of_a_kind))
stats['6oak'] = sum(map(lambda x: 1 if x==6 else 0, of_a_kind))
stats['4+2'] = ((stats['4oak'] == 1) and (stats['2oak'] == 1))
stats['3+3'] = (stats['3oak'] == 2)
stats['3P'] = (stats['2oak'] == 3)
return stats
def print_roll_stats (stats):
print()
print('Roll: %s' % str(stats['roll']))
print('1s:%d, 5s:%d' % (stats['1s'], stats['5s']))
print('2oak:%d, 3oak:%d, 4oak:%d, 5oak:%d, 6oak:%d' % (stats['2oak'],stats['3oak'],stats['4oak'],stats['5oak'],stats['6oak']))
print('3+3:%s, 4+2:%s, 3P:%s' % (stats['3+3'], stats['4+2'], stats['3P']))
print('straight: %s' % str(stats['str8']))
print('OAK: %s' % str(stats['oak']))
print()
def odds_of_a_number (dice=6):
'''Implements: SUM:dice-1:i=0:[(B^i)*((B-1)^((dice-1)-i))] where: B=6; dice=#-of-dice.'''
m = BASE ** dice
ix = range(dice)
ls = [BASE ** n for n in ix]
rs = [(BASE-1) ** ((dice-1)-n) for n in ix]
ss = map(lambda a,b: a*b, ls, rs)
n = sum(ss)
return [100.0 * (float(n)/float(m)), n, m, ss, rs, ls]
def do_odds (*args):
Log.info('odds/parameters: %d' % len(args))
odds = []
print()
for n in range(20):
g = odds_of_a_number(n+1)
print('%8.3f %16d %16d' % tuple(g[:3]))
odds.append(g)
return 'Done!'
def do_list (*args):
Log.info('list/parameters: %d' % len(args))
rolls = generate_all_possible_rolls()
fn = path.join(BasePath, 'dice.lst')
fp = open(fn, 'w')
try:
print('Possible Rolls (%d):' % len(rolls), file=fp)
for ix,r in enumerate(rolls):
g = roll_six_stats(r)
t = (g['1s'],g['5s'], g['2oak'],g['3oak'],g['4oak'],g['5oak'],g['6oak'], g['3+3'],g['4+2'],g['3P'],g['str8'])
s = '%d %d [%d %d %d %d %d] %5s %5s %5s %5s'
print('%5d: %s %s' % (ix, r, (s % t)), file=fp)
print(file=fp)
except:
raise
finally:
fp.close()
return 'Done!'
def do_demo (*args):
Log.info('demo/parameters: %d' % len(args))
d1 = int(args[0]) if 0 < len(args) else int((random()*BASE)+1)
d2 = int(args[1]) if 1 < len(args) else int((random()*BASE)+1)
d3 = int(args[2]) if 2 < len(args) else int((random()*BASE)+1)
d4 = int(args[3]) if 3 < len(args) else int((random()*BASE)+1)
d5 = int(args[4]) if 4 < len(args) else int((random()*BASE)+1)
d6 = int(args[5]) if 5 < len(args) else int((random()*BASE)+1)
roll = [d1, d2, d3, d4, d5, d6]
g = roll_six_stats(roll)
print_roll_stats(g)
return 'Done!'
def do_test (*args):
Log.info('test/parameters: %d' % len(args))
rolls = generate_all_possible_rolls()
stats = map(lambda x: roll_six_stats(x), rolls)
possible_rolls = float(len(rolls))
ones = 0
fives = 0
one_or_five = 0
straights = 0
six_oak = 0
five_oak = 0
four_oak = 0
three_oak = 0
four_two = 0
two_triples = 0
three_pairs = 0
for stat in stats:
if stat['1s']: ones += 1
if stat['5s']: fives += 1
if stat['1s'] or stat['5s']: one_or_five += 1
if stat['str8']: straights += 1
if stat['6oak']: six_oak += 1
if stat['5oak']: five_oak += 1
if stat['4oak']: four_oak += 1
if stat['3oak']: three_oak += 1
if stat['4+2']: four_two += 1
if stat['3+3']: two_triples += 1
if stat['3P']: three_pairs += 1
fn = path.join(BasePath, 'dice.out')
fp = open(fn, 'w')
try:
print('Roll Stats:', file=fp)
print('===========', file=fp)
print('Dice: 6', file=fp)
print('Sides: %d' % BASE, file=fp)
print('Possible rolls: %d' % possible_rolls, file=fp)
print(file=fp)
print('Rolls with a 1: %d (%.2f%%)' % (ones, 100.0*(ones/possible_rolls)), file=fp)
print('Rolls with a 5: %d (%.2f%%)' % (fives, 100.0*(fives/possible_rolls)), file=fp)
print('Rolls with a 1 or 5: %d (%.2f%%)' % (one_or_five, 100.0*(one_or_five/possible_rolls)), file=fp)
print(file=fp)
print('Straights: %d (%.2f%%)' % (straights, 100.0*(straights/possible_rolls)), file=fp)
print(file=fp)
print('Three Pairs: %d (%.2f%%)' % (three_pairs, 100.0*(three_pairs/possible_rolls)), file=fp)
print(file=fp)
print('4+2: %d (%.2f%%)' % (four_two, 100.0*(four_two/possible_rolls)), file=fp)
print('3+3: %d (%.2f%%)' % (two_triples, 100.0*(two_triples/possible_rolls)), file=fp)
print(file=fp)
print('6-of-a-kind: %5d (%7.4f%%)' % (six_oak, 100.0*(six_oak/possible_rolls)), file=fp)
print('5-of-a-kind: %5d (%6.3f%%)' % (five_oak, 100.0*(five_oak/possible_rolls)), file=fp)
n = four_oak - four_two
print('4-of-a-kind: %5d (%5.2f%%) [raw: %d]' % (n, 100.0*(n/possible_rolls), four_oak), file=fp)
n = three_oak - two_triples
print('3-of-a-kind: %5d (%5.2f%%) [raw: %d]' % (n, 100.0*(n/possible_rolls), three_oak), file=fp)
print(file=fp)
n = 0
print(file=fp)
for stat in stats:
oak = stat['oak']
if (oak[0]==0) and (oak[4]==0) and (max(oak) < 3) and (not stat['3P']):
n += 1
print('Farkle: %d (%.2f%%)' % (n, 100.0*(n/possible_rolls)), file=fp)
print(file=fp)
except:
raise
finally:
fp.close()
return 'Done!'
def do_main (*args):
Log.info('main/parameters: %d' % len(args))
return 'Done!'
def dispatch (cmd, *args):
Log.info('command: %s' % cmd)
Log.info('arguments: %d' % len(args))
if cmd == 'odds': return do_odds(*args)
if cmd == 'list': return do_list(*args)
if cmd == 'demo': return do_demo(*args)
if cmd == 'test': return do_test(*args)
if cmd == 'main': return do_main(*args)
return 'Nothing to do!'
if __name__ == '__main__':
print('autorun: %s' % argv[0])
Log.start(path.join(BasePath,'dice.log'))
Log.level(info())
cmd = argv[1] if 1 < len(argv) else ''
etc = argv[2:] if 2 < len(argv) else []
try:
obj = dispatch(cmd, *etc)
print(obj)
Log.info(obj)
except:
etype, evalue, tb = exc_info()
ts = extract_tb(tb)
Log.error('%s: %s' % (etype.__name__,str(evalue)))
for t in ts[-3:]:
Log.error('[%d] %s (%s)' % (t[1], t[2], t[0]))
Log.error(' %s' % t[3])
raise
finally:
Log.end()
'''eof'''