/
die.py
executable file
·331 lines (289 loc) · 9.95 KB
/
die.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
#!/usr/bin/env python
"""A simple dice module.
"""
# from ConfigParser import ConfigParser
from goal import Goal
from roll import Roll
from random import randint, seed
import itertools
import types
seed()
############################################################################
# Globals
############################################################################
DICE = []
DEF_OFFSET = 0
DEF_NAME = 'NA'
DEF_MOD = 0
CRITICAL_FAIL = 1
DEF_MAX = CRITICAL_FAIL + 1
DEF_ROLLS_LENGTH = 20
############################################################################
# Exceptions
############################################################################
class InvalidDieFunction(Exception):
"""Raised when a function assigned to a Die is invalid.
"""
def __init__(self, message):
"""Initialize.
"""
super(InvalidDieFunction, self).__init__()
self.message = message
def __str__(self):
"""Print.
"""
return repr(self.message)
############################################################################
# Functions
############################################################################
# TODO relative imports work, but not absolute ones
# TODO initialize DICE from config
# TODO remove from DICE based on class variables
# TODO limit list size of get_probability, limit size of dice
# TODO set_function(func) for die, calling function with roll value as
# argument for function
# TODO get_probability variant where fraction is given instead of percent
# for probability of value occurence
# TODO dice_utils module
# TODO store func result in Roll object???
# TODO dice_set object? Expand list functionality?
# TODO Die.add_funcs --> more than one function in __func variable
# TODO Die.set_funcs --> clears automatically
# TODO Die constructor optional argument. Provide list of values that will
# not be rolled. Should be verified to fit in range of 1 and max_roll,
# offset would be taken into acount during rolls. Additional optional
# argument to supplement die roll list --> list allows for more than one
# appearance of any given number.
# TODO roll, instead of using randint, instead returns a random number from
# a list of possible rolls determined by max_roll, offset, and the
# exclusive/inclusive numbers list.
def get_most_likely_roll(dice_set=DICE):
"""Given a set of dice, returns the value(s) that have the highest
chance of occuring.
"""
highest = 0
highest_list = [0]
probs = get_probability(dice_set=dice_set)
for prob in probs:
if probs[prob] > highest:
highest_list = [prob]
highest = probs[prob]
elif probs[prob] == highest:
highest_list.append(prob)
return highest_list
def get_probability(dice_set=DICE):
"""Given a set of dice, returns a dictionary of possible roll values and
their probability of coming up in percent.
"""
results = {}
possible_rolls = []
count = 0
for die in dice_set:
possible_rolls.append(die.possible_rolls())
possible_rolls = itertools.product(*possible_rolls)
for result in possible_rolls:
total = sum(result)
if total in results:
results[total] += 1
else:
results[total] = 1
count += 1
for key in results:
results[key] = float(results[key])/float(count) * 100
return results
def get_minimum_roll(dice_set=DICE):
"""Return the smallest roll possible given a set of dice.
"""
minimum_roll = 0
for die in dice_set:
offset = die.offset
minimum_roll += (offset + 1)
return minimum_roll
def get_maximum_roll(dice_set=DICE):
"""Return the largest roll possible given a set of dice.
"""
maximum_roll = 0
for die in dice_set:
maximum_roll += die.max_roll + die.offset
return maximum_roll
def roll_set(dice_set=DICE, count=1, goal=None):
"""Roll a given dice set and return the list of dice. The results are
stored in each dice's __rolls variable.
The optional argument count determines the number of times to roll
the dice set.
The optional agrument goal determines the goal against which the dice
rolls will be checked.
"""
for dice in dice_set:
dice.roll(count=count, goal=goal)
return dice_set
def remove(dice_obj):
"""Remove a given dice from DICE.
"""
DICE.remove(dice_obj)
############################################################################
# Classes
############################################################################
class Die(object):
"""This class specifies dice used in games.
"""
def __init__(
self,
max_roll,
name=DEF_NAME,
offset=DEF_OFFSET,
mod=DEF_MOD):
"""Dice have a max roll, an offset, and a name.
"""
self.max_roll = max_roll
self.offset = offset
self.name = name
self.mod = mod
self.__goal = None
self.__func = None
self.__rolls = []
self.check_vars()
DICE.append(self)
def __str__(self):
"""The print-out of a Dice object.
"""
self.check_vars()
message = '%s: ' % self.name
message += 'd%d' % self.max_roll
if self.offset > 0:
message += '+'
if self.offset != 0:
message += '%d, ' % self.offset
if self.mod != 0:
message += 'mod: %d, ' % self.mod
return message
########################################################################
# Public Methods
########################################################################
def roll(self, count=1, goal=None):
"""Appends a roll to the dice's rolls list.
"""
self.check_vars()
result = None
if goal == None and self.__goal != None:
goal = self.__goal
for _ in range(count):
value = randint(
self.offset + 1,
self.max_roll + self.offset + 1
)
if self.__func != None:
result = self.__func(value)
self.__rolls.insert(
0,
Roll(self, value, goal=goal, result=result)
)
def rolls(self):
"""Returns the list of rolls for the dice.
"""
return self.__rolls
def set_goal(self, goal):
"""Sets the goal of the dice to be used as a default for rolls.
"""
if isinstance(goal, Goal):
self.__goal = goal
else:
self.__goal = Goal(value=goal)
def set_function(self, func, clear_rolls=True):
"""This method assigns a function to a dice, which is executed
with each roll, and recieves a the Roll object as an argument.
Optional argument clear_rolls sets __rolls to an empty list if
True.
"""
if isinstance(func, types.FunctionType):
if func == self.__func:
return
self.__func = func
if clear_rolls == True:
self.__rolls = []
self.__check_func()
else:
self.__func = None
def possible_rolls(self):
"""Returns a list of possible roll values, not including Mod.
"""
possible_rolls = []
for num in range(self.offset + 1, self.offset + self.max_roll + 1):
possible_rolls.append(num)
return possible_rolls
def check_vars(self):
"""Maintain the validity of Dice variables.
"""
self.__check_max_roll()
self.__check_offset()
self.__check_name()
self.__check_modifier()
self.__check_rolls()
self.__check_goal()
self.__check_func()
########################################################################
# Private Methods
########################################################################
def __check_max_roll(self):
"""Make sure that any changes made to max_roll are valid.
"""
try:
self.max_roll = int(self.max_roll)
except ValueError:
self.max_roll = DEF_MAX
if self.max_roll <= CRITICAL_FAIL:
self.max_roll = DEF_MAX
def __check_offset(self):
"""Make sure that when and if the offset is changed, that it
is still positive and a number.
"""
try:
self.offset = int(self.offset)
except ValueError:
self.offset = DEF_OFFSET
if self.offset <= -1:
self.offset = DEF_OFFSET
def __check_name(self):
"""If the name is None, then the name displayed on print should be
NA.
"""
if self.name == 'None':
return
if self.name == None:
self.name = DEF_NAME
try:
self.name = str(self.name)
except ValueError:
self.name = DEF_NAME
def __check_modifier(self):
"""Make sure that when and if the moddifier is changed, that it is
still an integer.
"""
try:
self.mod = int(self.mod)
except ValueError:
self.mod = DEF_MOD
def __check_rolls(self):
"""Dice's rolls list length shouldn't exceed DEF_ROLLS_LENGTH
"""
self.__rolls = self.__rolls[:DEF_ROLLS_LENGTH]
def __check_goal(self):
"""Dice's goal should be checked by Goal class verification.
"""
if isinstance(self.__goal, Goal):
self.__goal.check_vars()
elif self.__goal != None:
self.__goal = Goal(self.__goal)
def __check_func(self):
"""Dice's function should be a FunctionType and be able to take an
integer argument.
"""
message = "Function %s() does not accept integer arguments."
if isinstance(self.__func, types.FunctionType):
try:
temp = self.__func(1)
except ValueError:
del temp
raise InvalidDieFunction(message % self.__func)
else:
self.__func = None