-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
116 lines (102 loc) · 4.12 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import sys
import re
import random
import shutil
import math
import parser
import distribution
def solve(input_string, report_cumulative=False, analytic=False, show_odds=False, just_roll=False):
result_distribution = distribution.DiscreteDistribution({1:1})
if just_roll:
result = parser.calculate(input_string, False)
print(result)
return
if analytic:
result_distribution = parser.calculate(input_string, analytic)
print_graph(result_distribution, report_cumulative, show_odds)
else:
count = 0
results = {}
try:
while True:
result_sample = parser.calculate(input_string, analytic)
count += 1
try:
results[result_sample] += 1
except KeyError:
results[result_sample] = 1
if (count+1)%1000 == 0:
result_distribution = distribution.DiscreteDistribution(results)
print_height = print_graph(result_distribution, report_cumulative, show_odds)
print(f"\033[F"*(print_height+2))
except KeyboardInterrupt:
print_graph(result_distribution, report_cumulative, show_odds)
def print_graph(results, cumulative=False, show_odds=False):
print("Expected value: {:6.3f}".format(sum(i*results[i]/sum(results.chances()) for i in results.values())))
if cumulative:
results = results.get_cumulative()
count = max(results.chances())
else:
count = sum(results.chances())
maxvalue = max(results.chances())
maxvalue_len = max(len(str(i)) for i in results.values())
total_bar_length = shutil.get_terminal_size((80,20))[0]-maxvalue_len-12
for key in sorted(results.values()):
bar_length = round(results[key]/maxvalue*total_bar_length)
if show_odds:
p_gcd = math.gcd(results[key],count-results[key])
probs_str = "{}:{}".format(results[key]//p_gcd,(count-results[key])//p_gcd)
print(("{:<"+ str(maxvalue_len+1)+"}{} {}").format(
str(key)+":",
"{:7.3f}%".format(results[key]/count*100),
probs_str
))
else:
print(("{:<"+ str(maxvalue_len+1)+"}{:7.3f}% {}").format(
str(key)+":",
results[key]/count*100,
u"\u2588"*bar_length + " "*(total_bar_length-bar_length+1)
))
return len(results.values())
def print_instructions():
print("usage: {} 'dice-math'".format(sys.argv[0]))
print("usage: {} 'options:dice-math'".format(sys.argv[0]))
print()
print("Evaluate probabilistic dice math expressions.")
print()
print("Supported dice types:")
print("- Regular dice '[R]dN'. Throws an N sided die R times.")
print("- Fate dice '[R]F'. Throws a Fate die (-1,-1,0,0,1,1) R times.")
print("Supported operators:")
print("+, -, *, =, <, <=, >, =>")
print("Also supports constant integers")
print()
print("Options:")
print("- 'A', gives analytic results")
print("- 'S', runs a statistical simulation")
print()
print("- 'O', displays odds instead of a graph result")
print("- 'C', displays the result in cumulative form")
def main(arg_string):
try:
instructions, calculation = arg_string.split(':')
except ValueError:
instructions, calculation = "", arg_string
analytic = "A" in instructions
statistical = "S" in instructions
just_roll = (not analytic) and (not statistical)
cumulative = "C" in instructions
show_odds = "O" in instructions
if analytic:
solve(calculation, report_cumulative=cumulative, analytic=True, show_odds=show_odds)
if statistical:
solve(calculation, report_cumulative=cumulative, analytic=False, show_odds=show_odds)
if just_roll:
solve(calculation, just_roll=True)
if __name__ == "__main__":
try:
if len(sys.argv[1:])==0:
raise ValueError
main(" ".join(sys.argv[1:]))
except ValueError:
print_instructions()